windows-nt/Source/XPSP1/NT/drivers/wdm/usb/driver/wceusbsh/serioctl.c
2020-09-26 16:20:57 +08:00

2493 lines
73 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ++
Copyright (c) 1999-2000 Microsoft Corporation
Module Name:
SERIOCTL.C
Abstract:
Routines to handle IOCTL_SERIAL_Xxx
Environment:
kernel mode only
Revision History:
07-14-99 Jeff Midkiff (jeffmi)
-- */
#include "wceusbsh.h"
VOID
SerialCompletePendingWaitMasks(
IN PDEVICE_EXTENSION PDevExt
);
VOID
SerialCancelWaitMask(
IN PDEVICE_OBJECT PDevObj,
IN PIRP PIrp
);
//
// Debug spew
//
#if DBG
//
// gets the function code fom an ioctl code, which uses method buffered.
// assumes the device type is serial port
//
#define SERIAL_FNCT_CODE( _ctl_code_ ) ( (_ctl_code_ & 0xFF) >> 2)
//
// debug dumps. no spin lock usage to better simulate free build's run time.
// if these trap in the debugger you know why.
//
#define DBG_DUMP_BAUD_RATE( _PDevExt ) \
{ \
DbgDump(DBG_SERIAL, ("SerialPort.CurrentBaud: %d\n", _PDevExt->SerialPort.CurrentBaud.BaudRate)); \
}
#define DBG_DUMP_LINE_CONTROL( _PDevExt ) \
{ \
DbgDump(DBG_SERIAL, ("SerialPort.LineControl.StopBits : 0x%x\n", _PDevExt->SerialPort.LineControl.StopBits )); \
DbgDump(DBG_SERIAL, ("SerialPort.LineControl.Parity : 0x%x\n", _PDevExt->SerialPort.LineControl.Parity )); \
DbgDump(DBG_SERIAL, ("SerialPort.LineControl.WordLength : 0x%x\n", _PDevExt->SerialPort.LineControl.WordLength )); \
}
#define DBG_DUMP_SERIAL_HANDFLOW( _PDevExt ) \
{ \
DbgDump(DBG_SERIAL, ("SerialPort.HandFlow.ControlHandShake: 0x%x\n", PDevExt->SerialPort.HandFlow.ControlHandShake)); \
DbgDump(DBG_SERIAL, ("SerialPort.HandFlow.FlowReplace: 0x%x\n", PDevExt->SerialPort.HandFlow.FlowReplace)); \
DbgDump(DBG_SERIAL, ("SerialPort.HandFlow.XonLimit: 0x%x\n", PDevExt->SerialPort.HandFlow.XonLimit)); \
DbgDump(DBG_SERIAL, ("SerialPort.HandFlow.XoffLimit: 0x%x\n", PDevExt->SerialPort.HandFlow.XoffLimit)); \
}
#define DBG_DUMP_SERIAL_TIMEOUTS( _PDevExt ) \
{ \
DbgDump(DBG_SERIAL|DBG_TIME, ("SerialPort.Timeouts.ReadIntervalTimeout: %d\n", _PDevExt->SerialPort.Timeouts.ReadIntervalTimeout )); \
DbgDump(DBG_SERIAL|DBG_TIME, ("SerialPort.Timeouts.ReadTotalTimeoutMultiplier: %d\n", _PDevExt->SerialPort.Timeouts.ReadTotalTimeoutMultiplier )); \
DbgDump(DBG_SERIAL|DBG_TIME, ("SerialPort.Timeouts.ReadTotalTimeoutConstant: %d\n", _PDevExt->SerialPort.Timeouts.ReadTotalTimeoutConstant )); \
DbgDump(DBG_SERIAL|DBG_TIME, ("SerialPort.Timeouts.WriteTotalTimeoutMultiplier: %d\n", _PDevExt->SerialPort.Timeouts.WriteTotalTimeoutMultiplier )); \
DbgDump(DBG_SERIAL|DBG_TIME, ("SerialPort.Timeouts.WriteTotalTimeoutConstant: %d\n", _PDevExt->SerialPort.Timeouts.WriteTotalTimeoutConstant )); \
}
#define DBG_DUMP_SERIAL_CHARS( _PDevExt) \
{ \
DbgDump(DBG_SERIAL, ("SerialPort.SpecialChars.EofChar: 0x%x\n", _PDevExt->SerialPort.SpecialChars.EofChar )); \
DbgDump(DBG_SERIAL, ("SerialPort.SpecialChars.ErrorChar: 0x%x\n", _PDevExt->SerialPort.SpecialChars.ErrorChar )); \
DbgDump(DBG_SERIAL, ("SerialPort.SpecialChars.BreakChar: 0x%x\n", _PDevExt->SerialPort.SpecialChars.BreakChar )); \
DbgDump(DBG_SERIAL, ("SerialPort.SpecialChars.EventChar: 0x%x\n", _PDevExt->SerialPort.SpecialChars.EventChar )); \
DbgDump(DBG_SERIAL, ("SerialPort.SpecialChars.XonChar: 0x%x\n", _PDevExt->SerialPort.SpecialChars.XonChar )); \
DbgDump(DBG_SERIAL, ("SerialPort.SpecialChars.XoffChar: 0x%x\n", _PDevExt->SerialPort.SpecialChars.XoffChar )); \
}
#else
#define DBG_DUMP_BAUD_RATE( _PDevExt )
#define DBG_DUMP_LINE_CONTROL( _PDevExt )
#define DBG_DUMP_SERIAL_HANDFLOW( _PDevExt )
#define DBG_DUMP_SERIAL_TIMEOUTS( _PDevExt )
#define DBG_DUMP_SERIAL_CHARS( _PDevExt)
#endif
__inline
NTSTATUS
IoctlSetSerialValue(
IN PDEVICE_EXTENSION PDevExt,
IN PIRP PIrp,
ULONG Size,
IN OUT PVOID PDest
)
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status = STATUS_DELETE_PENDING;
ULONG information = Size;
KIRQL oldIrql;
KeAcquireSpinLock(&PDevExt->ControlLock, &oldIrql);
pIrpSp = IoGetCurrentIrpStackLocation(PIrp);
if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < Size) {
information = 0;
status = STATUS_BUFFER_TOO_SMALL;
DbgDump(DBG_ERR, ("IoctlSetSerialValue: (0x%x)\n", status));
} else {
memcpy( PDest, PIrp->AssociatedIrp.SystemBuffer, Size);
status = STATUS_SUCCESS;
}
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
PIrp->IoStatus.Information = information;
PIrp->IoStatus.Status = status;
return status;
}
__inline
NTSTATUS
IoctlGetSerialValue(
IN PDEVICE_EXTENSION PDevExt,
IN PIRP PIrp,
ULONG Size,
IN PVOID PSrc
)
{
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status = STATUS_DELETE_PENDING;
ULONG information = Size;
KIRQL oldIrql;
KeAcquireSpinLock(&PDevExt->ControlLock, &oldIrql);
pIrpSp = IoGetCurrentIrpStackLocation(PIrp);
if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength < Size) {
information = 0;
status = STATUS_BUFFER_TOO_SMALL;
DbgDump(DBG_ERR, ("IoctlGetSerialValue: (0x%x)\n", status));
} else {
memcpy( PIrp->AssociatedIrp.SystemBuffer, PSrc, Size );
status = STATUS_SUCCESS;
}
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
PIrp->IoStatus.Information = information;
PIrp->IoStatus.Status = status;
return status;
}
__inline
NTSTATUS
SetBaudRate(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
DbgDump(DBG_SERIAL, (">SetBaudRate(%p)\n", PIrp));
status = IoctlSetSerialValue(PDevExt,
PIrp,
sizeof( PDevExt->SerialPort.CurrentBaud ),
&PDevExt->SerialPort.CurrentBaud );
DBG_DUMP_BAUD_RATE(PDevExt);
DbgDump(DBG_SERIAL, ("<SetBaudRate %x\n", status));
return status;
}
__inline
NTSTATUS
GetBaudRate(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
DbgDump(DBG_SERIAL, (">GetBaudRate(%p)\n", PIrp));
status = IoctlGetSerialValue( PDevExt,
PIrp,
sizeof( PDevExt->SerialPort.CurrentBaud ),
&PDevExt->SerialPort.CurrentBaud);
DBG_DUMP_BAUD_RATE(PDevExt);
DbgDump(DBG_SERIAL, ("<GetBaudRate %x\n", status));
return status;
}
__inline
NTSTATUS
SetLineControl(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
DbgDump(DBG_SERIAL, (">SetLineControl(%p)\n", PIrp));
status = IoctlSetSerialValue( PDevExt,
PIrp,
sizeof(PDevExt->SerialPort.LineControl),
&PDevExt->SerialPort.LineControl);
DBG_DUMP_LINE_CONTROL( PDevExt );
DbgDump(DBG_SERIAL, ("<SetLineControl %x\n",
status));
return status;
}
__inline
NTSTATUS
GetLineControl(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status= STATUS_DELETE_PENDING;
DbgDump(DBG_SERIAL, (">GetLineControl(%p)\n", PIrp));
status = IoctlGetSerialValue( PDevExt,
PIrp,
sizeof(PDevExt->SerialPort.LineControl),
&PDevExt->SerialPort.LineControl );
DBG_DUMP_LINE_CONTROL( PDevExt );
DbgDump(DBG_SERIAL, ("<GetLineControl %x\n",
status));
return status;
}
__inline
NTSTATUS
SetTimeouts(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
DbgDump(DBG_SERIAL|DBG_TIME,(">SetTimeouts(%p)\n", PIrp));
status = IoctlSetSerialValue( PDevExt,
PIrp,
sizeof(PDevExt->SerialPort.Timeouts),
&PDevExt->SerialPort.Timeouts);
DBG_DUMP_SERIAL_TIMEOUTS( PDevExt );
DbgDump(DBG_SERIAL|DBG_TIME,("<SetTimeouts %x\n", status));
return status;
}
__inline
NTSTATUS
GetTimeouts(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
DbgDump(DBG_SERIAL|DBG_TIME, (">GetTimeouts(%p)\n", PIrp));
status = IoctlGetSerialValue( PDevExt,
PIrp,
sizeof(PDevExt->SerialPort.Timeouts),
&PDevExt->SerialPort.Timeouts);
DBG_DUMP_SERIAL_TIMEOUTS( PDevExt );
DbgDump(DBG_SERIAL|DBG_TIME, ("<GetTimeouts %x\n", status));
return status;
}
__inline
NTSTATUS
SetSpecialChars(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
DbgDump(DBG_SERIAL, (">SetSpecialChars(%p)\n", PIrp));
status = IoctlSetSerialValue( PDevExt,
PIrp,
sizeof(PDevExt->SerialPort.SpecialChars),
&PDevExt->SerialPort.SpecialChars);
DBG_DUMP_SERIAL_CHARS( PDevExt);
DbgDump(DBG_SERIAL, ("<SetSpecialChars %x\n", status));
return status;
}
__inline
NTSTATUS
GetSpecialChars(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
DbgDump(DBG_SERIAL, (">GetSpecialChars(%p)\n", PIrp));
status = IoctlGetSerialValue( PDevExt,
PIrp,
sizeof(PDevExt->SerialPort.SpecialChars),
&PDevExt->SerialPort.SpecialChars);
DBG_DUMP_SERIAL_CHARS( PDevExt);
DbgDump(DBG_SERIAL, ("<GetSpecialChars %x\n", status));
return status;
}
__inline
NTSTATUS
SetClearDTR(
IN PDEVICE_EXTENSION PDevExt,
IN PIRP Irp,
IN BOOLEAN Set
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
KIRQL irql;
USHORT usState = 0; // DRT/RTS state to send to USB device
USHORT usOldMSR = 0;
USHORT usDeltaMSR = 0;
ULONG ulOldHistoryMask = 0;
ULONG ulOldRS232Lines = 0;
DbgDump(DBG_SERIAL, (">SetClearDTR (%x, %x)\n", PDevExt->DeviceObject, Set));
KeAcquireSpinLock(&PDevExt->ControlLock, &irql);
//
// we queue the user's Irp because this operation may take some time,
// and we only want to hit the USB with one of these requests at a time.
//
if ( NULL != PDevExt->SerialPort.ControlIrp ) {
DbgDump(DBG_WRN, ("SetClearDTR: STATUS_DEVICE_BUSY\n"));
status = STATUS_DEVICE_BUSY;
KeReleaseSpinLock(&PDevExt->ControlLock, irql);
return status;
}
if ( !CanAcceptIoRequests(PDevExt->DeviceObject, FALSE, TRUE) ||
!NT_SUCCESS(AcquireRemoveLock(&PDevExt->RemoveLock, Irp)) )
{
status = STATUS_DELETE_PENDING;
DbgDump(DBG_ERR, ("SetClearDTR: 0x%x\n", status));
KeReleaseSpinLock( &PDevExt->ControlLock, irql);
return status;
}
//
// Queue the Irp.
//
ASSERT( NULL == PDevExt->SerialPort.ControlIrp );
PDevExt->SerialPort.ControlIrp = Irp;
usOldMSR = PDevExt->SerialPort.ModemStatus;
ulOldRS232Lines = PDevExt->SerialPort.RS232Lines;
ulOldHistoryMask = PDevExt->SerialPort.HistoryMask;
if (PDevExt->SerialPort.RS232Lines & SERIAL_RTS_STATE) {
usState |= USB_COMM_RTS;
}
if (Set) {
PDevExt->SerialPort.RS232Lines |= SERIAL_DTR_STATE;
//
// If there is an INT pipe then MSR could get modified
//
PDevExt->SerialPort.ModemStatus |= SERIAL_MSR_DSR | SERIAL_MSR_DCD;
usState |= USB_COMM_DTR;
} else {
PDevExt->SerialPort.RS232Lines &= ~SERIAL_DTR_STATE;
//
// If there is an INT pipe then MSR could get modified
//
PDevExt->SerialPort.ModemStatus &= ~SERIAL_MSR_DSR & ~SERIAL_MSR_DCD;
}
// see what has changed in the MSR
usDeltaMSR = usOldMSR ^ PDevExt->SerialPort.ModemStatus;
if (usDeltaMSR & (SERIAL_MSR_DSR|SERIAL_MSR_DCD)) {
// set delta MSR bits
PDevExt->SerialPort.ModemStatus |= SERIAL_MSR_DDSR | SERIAL_MSR_DDCD;
}
DbgDump(DBG_SERIAL, ("SerialPort.RS232Lines : 0x%x\n", PDevExt->SerialPort.RS232Lines ));
DbgDump(DBG_SERIAL, ("SerialPort.ModemStatus: 0x%x\n", PDevExt->SerialPort.ModemStatus ));
DbgDump(DBG_SERIAL, ("SerialPort.HistoryMask: 0x%x\n", PDevExt->SerialPort.HistoryMask ));
KeReleaseSpinLock(&PDevExt->ControlLock, irql);
//
// set DTR/RTS on the USB device
//
status = UsbClassVendorCommand( PDevExt->DeviceObject,
USB_COMM_SET_CONTROL_LINE_STATE,
usState,
PDevExt->UsbInterfaceNumber,
NULL,
NULL,
FALSE,
WCEUSB_CLASS_COMMAND );
DbgDump(DBG_SERIAL|DBG_READ_LENGTH, ("USB_COMM_SET_CONTROL_LINE_STATE(1, State: 0x%x, Status: 0x%x)\n", usState, status ));
_EzLink:
if ( STATUS_SUCCESS == status ) {
KeAcquireSpinLock(&PDevExt->ControlLock, &irql);
// signal history massk
if ( usDeltaMSR & (SERIAL_MSR_DSR|SERIAL_MSR_DCD) ) {
PDevExt->SerialPort.HistoryMask |= SERIAL_EV_DSR | SERIAL_EV_RLSD;
}
PDevExt->EP0DeviceErrors = 0;
DbgDump(DBG_SERIAL, ("SerialPort.HistoryMask: 0x%x\n", PDevExt->SerialPort.HistoryMask ));
KeReleaseSpinLock(&PDevExt->ControlLock, irql);
} else {
// Ez-link
if ((PDevExt->DeviceDescriptor.idVendor != 0x0547) &&
((PDevExt->DeviceDescriptor.idProduct != 0x2710) || (PDevExt->DeviceDescriptor.idProduct != 0x2720)))
{
// WINCE BUG 19544:
// AS 3.1 does not handle STATUS_TIMEOUT, so will not see a problem.
// A side effect is that it could sit spinning the green light trying to connect forever.
// However, this is a different case from BUG 19544, which is a disconnect problem.
// If we return failure then it will keep pounding us with Set DTR Irps.
// This would be OK if AS would recognize that we disabled the interface, but it won't - see above.
// You only see this bug when you have a flakey device (iPAQ, hung Jornada, etc.) that times out or
// fails to properly handle the command. To prevent the bugcheck 0xCE the choices as of today are:
// a) let it spin and never connect for these bad devices (iPAQ). Fix your firmware.
// b) fix AcvtiveSync
// I prefer both - pending email with COMPAQ (HTC) and ActiveSync. When AS gets their changes in then we need to
// investigate again.
status = STATUS_TIMEOUT;
KeAcquireSpinLock( &PDevExt->ControlLock, &irql);
if ( ++PDevExt->EP0DeviceErrors < MAX_EP0_DEVICE_ERRORS) {
DbgDump(DBG_ERR, ("USB_COMM_SET_CONTROL_LINE_STATE error: 0x%x\n", status ));
//
// The command failed. Reset the old states, propogate status, and disable the device interface.
// This should stop AS 3.1 from pounding us with Set DTR Irps.
// However, AS does not participate in PnP well if we disable the interface
// (see the note in IRP_MN_QUERY_PNP_DEVICE_STATE). Disabeling the
// interface has the desired effect of notifying apps to stop sending us requests and Close the handle.
//
PDevExt->SerialPort.ModemStatus = usOldMSR;
PDevExt->SerialPort.HistoryMask = ulOldHistoryMask;
PDevExt->SerialPort.RS232Lines = ulOldRS232Lines;
KeReleaseSpinLock( &PDevExt->ControlLock, irql);
} else {
DbgDump(DBG_ERR, ("*** UNRECOVERABLE DEVICE ERROR.2: (0x%x, %d) No longer Accepting Requests ***\n", status, PDevExt->EP0DeviceErrors ));
// mark as PNP_DEVICE_FAILED
InterlockedExchange(&PDevExt->AcceptingRequests, FALSE);
KeReleaseSpinLock( &PDevExt->ControlLock, irql);
IoInvalidateDeviceState( PDevExt->PDO );
LogError( NULL,
PDevExt->DeviceObject,
0, 0,
(UCHAR)PDevExt->EP0DeviceErrors,
ERR_NO_DTR,
status,
SERIAL_HARDWARE_FAILURE,
PDevExt->DeviceName.Length + sizeof(WCHAR),
PDevExt->DeviceName.Buffer,
0,
NULL );
}
} else {
DbgDump(DBG_WRN, ("Ez-Link\n" ));
status = STATUS_SUCCESS;
goto _EzLink;
}
}
//
// finally, release any pending serial events
//
ProcessSerialWaits(PDevExt);
//
// DeQueue the user's Irp. It gets completed in the SerialIoctl dispatch
//
KeAcquireSpinLock(&PDevExt->ControlLock, &irql);
ReleaseRemoveLock(&PDevExt->RemoveLock, Irp);
ASSERT( NULL != PDevExt->SerialPort.ControlIrp );
PDevExt->SerialPort.ControlIrp = NULL;
KeReleaseSpinLock(&PDevExt->ControlLock, irql);
DbgDump(DBG_SERIAL, ("<SetClearDTR %x\n", status ));
return status;
}
__inline
NTSTATUS
SetClearRTS(
IN PDEVICE_EXTENSION PDevExt,
IN PIRP Irp,
IN BOOLEAN Set
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
KIRQL irql;
USHORT usState = 0; // DRT/RTS state to send to USB device
USHORT usOldMSR = 0;
USHORT usDeltaMSR = 0;
ULONG ulOldRS232Lines = 0;
ULONG ulOldHistoryMask = 0;
DbgDump(DBG_SERIAL, (">SetClearRTS (%x, %x)\n", PDevExt->DeviceObject, Set));
KeAcquireSpinLock(&PDevExt->ControlLock, &irql);
//
// we queue the user's Irp because this operation may take some time,
// and we only want to hit the USB with one of these requests at a time.
//
if ( NULL != PDevExt->SerialPort.ControlIrp ) {
status = STATUS_DEVICE_BUSY;
DbgDump(DBG_WRN, ("SetClearRTS.1: 0x%x\n", status));
KeReleaseSpinLock(&PDevExt->ControlLock, irql);
return status;
}
if ( !CanAcceptIoRequests(PDevExt->DeviceObject, FALSE, TRUE) ||
!NT_SUCCESS(AcquireRemoveLock(&PDevExt->RemoveLock, Irp)) )
{
status = STATUS_DELETE_PENDING;
DbgDump(DBG_ERR, ("SetClearRTS.2: 0x%x\n", status));
KeReleaseSpinLock(&PDevExt->ControlLock, irql);
return status;
}
//
// Queue the Irp.
//
ASSERT( NULL == PDevExt->SerialPort.ControlIrp );
PDevExt->SerialPort.ControlIrp = Irp;
usOldMSR = PDevExt->SerialPort.ModemStatus;
ulOldRS232Lines = PDevExt->SerialPort.RS232Lines;
ulOldHistoryMask = PDevExt->SerialPort.HistoryMask;
if (PDevExt->SerialPort.RS232Lines & SERIAL_DTR_STATE) {
usState |= USB_COMM_DTR;
}
if (Set) {
PDevExt->SerialPort.RS232Lines |= SERIAL_RTS_STATE;
//
// If there is an INT pipe then MSR could get modified
//
PDevExt->SerialPort.ModemStatus |= SERIAL_MSR_CTS;
usState |= USB_COMM_RTS;
} else {
PDevExt->SerialPort.RS232Lines &= ~SERIAL_RTS_STATE;
//
// If there is an INT pipe then MSR could get modified
//
PDevExt->SerialPort.ModemStatus &= ~SERIAL_MSR_CTS;
}
// see what has changed in the MSR
usDeltaMSR = usOldMSR ^ PDevExt->SerialPort.ModemStatus;
if (usDeltaMSR & SERIAL_MSR_CTS) {
// set delta MSR bits
PDevExt->SerialPort.ModemStatus |= SERIAL_MSR_DCTS;
}
DbgDump(DBG_SERIAL, ("SerialPort.RS232Lines : 0x%x\n", PDevExt->SerialPort.RS232Lines ));
DbgDump(DBG_SERIAL, ("SerialPort.ModemStatus: 0x%x\n", PDevExt->SerialPort.ModemStatus));
DbgDump(DBG_SERIAL, ("SerialPort.HistoryMask: 0x%x\n", PDevExt->SerialPort.HistoryMask ));
KeReleaseSpinLock(&PDevExt->ControlLock, irql);
//
// set DTR/RTS on the USB device
//
status = UsbClassVendorCommand( PDevExt->DeviceObject,
USB_COMM_SET_CONTROL_LINE_STATE,
usState,
PDevExt->UsbInterfaceNumber,
NULL,
NULL,
FALSE,
WCEUSB_CLASS_COMMAND );
DbgDump(DBG_SERIAL|DBG_READ_LENGTH, ("USB_COMM_SET_CONTROL_LINE_STATE(2, State: 0x%x, Status: 0x%x)\n", usState, status ));
_EzLink:
if ( STATUS_SUCCESS == status ) {
KeAcquireSpinLock(&PDevExt->ControlLock, &irql);
// signal history mask
if ( usDeltaMSR & SERIAL_MSR_CTS ) {
PDevExt->SerialPort.HistoryMask |= SERIAL_EV_CTS;
}
PDevExt->EP0DeviceErrors = 0;
DbgDump(DBG_SERIAL, ("SerialPort.HistoryMask: 0x%x\n", PDevExt->SerialPort.HistoryMask ));
KeReleaseSpinLock(&PDevExt->ControlLock, irql);
} else {
// Ez-link
if ((PDevExt->DeviceDescriptor.idVendor != 0x0547) &&
((PDevExt->DeviceDescriptor.idProduct != 0x2710) || (PDevExt->DeviceDescriptor.idProduct != 0x2720)))
{
// AS 3.1 does not handle STATUS_TIMEOUT, so will not see a problem.
// A side effect is that it could sit spinning the green light trying to connect forever.
// However, this is a different case from BUG 19544, which is a disconnect problem.
// If we return failure then it will keep pounding us with Set DTR Irps.
// This would be OK if AS would recognize that we disabled the interface, but it won't - see above.
// You only see this bug when you have a flakey device (iPAQ, hung Jornada, etc.) that times out or
// fails to properly handle the command. To prevent the bugcheck 0xCE the choices as of today are:
// a) let it spin and never connect for these bad devices (iPAQ). Fix your firmware.
// b) fix AcvtiveSync
// I prefer both - pending email with COMPAQ (HTC) and ActiveSync. When AS gets their changes in then we need to
// investigate again.
status = STATUS_TIMEOUT;
KeAcquireSpinLock( &PDevExt->ControlLock, &irql);
TEST_TRAP();
if ( ++PDevExt->EP0DeviceErrors < MAX_EP0_DEVICE_ERRORS) {
DbgDump(DBG_ERR, ("USB_COMM_SET_CONTROL_LINE_STATE error: %x\n", status ));
//
// The command failed. Reset the old states, propogate status, and disable the device interface.
// This should stop AS 3.1 from pounding us with Set DTR Irps.
// However, AS does not participate in PnP well if we disable the interface
// (see the note in IRP_MN_QUERY_PNP_DEVICE_STATE). Disabeling the
// interface has the desired effect of notifying apps to stop sending us requests and Close the handle.
//
PDevExt->SerialPort.ModemStatus = usOldMSR;
PDevExt->SerialPort.RS232Lines = ulOldRS232Lines;
PDevExt->SerialPort.HistoryMask = ulOldHistoryMask;
KeReleaseSpinLock( &PDevExt->ControlLock, irql);
} else {
DbgDump(DBG_ERR, ("*** UNRECOVERABLE DEVICE ERROR.3: (0x%x, %d) No longer Accepting Requests ***\n", status, PDevExt->EP0DeviceErrors ));
// mark as PNP_DEVICE_FAILED
InterlockedExchange(&PDevExt->AcceptingRequests, FALSE);
KeReleaseSpinLock( &PDevExt->ControlLock, irql);
IoInvalidateDeviceState( PDevExt->PDO );
LogError( NULL,
PDevExt->DeviceObject,
0, 0,
(UCHAR)PDevExt->EP0DeviceErrors,
ERR_NO_RTS,
status,
SERIAL_HARDWARE_FAILURE,
PDevExt->DeviceName.Length + sizeof(WCHAR),
PDevExt->DeviceName.Buffer,
0,
NULL );
}
} else {
DbgDump(DBG_WRN, ("Ez-Link\n" ));
status = STATUS_SUCCESS;
goto _EzLink;
}
}
//
// finally, release any pending serial events
//
ProcessSerialWaits(PDevExt);
//
// DeQueue the user's Irp. It gets completed in the SerialIoctl dispatch
//
KeAcquireSpinLock(&PDevExt->ControlLock, &irql);
ReleaseRemoveLock(&PDevExt->RemoveLock, Irp);
ASSERT( NULL != PDevExt->SerialPort.ControlIrp );
PDevExt->SerialPort.ControlIrp = NULL;
KeReleaseSpinLock(&PDevExt->ControlLock, irql);
DbgDump(DBG_SERIAL, ("<SetClearRTS %x\n", status ));
return status;
}
__inline
NTSTATUS
GetDtrRts(
IN PIRP Irp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
DbgDump(DBG_SERIAL, (">GetDtrRts (%p)\n", Irp));
status = IoctlGetSerialValue( PDevExt,
Irp,
sizeof(PDevExt->SerialPort.RS232Lines),
&PDevExt->SerialPort.RS232Lines);
DbgDump(DBG_SERIAL, ("SerialPort.RS232Lines: 0x%x\n", PDevExt->SerialPort.RS232Lines ));
DbgDump(DBG_SERIAL, ("<GetDtrRts %x\n", status));
return status;
}
__inline
NTSTATUS
SerialResetDevice(
IN PDEVICE_EXTENSION PDevExt,
IN PIRP Irp,
IN BOOLEAN ClearDTR
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
BOOLEAN bRelease = TRUE;
KIRQL oldIrql;
DbgDump(DBG_SERIAL, (">SerialResetDevice (%x, %d)\n", PDevExt->DeviceObject, ClearDTR ));
KeAcquireSpinLock(&PDevExt->ControlLock, &oldIrql);
ASSERT_SERIAL_PORT( PDevExt->SerialPort );
PDevExt->SerialPort.SupportedBauds = SERIAL_BAUD_075 | SERIAL_BAUD_110 | SERIAL_BAUD_150
| SERIAL_BAUD_300 | SERIAL_BAUD_600 | SERIAL_BAUD_1200
| SERIAL_BAUD_1800 | SERIAL_BAUD_2400 | SERIAL_BAUD_4800 | SERIAL_BAUD_7200
| SERIAL_BAUD_9600 | SERIAL_BAUD_14400 | SERIAL_BAUD_19200 | SERIAL_BAUD_38400 | SERIAL_BAUD_56K
| SERIAL_BAUD_128K | SERIAL_BAUD_57600 | SERIAL_BAUD_115200 | SERIAL_BAUD_USER;
PDevExt->SerialPort.CurrentBaud.BaudRate = 115200;
PDevExt->SerialPort.LineControl.StopBits = STOP_BIT_1;
PDevExt->SerialPort.LineControl.Parity = NO_PARITY;
PDevExt->SerialPort.LineControl.WordLength = SERIAL_DATABITS_8;
PDevExt->SerialPort.HandFlow.ControlHandShake = 0;
PDevExt->SerialPort.HandFlow.FlowReplace = 0;
PDevExt->SerialPort.HandFlow.XonLimit = 0;
PDevExt->SerialPort.HandFlow.XoffLimit = 0;
RtlZeroMemory( &PDevExt->SerialPort.Timeouts, sizeof(SERIAL_TIMEOUTS) );
RtlZeroMemory( &PDevExt->SerialPort.SpecialChars, sizeof(SERIAL_CHARS) );
PDevExt->SerialPort.RS232Lines = 0;
PDevExt->SerialPort.HistoryMask = 0;
PDevExt->SerialPort.WaitMask = 0;
PDevExt->SerialPort.ModemStatus = 0;
DbgDump(DBG_SERIAL, ("SerialPort.RS232Lines : 0x%x\n", PDevExt->SerialPort.RS232Lines ));
DbgDump(DBG_SERIAL, ("SerialPort.ModemStatus: 0x%x\n", PDevExt->SerialPort.ModemStatus));
DbgDump(DBG_SERIAL, ("SerialPort.HistoryMask: 0x%x\n", PDevExt->SerialPort.HistoryMask));
if ( PDevExt->SerialPort.CurrentWaitMaskIrp ) {
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
bRelease = FALSE;
SerialCompletePendingWaitMasks(PDevExt);
}
//
// drop the RTS/DTR lines on the USB device
//
if (bRelease) {
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
bRelease = FALSE;
}
status = ClearDTR ? SetClearDTR(PDevExt, Irp, FALSE) : STATUS_SUCCESS;
if (bRelease) {
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
}
DBG_DUMP_BAUD_RATE(PDevExt);
DBG_DUMP_LINE_CONTROL(PDevExt);
DBG_DUMP_SERIAL_HANDFLOW(PDevExt);
DBG_DUMP_SERIAL_TIMEOUTS(PDevExt);
DBG_DUMP_SERIAL_CHARS(PDevExt);
DbgDump(DBG_SERIAL, ("<SerialResetDevice %x\n", status));
return status;
}
__inline
NTSTATUS
SetBreak(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt,
USHORT Time
)
{
UNREFERENCED_PARAMETER(PIrp);
UNREFERENCED_PARAMETER(PDevExt);
UNREFERENCED_PARAMETER(Time);
DbgDump(DBG_SERIAL, (">SetBreak(%p)\n", PIrp));
DbgDump(DBG_SERIAL, ("<SetBreak %x\n", STATUS_NOT_SUPPORTED));
return STATUS_NOT_SUPPORTED;
}
__inline
NTSTATUS
SetQueueSize(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
/* ++
IOCTL_SERIAL_SET_QUEUE_SIZE
Operation
Resizes the driver's internal typeahead and input buffers.
The driver can allocate buffers larger than the requested sizes
and can refuse to allocate buffers larger than its capacity.
Input
Parameters.DeviceIoControl.InputBufferLength
indicates the size in bytes (must be >= sizeof(SERIAL_QUEUE_SIZE))
of the buffer at Irp->AssociatedIrp.SystemBuffer, containing the
InSize and OutSize specifications.
Output
None
I/O Status Block
The Information field is set to zero.
The Status field is set to STATUS_SUCCESS or
possibly to STATUS_BUFFER_TOO_SMALL or
STATUS_INSUFFICIENT_RESOURCES if the driver
cannot satisfy the request by allocating more memory.
-- */
{
NTSTATUS status = STATUS_DELETE_PENDING;
UNREFERENCED_PARAMETER(PIrp);
UNREFERENCED_PARAMETER(PDevExt);
DbgDump(DBG_SERIAL, (">SetQueueSize (%p)\n", PIrp));
// we pretend to set this, but don't really care
status = IoctlSetSerialValue(PDevExt,
PIrp,
sizeof(PDevExt->SerialPort.FakeQueueSize ),
&PDevExt->SerialPort.FakeQueueSize );
DbgDump( DBG_SERIAL, ("SerialPort.FakeQueueSize.InSize = 0x%x\n", PDevExt->SerialPort.FakeQueueSize.InSize ));
DbgDump( DBG_SERIAL, ("SerialPort.FakeQueueSize.OutSize = 0x%x\n", PDevExt->SerialPort.FakeQueueSize.OutSize));
DbgDump(DBG_SERIAL, ("DataOutMaxPacketSize = %d\n", PDevExt->WritePipe.MaxPacketSize));
DbgDump(DBG_SERIAL, ("UsbReadBuffSize = %d\n", PDevExt->UsbReadBuffSize ));
#if USE_RING_BUFF
DbgDump(DBG_SERIAL, ("Internal RingBuffer Size: %d\n", PDevExt->RingBuff.Size ));
#endif
DbgDump(DBG_SERIAL, ("<SetQueueSize %x\n", status));
return status;
}
__inline
NTSTATUS
GetWaitMask(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
DbgDump(DBG_SERIAL, (">GetWaitMask (%p)\n", PIrp));
status = IoctlGetSerialValue(PDevExt,
PIrp,
sizeof(PDevExt->SerialPort.WaitMask),
&PDevExt->SerialPort.WaitMask);
DbgDump(DBG_SERIAL, ("Current SerialPort.WaitMask = 0x%x\n", PDevExt->SerialPort.WaitMask));
DbgDump(DBG_SERIAL, ("<GetWaitMask %x\n", status));
return status;
}
__inline
NTSTATUS
SetWaitMask(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
/* ++
IOCTL_SERIAL_SET_WAIT_MASK
Operation
Causes the driver to track the specified events, or,
if the specified value is zero, to complete pending waits.
Input
Parameters.DeviceIoControl.InputBufferLength
indicates the size in bytes (must be >= sizeof(ULONG)) of
the bitmask at Irp->AssociatedIrp.SystemBuffer.
Output
None
I/O Status Block
The Information field is set to zero.
The Status field is set to STATUS_SUCCESS or
possibly to STATUS_PENDING, STATUS_CANCELLED,
STATUS_BUFFER_TOO_SMALL, or STATUS_INVALID_PARAMETER.
-- */
{
PULONG pWaitMask = (PULONG)PIrp->AssociatedIrp.SystemBuffer;
NTSTATUS status = STATUS_DELETE_PENDING;
KIRQL oldIrql;
PIO_STACK_LOCATION pIrpSp;
DbgDump(DBG_SERIAL, (">SetWaitMask (%p)\n", PIrp));
KeAcquireSpinLock(&PDevExt->ControlLock, &oldIrql);
PIrp->IoStatus.Information = 0;
pIrpSp = IoGetCurrentIrpStackLocation(PIrp);
if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG)) {
status = STATUS_BUFFER_TOO_SMALL;
DbgDump(DBG_ERR, ("SetWaitMask: (0x%x)\n", status));
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
} else {
// make sure it's a valid request
if (*pWaitMask & ~(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) ) {
status = STATUS_INVALID_PARAMETER;
DbgDump(DBG_ERR, ("Invalid WaitMask: (0x%x)\n", *pWaitMask));
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
} else {
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
// force completion of any pending waits
SerialCompletePendingWaitMasks( PDevExt );
KeAcquireSpinLock(&PDevExt->ControlLock, &oldIrql);
PDevExt->SerialPort.HistoryMask = 0; // clear the history mask
PDevExt->SerialPort.WaitMask = *pWaitMask;
//
// for NT RAS
// A value of '0' means clear any pending waits, which should have read
// and cleared the MSR delta bits. The delta bits are the low nibble.
//
if (PDevExt->SerialPort.WaitMask == 0) {
// clear delta bits
PDevExt->SerialPort.ModemStatus &= 0xF0;
}
DbgDump(DBG_SERIAL, ("New SerialPort.WaitMask = 0x%x\n", PDevExt->SerialPort.WaitMask));
DbgDump(DBG_SERIAL, ("SerialPort.RS232Lines = 0x%x\n", PDevExt->SerialPort.RS232Lines ));
DbgDump(DBG_SERIAL, ("SerialPort.ModemStatus = 0x%x\n", PDevExt->SerialPort.ModemStatus));
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
status = STATUS_SUCCESS;
}
}
DbgDump(DBG_SERIAL, ("<SetWaitMask %x\n", status));
return status;
}
VOID
ProcessSerialWaits(
IN PDEVICE_EXTENSION PDevExt
)
{
KIRQL irql;
PULONG pWaitMask;
PIRP pMaskIrp;
BOOLEAN bReleaseNeeded = TRUE;
PERF_ENTRY( PERF_ProcessSerialWaits );
ASSERT(PDevExt);
DbgDump(DBG_SERIAL|DBG_TRACE, (">ProcessSerialWaits\n"));
KeAcquireSpinLock(&PDevExt->ControlLock, &irql);
if ( PDevExt->SerialPort.CurrentWaitMaskIrp ) {
ASSERT_SERIAL_PORT( PDevExt->SerialPort );
if ( PDevExt->SerialPort.WaitMask & PDevExt->SerialPort.HistoryMask) {
DbgDump(DBG_SERIAL, ("Releasing SerialPort.CurrentWaitMaskIrp(%p) with Mask: 0x%x\n",
PDevExt->SerialPort.CurrentWaitMaskIrp, PDevExt->SerialPort.HistoryMask));
pWaitMask = (PULONG)PDevExt->SerialPort.CurrentWaitMaskIrp->AssociatedIrp.SystemBuffer;
*pWaitMask = PDevExt->SerialPort.HistoryMask;
PDevExt->SerialPort.HistoryMask = 0;
pMaskIrp = PDevExt->SerialPort.CurrentWaitMaskIrp;
pMaskIrp->IoStatus.Information = sizeof(ULONG);
pMaskIrp->IoStatus.Status = STATUS_SUCCESS;
PDevExt->SerialPort.CurrentWaitMaskIrp = NULL;
IoSetCancelRoutine(pMaskIrp, NULL);
bReleaseNeeded = FALSE;
ReleaseRemoveLock(&PDevExt->RemoveLock, pMaskIrp);
KeReleaseSpinLock(&PDevExt->ControlLock, irql);
IoCompleteRequest(pMaskIrp, IO_NO_INCREMENT );
} else {
DbgDump(DBG_SERIAL, ("No Serial Events\n" ));
}
} else {
DbgDump(DBG_SERIAL, ("No CurrentWaitMaskIrp\n"));
}
if (bReleaseNeeded) {
KeReleaseSpinLock(&PDevExt->ControlLock, irql);
}
DbgDump(DBG_SERIAL|DBG_TRACE, ("<ProcessSerialWaits\n"));
PERF_EXIT( PERF_ProcessSerialWaits );
return;
}
__inline
NTSTATUS
WaitOnMask(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
/* ++
IOCTL_SERIAL_WAIT_ON_MASK
Operation
Returns information about which events have occurred
among those that the caller was waiting on.
Input
Parameters.DeviceIoControl.OutputBufferLength indicates the
size in bytes (must be >= sizeof(ULONG)) of the buffer.
Output
The driver returns a bitmask with bits set for events that
occurred (or with a value of zero if the preceding
set-waitmask request specified zero) to the buffer at
Irp->AssociatedIrp.SystemBuffer.
I/O Status Block
The Information field is set to sizeof(ULONG) when the
Status field is set to STATUS_SUCCESS. Otherwise,
the Information field is set to zero, and the Status field
can be set to STATUS_PENDING or
STATUS_INVALID_PARAMETER if a wait is already pending.
-- */
{
PULONG pWaitMask = (PULONG)PIrp->AssociatedIrp.SystemBuffer;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status = STATUS_DELETE_PENDING;
KIRQL oldIrql;
DbgDump(DBG_SERIAL|DBG_TRACE, (">WaitOnMask (%p)\n", PIrp));
KeAcquireSpinLock(&PDevExt->ControlLock, &oldIrql);
if ( !CanAcceptIoRequests(PDevExt->DeviceObject, FALSE, TRUE) ) {
status = STATUS_DELETE_PENDING;
DbgDump(DBG_ERR, ("WaitOnMask: 0x%x\n", status) );
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
return status;
}
status = STATUS_SUCCESS;
PIrp->IoStatus.Information = 0;
pIrpSp = IoGetCurrentIrpStackLocation(PIrp);
if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) {
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
status = STATUS_BUFFER_TOO_SMALL;
DbgDump(DBG_ERR, ("WaitOnMask: (0x%x)\n", status));
} else {
//
// Fake NULL modem...
//
if ((PDevExt->SerialPort.WaitMask & SERIAL_EV_CTS) && (PDevExt->SerialPort.ModemStatus & SERIAL_MSR_DCTS) ) {
PDevExt->SerialPort.HistoryMask |= SERIAL_EV_CTS;
PDevExt->SerialPort.ModemStatus &= ~SERIAL_MSR_DCTS;
}
if ((PDevExt->SerialPort.WaitMask & SERIAL_EV_DSR) && (PDevExt->SerialPort.ModemStatus & SERIAL_MSR_DDSR) ) {
PDevExt->SerialPort.HistoryMask |= SERIAL_EV_DSR;
PDevExt->SerialPort.ModemStatus &= ~SERIAL_MSR_DDSR;
// make RAS happy
PDevExt->SerialPort.HistoryMask |= SERIAL_EV_RLSD;
PDevExt->SerialPort.ModemStatus &= ~SERIAL_MSR_DDCD;
}
if ((PDevExt->SerialPort.WaitMask & SERIAL_EV_RLSD) && (PDevExt->SerialPort.ModemStatus & SERIAL_MSR_DDCD) ) {
PDevExt->SerialPort.HistoryMask |= SERIAL_EV_RLSD;
PDevExt->SerialPort.ModemStatus &= ~SERIAL_MSR_DDCD;
}
if ((PDevExt->SerialPort.WaitMask & SERIAL_EV_RING) && (PDevExt->SerialPort.ModemStatus & SERIAL_MSR_DRI) ) {
PDevExt->SerialPort.HistoryMask |= SERIAL_EV_RING;
PDevExt->SerialPort.ModemStatus &= ~SERIAL_MSR_DRI;
}
DbgDump(DBG_SERIAL, ("WaitOnMask::SerialPort.ModemStatus: 0x%x\n", PDevExt->SerialPort.ModemStatus ));
DbgDump(DBG_SERIAL, ("WaitOnMask::SerialPort.WaitMask : 0x%x\n", PDevExt->SerialPort.WaitMask ));
DbgDump(DBG_SERIAL, ("WaitOnMask::SerialPort.HistoryMask: 0x%x\n", PDevExt->SerialPort.HistoryMask ));
//
// If we already have an event to report, then just go ahead and return it.
//
if ( PDevExt->SerialPort.HistoryMask ) {
*pWaitMask = PDevExt->SerialPort.HistoryMask;
PDevExt->SerialPort.HistoryMask = 0;
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
PIrp->IoStatus.Information = sizeof(PDevExt->SerialPort.HistoryMask);
// the Irp gets completed by the calling routine
DbgDump(DBG_SERIAL | DBG_EVENTS, ("Returning WatiMask: 0x%08x\n", *pWaitMask));
} else {
//
// we don't have any events yet,
// so queue the input Irp (PIrp)
//
//
// just in case something comes in (Rx/Tx),
// we'll use a while loop to complete any
// pending wait mask Irps.
//
while (PDevExt->SerialPort.CurrentWaitMaskIrp) {
PIRP pOldIrp;
pOldIrp = PDevExt->SerialPort.CurrentWaitMaskIrp;
PDevExt->SerialPort.CurrentWaitMaskIrp = NULL;
pOldIrp->IoStatus.Status = STATUS_SUCCESS;
IoSetCancelRoutine(pOldIrp, NULL);
*pWaitMask = 0;
DbgDump(DBG_SERIAL|DBG_EVENTS|DBG_TRACE, ("Completing maskirp(4) %p\n", pOldIrp));
//
// Release locks, complete request, then reacquire locks
//
ReleaseRemoveLock(&PDevExt->RemoveLock, pOldIrp);
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
IoCompleteRequest(pOldIrp, IO_SERIAL_INCREMENT);
KeAcquireSpinLock(&PDevExt->ControlLock, &oldIrql);
}
//
// Check to see if the input Irp needs to be cancelled
//
if (PIrp->Cancel) {
PIrp->IoStatus.Information = 0;
status = PIrp->IoStatus.Status = STATUS_CANCELLED;
//
// the caller completes the Irp
//
} else {
//
// queue the input Irp as the SerialPort.CurrentWaitMaskIrp
//
DbgDump(DBG_SERIAL | DBG_EVENTS, ("Queuing Irp: %p for WatiMask: 0x%08x\n", PIrp, PDevExt->SerialPort.WaitMask ));
IoSetCancelRoutine( PIrp, SerialCancelWaitMask );
IoMarkIrpPending(PIrp);
status = PIrp->IoStatus.Status = STATUS_PENDING;
ASSERT( NULL == PDevExt->SerialPort.CurrentWaitMaskIrp); // don't want to drop Irps on the floor
PDevExt->SerialPort.CurrentWaitMaskIrp = PIrp;
//
// now the Irp is on our queue,
// so the caller should NOT try to complete it.
//
}
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
} // !SerialPort.HistoryMask
} // pIrpSp->Parameters
DbgDump(DBG_SERIAL, ("<WaitOnMask %x\n", status));
return status;
}
VOID
SerialCompletePendingWaitMasks(
IN PDEVICE_EXTENSION PDevExt
)
/*++
Routine Description:
This function is used to complete the pending SerialPort.WaitMask Irp
due to IOCTL_SERIAL_SET_WAIT_MASK
Arguments:
Return Value:
VOID
--*/
{
KIRQL oldIrql;
PIRP pCurrentMaskIrp = NULL;
ASSERT(PDevExt);
DbgDump(DBG_SERIAL|DBG_TRACE, (">SerialCompletePendingWaitMasks\n"));
KeAcquireSpinLock(&PDevExt->ControlLock, &oldIrql);
ASSERT_SERIAL_PORT( PDevExt->SerialPort );
pCurrentMaskIrp = PDevExt->SerialPort.CurrentWaitMaskIrp;
if (pCurrentMaskIrp) {
pCurrentMaskIrp->IoStatus.Status = STATUS_SUCCESS;
pCurrentMaskIrp->IoStatus.Information = sizeof(PDevExt->SerialPort.HistoryMask);
DbgDump(DBG_SERIAL, ("SerialCompletePendingWaitMasks: %p with 0x%x\n", PDevExt->SerialPort.CurrentWaitMaskIrp, PDevExt->SerialPort.HistoryMask));
*((PULONG)pCurrentMaskIrp->AssociatedIrp.SystemBuffer) = PDevExt->SerialPort.HistoryMask;
PDevExt->SerialPort.HistoryMask = 0;
PDevExt->SerialPort.CurrentWaitMaskIrp = NULL;
IoSetCancelRoutine(pCurrentMaskIrp, NULL);
}
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
// complete the queued SerialPort.WaitMask IRP if needed
if (pCurrentMaskIrp) {
ReleaseRemoveLock(&PDevExt->RemoveLock, pCurrentMaskIrp);
IoCompleteRequest(pCurrentMaskIrp, IO_SERIAL_INCREMENT);
}
DbgDump(DBG_SERIAL|DBG_TRACE, ("<SerialCompletePendingWaitMasks\n"));
return;
}
VOID
SerialCancelWaitMask(
IN PDEVICE_OBJECT PDevObj,
IN PIRP PIrp
)
/*++
Routine Description:
This function is used as a cancel routine for Irps queued due
to IOCTL_SERIAL_WAIT_ON_MASK
Arguments:
PDevObj - Pointer to Device Object
PIrp - Pointer to IRP that is being canceled; must be the same as
the current mask IRP.
Return Value:
VOID
--*/
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)PDevObj->DeviceExtension;
KIRQL oldIrql;
DbgDump(DBG_SERIAL|DBG_IRP|DBG_CANCEL|DBG_TRACE, (">SerialCancelWaitMask (%p)\n", PIrp));
//
// release ASAP since we queue our own Irps
//
IoReleaseCancelSpinLock(PIrp->CancelIrql);
KeAcquireSpinLock(&pDevExt->ControlLock, &oldIrql);
ASSERT_SERIAL_PORT( pDevExt->SerialPort );
ASSERT(pDevExt->SerialPort.CurrentWaitMaskIrp == PIrp);
PIrp->IoStatus.Status = STATUS_CANCELLED;
PIrp->IoStatus.Information = 0;
pDevExt->SerialPort.CurrentWaitMaskIrp = NULL;
ReleaseRemoveLock(&pDevExt->RemoveLock, PIrp);
KeReleaseSpinLock(&pDevExt->ControlLock, oldIrql);
IoCompleteRequest(PIrp, IO_SERIAL_INCREMENT);
DbgDump(DBG_SERIAL|DBG_IRP|DBG_CANCEL|DBG_TRACE, ("<SerialCancelWaitMask\n"));
return;
}
__inline
NTSTATUS
GetCommStatus(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
/* ++
IOCTL_SERIAL_GET_COMMSTATUS
Operation
Returns general status information, including how many
Errors and HoldReasons have occurred, how much data
is in the driver's buffers as indicated by the AmountInInQueue
and AmountInOutQueue values, and whether EofReceived and
WaitForImmediate are set.
Input
Parameters.DeviceIoControl.OutputBufferLength
indicates the size in bytes of the buffer, which must be
>= sizeof(SERIAL_STATUS).
Output
The driver returns information to the buffer at
Irp->AssociatedIrp.SystemBuffer.
I/O Status Block
The Information field is set to sizeof(SERIAL_STATUS)
when the Status field is set to STATUS_SUCCESS. Otherwise,
the Information field is set to zero and the Status field is set to
STATUS_BUFFER_TOO_SMALL.
-- */
{
PSERIAL_STATUS pSerialStatus = (PSERIAL_STATUS)PIrp->AssociatedIrp.SystemBuffer;
NTSTATUS status = STATUS_DELETE_PENDING;
KIRQL oldIrql;
PIO_STACK_LOCATION pIrpSp;
DbgDump(DBG_SERIAL, (">GetCommStatus (%p)\n", PIrp));
KeAcquireSpinLock(&PDevExt->ControlLock, &oldIrql);
pIrpSp = IoGetCurrentIrpStackLocation(PIrp);
if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SERIAL_STATUS)) {
status = STATUS_BUFFER_TOO_SMALL;
PIrp->IoStatus.Information = 0;
DbgDump(DBG_ERR, ("GetCommStatus: (0x%x)\n", status));
} else {
status = STATUS_SUCCESS;
PIrp->IoStatus.Information = sizeof(SERIAL_STATUS);
RtlZeroMemory(pSerialStatus, sizeof(SERIAL_STATUS));
pSerialStatus->Errors = 0;
pSerialStatus->EofReceived = FALSE;
pSerialStatus->WaitForImmediate = 0;
pSerialStatus->HoldReasons = 0;
#if defined (USE_RING_BUFF)
pSerialStatus->AmountInInQueue = PDevExt->RingBuff.CharsInBuff;
#else
pSerialStatus->AmountInInQueue = PDevExt->UsbReadBuffChars;
#endif
pSerialStatus->AmountInOutQueue= PDevExt->SerialPort.CharsInWriteBuf;
DbgDump(DBG_SERIAL, ("AmountInInQueue: %x\n", pSerialStatus->AmountInInQueue ));
DbgDump(DBG_SERIAL, ("AmountInOutQueue: %x\n", pSerialStatus->AmountInOutQueue));
}
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
DbgDump(DBG_SERIAL, ("<GetCommStatus %x\n", status));
return status;
}
__inline
NTSTATUS
GetModemStatus(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
DbgDump(DBG_SERIAL, (">GetModemStatus (%p)\n", PIrp));
// get current MSR
status = IoctlGetSerialValue(PDevExt,
PIrp,
sizeof( PDevExt->SerialPort.ModemStatus ),
&PDevExt->SerialPort.ModemStatus );
DbgDump(DBG_SERIAL, ("<GetModemStatus %x\n", status));
return status;
}
__inline
NTSTATUS
ImmediateChar(
IN PIRP Irp,
IN PDEVICE_OBJECT DeviceObject
)
{
PUCHAR Char = (PUCHAR) Irp->AssociatedIrp.SystemBuffer;
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION IrpStack;
DbgDump(DBG_SERIAL, (">ImmediateChar (%p)\n", Irp));
TEST_TRAP();
Irp->IoStatus.Information = 0;
IrpStack = IoGetCurrentIrpStackLocation(Irp);
if (IrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(UCHAR)) {
status = STATUS_BUFFER_TOO_SMALL;
DbgDump(DBG_ERR, ("ImmediateChar: (0x%x)\n", status));
} else {
//
// Fabricate a write irp & send it to our write path.
// We do this because the R/W path depends on receiving an Irp of type
// IRP_MJ_WRITE or IRP_MJ_READ. It would be easier to
// simply say "not supported", but legacy apps depend on this.
//
PIRP pIrp;
KEVENT event;
IO_STATUS_BLOCK ioStatusBlock = {0, 0};
LARGE_INTEGER startingOffset = {0, 0};
PAGED_CODE();
KeInitializeEvent(
&event,
NotificationEvent,
FALSE
);
pIrp = IoBuildSynchronousFsdRequest(
IRP_MJ_WRITE, // MajorFunction,
DeviceObject, // DeviceObject,
&Char, // Buffer,
sizeof(Char), // Length ,
&startingOffset,// StartingOffset,
&event, // Event,
&ioStatusBlock // OUT PIO_STATUS_BLOCK IoStatusBlock
);
if ( !pIrp ) {
status = Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
DbgDump(DBG_ERR, ("IoBuildSynchronousFsdRequest: STATUS_INSUFFICIENT_RESOURCES\n", status));
} else {
status = IoCallDriver( DeviceObject,
pIrp );
if ( STATUS_PENDING == status ) {
KeWaitForSingleObject( &event, Suspended, KernelMode, FALSE, NULL );
}
//
// Propogate Write status.
// Note: the system released the Irp we just created & sent
// when the Write completes.... so don't touch it.
//
status = ioStatusBlock.Status ;
}
}
DbgDump(DBG_SERIAL, ("<ImmediateChar, %x\n", status));
return status;
}
NTSTATUS
SerialPurgeRxClear(
IN PDEVICE_OBJECT PDevObj,
IN BOOLEAN CancelRead
)
{
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;
KIRQL irql;
DbgDump( DBG_SERIAL, (">SerialPurgeRxClear:%d\n", CancelRead));
//
// Cancel the USB INT & Read Irps, which effectvely NAKs all packets from the client
// device untill we resubmit it.
//
if ( CancelRead )
{
if (pDevExt->IntIrp)
{
status = CancelUsbInterruptIrp( PDevObj );
}
status = CancelUsbReadIrp( PDevObj );
}
if (STATUS_SUCCESS == status) {
//
// Now, purge the Rx buffer.
//
KeAcquireSpinLock(&pDevExt->ControlLock, &irql);
#if DBG
if ( DebugLevel & (DBG_DUMP_READS|DBG_READ_LENGTH))
{
ULONG i;
#if defined (USE_RING_BUFF)
KdPrint( ("PurgeRxBuff[%d]: ", pDevExt->RingBuff.CharsInBuff ));
for (i = 0; i < pDevExt->RingBuff.CharsInBuff; i++) {
KdPrint(("%02x ", *pDevExt->RingBuff.pHead++ & 0xFF));
}
#else
KdPrint( ("PurgeRxBuff[%d]: ", pDevExt->UsbReadBuffChars ));
for (i = 0; i < pDevExt->UsbReadBuffChars; i++) {
KdPrint(("%02x ", pDevExt->UsbReadBuff[i] & 0xFF));
}
#endif // USE_RING_BUFF
KdPrint(("\n"));
}
#endif // DBG
#if defined (USE_RING_BUFF)
pDevExt->RingBuff.CharsInBuff = 0;
pDevExt->RingBuff.pHead =
pDevExt->RingBuff.pTail =
pDevExt->RingBuff.pBase;
#else // USE_RING_BUFF
pDevExt->UsbReadBuffChars = 0;
pDevExt->UsbReadBuffIndex = 0;
#endif
if ( CancelRead ) {
//
// reset read states
//
InterlockedExchange(&pDevExt->UsbReadState, IRP_STATE_COMPLETE);
InterlockedExchange(&pDevExt->IntState, IRP_STATE_COMPLETE);
}
KeReleaseSpinLock(&pDevExt->ControlLock, irql);
}
DbgDump(DBG_SERIAL, ("<SerialPurgeRxClear:0x%x\n", status ));
return status;
}
__inline
NTSTATUS
Purge(
IN PDEVICE_OBJECT PDevObj,
IN PIRP Irp
)
/* ++
IOCTL_SERIAL_PURGE
Operation
Purges the specified operation(s) or queues: one or more of
the current and all pending writes, the current and all pending
reads, the transmit buffer if one exists, and the receive buffer
if one exists.
Input
Parameters.DeviceIoControl.InputBufferLength indicates the
size in bytes of the buffer at Irp->AssociatedIrp.SystemBuffer,
which contains a bitmask of type ULONG, indicating what to purge.
Output
None
I/O Status Block
The Information field is set to zero, and the Status field is set
to STATUS_SUCCESS or possibly to STATUS_PENDING,
STATUS_CANCELLED, or STATUS_INVALID_PARAMETER
-- */
{
PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
PIO_STACK_LOCATION pIrpStack;
NTSTATUS status = STATUS_DELETE_PENDING;
ULONG ulMask = 0;
KIRQL irql;
PIRP NulllIrp = NULL;
DbgDump(DBG_SERIAL|DBG_IRP, (">Purge (%p)\n", Irp));
KeAcquireSpinLock(&pDevExt->ControlLock, &irql);
Irp->IoStatus.Information = 0;
pIrpStack = IoGetCurrentIrpStackLocation(Irp);
if (!Irp->AssociatedIrp.SystemBuffer ||
pIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG) ) {
status = STATUS_BUFFER_TOO_SMALL;
DbgDump(DBG_ERR, ("Purge: (0x%x)\n", status));
} else {
ulMask = *((PULONG) Irp->AssociatedIrp.SystemBuffer);
// make sure purge request is valid
if ( (!ulMask) || (ulMask & ( ~( SERIAL_PURGE_TXABORT |
SERIAL_PURGE_RXABORT |
SERIAL_PURGE_TXCLEAR |
SERIAL_PURGE_RXCLEAR)))) {
status = STATUS_INVALID_PARAMETER;
DbgDump(DBG_ERR, ("Purge: (0x%x)\n", status));
} else {
DbgDump(DBG_SERIAL, ("Purge Mask: 0x%x\n", ulMask ));
status = STATUS_SUCCESS;
if ( ulMask & SERIAL_PURGE_RXCLEAR) {
//
// SERIAL_PURGE_RXCLEAR - Implies the receive buffer if exists.
//
DbgDump(DBG_SERIAL|DBG_IRP, ("SERIAL_PURGE_RXCLEAR\n"));
KeReleaseSpinLock(&pDevExt->ControlLock, irql);
status = SerialPurgeRxClear(PDevObj, TRUE);
if ( NT_SUCCESS(status) ) {
if ( !pDevExt->IntPipe.hPipe ) {
DbgDump(DBG_SERIAL, ("kick starting another USB Read\n" ));
status = UsbRead( pDevExt, FALSE );
} else {
DbgDump(DBG_SERIAL, ("kick starting another USB INT Read\n" ));
status = UsbInterruptRead( pDevExt );
}
}
if ( NT_SUCCESS(status) ) {
// should be STATUS_PENDING
status = STATUS_SUCCESS;
}
KeAcquireSpinLock(&pDevExt->ControlLock, &irql);
}
if (ulMask & SERIAL_PURGE_RXABORT) {
//
// SERIAL_PURGE_RXABORT - Implies the current and all pending reads.
//
DbgDump(DBG_SERIAL|DBG_IRP, ("SERIAL_PURGE_RXABORT\n"));
KeReleaseSpinLock(&pDevExt->ControlLock, irql);
// cancel all outstanding USB read requests
//status = CleanUpPacketList( PDevObj, &pDevExt->PendingReadPackets);
// cancel all outstanding user reads
KillAllPendingUserReads( PDevObj,
&pDevExt->UserReadQueue,
&pDevExt->UserReadIrp ); //&NulllIrp );
KeAcquireSpinLock(&pDevExt->ControlLock, &irql);
}
if (ulMask & SERIAL_PURGE_TXCLEAR) {
//
// SERIAL_PURGE_TXCLEAR - Implies the transmit buffer if exists
//
DbgDump(DBG_SERIAL|DBG_IRP, ("SERIAL_PURGE_TXCLEAR\n"));
pDevExt->SerialPort.CharsInWriteBuf = 0;
}
if (ulMask & SERIAL_PURGE_TXABORT) {
//
// SERIAL_PURGE_TXABORT - Implies the current and all pending writes.
//
DbgDump(DBG_SERIAL|DBG_IRP, ("SERIAL_PURGE_TXABORT\n"));
KeReleaseSpinLock(&pDevExt->ControlLock, irql);
//
// We don't queue write Irps, rather write packets.
// So, cancel all outstanding write requests
//
status = CleanUpPacketList( PDevObj,
&pDevExt->PendingWritePackets,
&pDevExt->PendingDataOutEvent
);
KeAcquireSpinLock(&pDevExt->ControlLock, &irql);
}
}
}
KeReleaseSpinLock(&pDevExt->ControlLock, irql);
DbgDump(DBG_SERIAL|DBG_IRP, ("<Purge %x\n", status));
return status;
}
__inline
NTSTATUS
GetHandflow(
IN PIRP Irp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
DbgDump(DBG_SERIAL, (">GetHandFlow (%p)\n", Irp));
status = IoctlGetSerialValue(PDevExt,
Irp,
sizeof( PDevExt->SerialPort.HandFlow ),
&PDevExt->SerialPort.HandFlow);
DBG_DUMP_SERIAL_HANDFLOW( PDevExt );
DbgDump(DBG_SERIAL, ("<GetHandFlow %x\n", status));
return status;
}
__inline
NTSTATUS
SetHandflow(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status= STATUS_DELETE_PENDING;
DbgDump(DBG_SERIAL, (">SetHandFlow(%p)\n", PIrp));
status = IoctlSetSerialValue( PDevExt,
PIrp,
sizeof( PDevExt->SerialPort.HandFlow ),
&PDevExt->SerialPort.HandFlow);
DBG_DUMP_SERIAL_HANDFLOW( PDevExt );
DbgDump(DBG_SERIAL, ("<SetHandFlow %x\n", status));
return status;
}
NTSTATUS
GetProperties(
IN PIRP Irp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
PSERIAL_COMMPROP Properties;
PIO_STACK_LOCATION IrpStack;
KIRQL oldIrql;
DbgDump(DBG_SERIAL, (">GetProperties (%p)\n", Irp));
KeAcquireSpinLock(&PDevExt->ControlLock, &oldIrql);
IrpStack = IoGetCurrentIrpStackLocation(Irp);
if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SERIAL_COMMPROP)) {
status = STATUS_BUFFER_TOO_SMALL;
DbgDump(DBG_ERR, ("GetProperties: (0x%x)\n", status));
} else {
Properties = (PSERIAL_COMMPROP)Irp->AssociatedIrp.SystemBuffer;
RtlZeroMemory(Properties, sizeof(SERIAL_COMMPROP));
Properties->PacketLength = sizeof(SERIAL_COMMPROP);
Properties->PacketVersion = 2;
Properties->ServiceMask = SERIAL_SP_SERIALCOMM;
// internal limits
Properties->MaxTxQueue = DEFAULT_PIPE_MAX_TRANSFER_SIZE;
#if defined (USE_RING_BUFF)
Properties->MaxRxQueue = PDevExt->RingBuff.Size;
#else
Properties->MaxRxQueue = PDevExt->UsbReadBuffSize;
#endif
Properties->MaxBaud = SERIAL_BAUD_USER; // SERIAL_BAUD_115200;
Properties->SettableBaud = PDevExt->SerialPort.SupportedBauds;
Properties->ProvSubType = SERIAL_SP_UNSPECIFIED; // SERIAL_SP_RS232;
Properties->ProvCapabilities = SERIAL_PCF_DTRDSR | SERIAL_PCF_RTSCTS
| SERIAL_PCF_CD | SERIAL_PCF_XONXOFF
| SERIAL_PCF_TOTALTIMEOUTS | SERIAL_PCF_INTTIMEOUTS
| SERIAL_PCF_SPECIALCHARS;
Properties->SettableParams = SERIAL_SP_BAUD | SERIAL_SP_CARRIER_DETECT;
Properties->SettableData = SERIAL_DATABITS_8;
Properties->SettableStopParity = SERIAL_STOPBITS_10 | SERIAL_STOPBITS_20
| SERIAL_PARITY_NONE | SERIAL_PARITY_ODD
| SERIAL_PARITY_EVEN | SERIAL_PARITY_MARK
| SERIAL_PARITY_SPACE;
#if defined (USE_RING_BUFF)
Properties->CurrentRxQueue = PDevExt->RingBuff.Size;
#else
Properties->CurrentRxQueue = PDevExt->UsbReadBuffSize;
Properties->CurrentTxQueue = PDevExt->ReadPipe.MaxPacketSize;
#endif
status = STATUS_SUCCESS;
}
KeReleaseSpinLock(&PDevExt->ControlLock, oldIrql);
if (STATUS_SUCCESS == status) {
Irp->IoStatus.Information = sizeof(SERIAL_COMMPROP);
} else {
Irp->IoStatus.Information = 0;
}
DbgDump(DBG_SERIAL, ("<GetProperties %x\n", status));
return status;
}
__inline
NTSTATUS
LsrmstInsert(
IN PIRP Irp,
IN PDEVICE_EXTENSION PDevExt
)
{
UNREFERENCED_PARAMETER( Irp );
UNREFERENCED_PARAMETER( PDevExt );
DbgDump(DBG_SERIAL, (">LsrmstInsert (%p)\n", Irp));
DbgDump(DBG_SERIAL, ("<LsrmstInsert (%x)\n", STATUS_NOT_SUPPORTED));
return STATUS_NOT_SUPPORTED;
}
__inline
NTSTATUS
ConfigSize(
IN PIRP Irp,
IN PDEVICE_EXTENSION PDevExt
)
{
PULONG ConfigSize = (PULONG) Irp->AssociatedIrp.SystemBuffer;
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION IrpStack;
UNREFERENCED_PARAMETER( PDevExt );
DbgDump(DBG_SERIAL, (">ConfigSize (%p)\n", Irp));
Irp->IoStatus.Information = 0;
IrpStack = IoGetCurrentIrpStackLocation(Irp);
if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) {
status = STATUS_BUFFER_TOO_SMALL;
DbgDump(DBG_ERR, ("ConfigSize: (0x%x)\n", status));
} else {
*ConfigSize = 0;
Irp->IoStatus.Information = sizeof(ULONG);
}
DbgDump(DBG_SERIAL, ("<ConfigSize %x\n", status));
return status;
}
__inline
NTSTATUS
GetStats(
IN PIRP PIrp,
IN PDEVICE_EXTENSION PDevExt
)
{
PSERIALPERF_STATS pStats = NULL;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS status = STATUS_DELETE_PENDING;
ULONG information = 0;
KIRQL irql;
DbgDump(DBG_SERIAL, (">GetStats %p\n", PIrp));
KeAcquireSpinLock(&PDevExt->ControlLock, &irql );
pIrpSp = IoGetCurrentIrpStackLocation(PIrp);
if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SERIALPERF_STATS) ) {
status = STATUS_BUFFER_TOO_SMALL;
DbgDump(DBG_ERR, ("GetStats: (0x%x)\n", status));
} else {
information = sizeof(SERIALPERF_STATS);
status = STATUS_SUCCESS;
pStats = (PSERIALPERF_STATS)PIrp->AssociatedIrp.SystemBuffer;
RtlZeroMemory(pStats , sizeof(SERIALPERF_STATS));
pStats->ReceivedCount = PDevExt->TtlUSBReadBytes;
pStats->TransmittedCount = PDevExt->TtlWriteBytes;
pStats->FrameErrorCount = PDevExt->ReadDeviceErrors + PDevExt->WriteDeviceErrors + PDevExt->IntDeviceErrors; // ??
pStats->SerialOverrunErrorCount = PDevExt->TtlUSBReadBuffOverruns;
#if defined (USE_RING_BUFF)
pStats->BufferOverrunErrorCount = PDevExt->TtlRingBuffOverruns;
#else
pStats->BufferOverrunErrorCount = 0;
#endif
pStats->ParityErrorCount = 0;
DbgDump(DBG_SERIAL, ("ReceivedCount: %d\n", pStats->ReceivedCount )); \
DbgDump(DBG_SERIAL, ("TransmittedCount: %d\n", pStats->TransmittedCount )); \
DbgDump(DBG_SERIAL, ("FrameErrorCount: %d\n", pStats->FrameErrorCount )); \
DbgDump(DBG_SERIAL, ("SerialOverrunErrorCount: %d\n", pStats->SerialOverrunErrorCount )); \
DbgDump(DBG_SERIAL, ("BufferOverrunErrorCount: %d\n", pStats->BufferOverrunErrorCount )); \
DbgDump(DBG_SERIAL, ("ParityErrorCount: %d\n", pStats->ParityErrorCount )); \
}
KeReleaseSpinLock(&PDevExt->ControlLock, irql);
PIrp->IoStatus.Information = information;
PIrp->IoStatus.Status = status;
DbgDump(DBG_SERIAL, ("<GetStats %x\n", status));
return status;
}
NTSTATUS
ClearStats(
IN PIRP Irp,
IN PDEVICE_EXTENSION PDevExt
)
{
NTSTATUS status = STATUS_DELETE_PENDING;
KIRQL irql;
DbgDump(DBG_SERIAL, (">ClearStats (%p)\n", Irp));
KeAcquireSpinLock(&PDevExt->ControlLock, &irql);
PDevExt->TtlWriteRequests = 0;
PDevExt->TtlWriteBytes = 0;
PDevExt->TtlReadRequests = 0;
PDevExt->TtlReadBytes = 0;
PDevExt->TtlUSBReadRequests = 0;
PDevExt->TtlUSBReadBytes = 0;
PDevExt->TtlUSBReadBuffOverruns = 0;
#if defined (USE_RING_BUFF)
PDevExt->TtlRingBuffOverruns = 0;
#endif
status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
KeReleaseSpinLock(&PDevExt->ControlLock, irql);
DbgDump(DBG_SERIAL, ("<ClearStats %x\n", status));
return status;
}
/*++
Note: Unhandled IOCTL_SERIAL_: 0x2b002c : function code 11 is IOCTL_MODEM_CHECK_FOR_MODEM,
which if unhandled tells the system to load modem.sys over this serial port driver.
This is setup by RAS & unimodem.
--*/
NTSTATUS
SerialIoctl(
PDEVICE_OBJECT PDevObj,
PIRP PIrp
)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)PDevObj->DeviceExtension;
PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(PIrp);
ULONG ioctl = pIrpSp->Parameters.DeviceIoControl.IoControlCode;
NTSTATUS status = STATUS_NOT_SUPPORTED;
BOOLEAN bSignalNeeded = FALSE;
KIRQL irql;
LONG lSanity = 0;
DbgDump(DBG_SERIAL|DBG_TRACE, (">SerialIoctl(%p)\n", PIrp));
do {
KeAcquireSpinLock(&pDevExt->ControlLock, &irql);
//
// Make sure the device is accepting request
//
if ( !CanAcceptIoRequests( PDevObj, FALSE, TRUE) ||
!NT_SUCCESS(AcquireRemoveLock(&pDevExt->RemoveLock, PIrp)) )
{
status = STATUS_DELETE_PENDING;
DbgDump(DBG_WRN, ("SerialIoctl: 0x%x, 0x%x\n", ioctl, status));
PIrp->IoStatus.Status = status;
KeReleaseSpinLock(&pDevExt->ControlLock, irql);
IoCompleteRequest(PIrp, IO_NO_INCREMENT);
return status;
}
ASSERT_SERIAL_PORT( pDevExt->SerialPort );
KeReleaseSpinLock(&pDevExt->ControlLock, irql);
switch (ioctl)
{
case IOCTL_SERIAL_SET_BAUD_RATE:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_SET_BAUD_RATE\n"));
status = SetBaudRate(PIrp, pDevExt);
break;
case IOCTL_SERIAL_GET_BAUD_RATE:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_GET_BAUD_RATE\n"));
status = GetBaudRate(PIrp, pDevExt);
break;
case IOCTL_SERIAL_SET_LINE_CONTROL:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_SET_LINE_CONTROL\n"));
status = SetLineControl(PIrp, pDevExt);
break;
case IOCTL_SERIAL_GET_LINE_CONTROL:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_GET_LINE_CONTROL\n"));
status = GetLineControl(PIrp, pDevExt);
break;
case IOCTL_SERIAL_SET_TIMEOUTS:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_SET_TIMEOUTS\n"));
status = SetTimeouts(PIrp, pDevExt);
break;
case IOCTL_SERIAL_GET_TIMEOUTS:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_GET_TIMEOUTS\n"));
status = GetTimeouts(PIrp, pDevExt);
break;
case IOCTL_SERIAL_SET_CHARS:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_SET_CHARS\n"));
status = SetSpecialChars(PIrp, pDevExt);
break;
case IOCTL_SERIAL_GET_CHARS:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_GET_CHARS\n"));
status = GetSpecialChars(PIrp, pDevExt);
break;
case IOCTL_SERIAL_SET_DTR:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_SET_DTR\n"));
status = SetClearDTR(pDevExt, PIrp, TRUE);
break;
case IOCTL_SERIAL_CLR_DTR:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_CLR_DTR\n"));
status = SetClearDTR(pDevExt, PIrp, FALSE);
break;
case IOCTL_SERIAL_SET_RTS:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_SET_RTS\n"));
status = SetClearRTS(pDevExt, PIrp, TRUE);
break;
case IOCTL_SERIAL_CLR_RTS:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_CLR_RTS\n"));
status = SetClearRTS(pDevExt, PIrp, FALSE);
break;
case IOCTL_SERIAL_GET_DTRRTS:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_GET_DTRRTS\n"));
status = GetDtrRts(PIrp, pDevExt);
break;
case IOCTL_SERIAL_SET_BREAK_ON:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_SET_BREAK_ON\n"));
status = SetBreak(PIrp, pDevExt, 0xFFFF);
break;
case IOCTL_SERIAL_SET_BREAK_OFF:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_SET_BREAK_OFF\n"));
status = SetBreak(PIrp, pDevExt, 0);
break;
case IOCTL_SERIAL_SET_QUEUE_SIZE:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_SET_QUEUE_SIZE\n"));
status = SetQueueSize(PIrp, pDevExt);
break;
case IOCTL_SERIAL_GET_WAIT_MASK:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_GET_WAIT_MASK\n"));
status = GetWaitMask(PIrp, pDevExt);
break;
case IOCTL_SERIAL_SET_WAIT_MASK:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_SET_WAIT_MASK\n"));
status = SetWaitMask(PIrp, pDevExt);
break;
case IOCTL_SERIAL_WAIT_ON_MASK:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_WAIT_ON_MASK\n"));
status = WaitOnMask(PIrp, pDevExt);
break;
case IOCTL_SERIAL_GET_MODEMSTATUS:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_GET_MODEMSTATUS\n"));
status = GetModemStatus(PIrp, pDevExt);
break;
case IOCTL_SERIAL_GET_COMMSTATUS:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_GET_COMMSTATUS\n"));
status = GetCommStatus(PIrp, pDevExt);
break;
case IOCTL_SERIAL_IMMEDIATE_CHAR:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_IMMEDIATE_CHAR\n"));
status = ImmediateChar(PIrp, PDevObj);
break;
case IOCTL_SERIAL_PURGE:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_PURGE\n"));
status = Purge(PDevObj, PIrp);
break;
case IOCTL_SERIAL_GET_HANDFLOW:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_GET_HANDFLOW\n"));
status = GetHandflow(PIrp, pDevExt);
break;
case IOCTL_SERIAL_SET_HANDFLOW:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_SET_HANDFLOW\n"));
status = SetHandflow(PIrp, pDevExt);
break;
case IOCTL_SERIAL_RESET_DEVICE:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_RESET_DEVICE\n"));
status = SerialResetDevice(pDevExt, PIrp, TRUE);
break;
case IOCTL_SERIAL_LSRMST_INSERT:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_LSRMST_INSERT\n"));
status = LsrmstInsert(PIrp, pDevExt);
break;
case IOCTL_SERIAL_CONFIG_SIZE:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_CONFIG_SIZE\n"));
status = ConfigSize(PIrp, pDevExt);
break;
case IOCTL_SERIAL_GET_STATS:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_GET_STATS\n"));
status = GetStats(PIrp, pDevExt);
break;
case IOCTL_SERIAL_CLEAR_STATS:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_CLEAR_STATS\n"));
status = ClearStats(PIrp, pDevExt);
break;
case IOCTL_SERIAL_GET_PROPERTIES:
DbgDump(DBG_SERIAL, ("IOCTL_SERIAL_GET_PROPERTIES\n"));
status = GetProperties(PIrp, pDevExt);
break;
default:
DbgDump(DBG_WRN, ("Unhandled IOCTL_SERIAL_: 0x%x : function code %d\n",
ioctl, SERIAL_FNCT_CODE(ioctl) ) );
status = STATUS_NOT_SUPPORTED;
break;
}
} while (0);
//
// Don't complete any pending Irps
//
if ( STATUS_PENDING != status) {
PIrp->IoStatus.Status = status;
ReleaseRemoveLock(&pDevExt->RemoveLock, PIrp);
IoCompleteRequest(PIrp, IO_SERIAL_INCREMENT);
}
#ifdef DELAY_RXBUFF
//
// Special Case: the device was just opened.
// To emulate a serial port RX buffer we need to kick start a USB read.
// We don't want to do this in the IRP_MJ_CREATE code since AS does
// IOCTL_SERIAL_SET_WAIT_MASK then *two* IOCTL_SERIAL_SET_DTR requests. If we start the USB read
// too soon then the CE device can get confused with a Read then SetDTR requests.
// So, if we were just opened, and we have seen *one* successful IOCTL_SERIAL_SET_DTR then start our USB read.
// Two good DTRs works better, but we'll let one slip for a timeout or something to recover, e.g. iPAQ on NEC E13+.
// This may cause problems with other apps, but our target is ActiveSync. We could add a magic registry flag is required.
// This means that outside of the initial get/set descriptors/configuration the USB is quiet until
// an app opens the device for I/O... which is a good thing.
// However, this implementation causes the initial connect to a bit too slow for slow devices (e.g., HP Jornada, Cassiopeia).
//
if ( pDevExt->StartUsbRead && (IOCTL_SERIAL_SET_DTR == ioctl) && (STATUS_SUCCESS == status))
{
if ( 0 == InterlockedDecrement(&pDevExt->StartUsbRead))
{
if ( !pDevExt->IntPipe.hPipe ) {
DbgDump(DBG_SERIAL, ("SerialIoctl: kick starting another USB Read\n" ));
status = UsbRead( pDevExt, FALSE );
} else {
DbgDump(DBG_SERIAL, ("SerialIoctl: kick starting another USB INT Read\n" ));
status = UsbInterruptRead( pDevExt );
}
if ( NT_SUCCESS(status) ) {
// should be STATUS_PENDING
status = STATUS_SUCCESS;
} else {
InterlockedIncrement(&pDevExt->StartUsbRead);
}
}
}
#endif
DbgDump(DBG_SERIAL|DBG_TRACE, ("<SerialIoctl: %p, 0x%x\n", PIrp, status));
return status;
}
// EOF