/* ++ 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, ("GetBaudRate(%p)\n", PIrp)); status = IoctlGetSerialValue( PDevExt, PIrp, sizeof( PDevExt->SerialPort.CurrentBaud ), &PDevExt->SerialPort.CurrentBaud); DBG_DUMP_BAUD_RATE(PDevExt); 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, ("GetLineControl(%p)\n", PIrp)); status = IoctlGetSerialValue( PDevExt, PIrp, sizeof(PDevExt->SerialPort.LineControl), &PDevExt->SerialPort.LineControl ); DBG_DUMP_LINE_CONTROL( PDevExt ); DbgDump(DBG_SERIAL, ("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,("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, ("SetSpecialChars(%p)\n", PIrp)); status = IoctlSetSerialValue( PDevExt, PIrp, sizeof(PDevExt->SerialPort.SpecialChars), &PDevExt->SerialPort.SpecialChars); DBG_DUMP_SERIAL_CHARS( PDevExt); 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, ("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, ("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, ("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, ("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, ("SetBreak(%p)\n", PIrp)); DbgDump(DBG_SERIAL, ("= 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, ("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, ("= 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, ("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, ("= 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, ("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, ("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, ("= 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, ("GetModemStatus (%p)\n", PIrp)); // get current MSR status = IoctlGetSerialValue(PDevExt, PIrp, sizeof( PDevExt->SerialPort.ModemStatus ), &PDevExt->SerialPort.ModemStatus ); DbgDump(DBG_SERIAL, ("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, ("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, ("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, ("GetHandFlow (%p)\n", Irp)); status = IoctlGetSerialValue(PDevExt, Irp, sizeof( PDevExt->SerialPort.HandFlow ), &PDevExt->SerialPort.HandFlow); DBG_DUMP_SERIAL_HANDFLOW( PDevExt ); 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, ("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, ("LsrmstInsert (%p)\n", Irp)); DbgDump(DBG_SERIAL, ("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, ("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, ("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, ("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, ("