VOID UpdateTransmitToggle( PC0C_IO_PORT pIoPort, PLIST_ENTRY pQueueToComplete) { if ((pIoPort->handFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { UCHAR bits; if ((pIoPort->writeHolding & SERIAL_TX_WAITING_ON_BREAK) != 0 || !C0C_TX_BUFFER_EMPTY(&pIoPort->txBuf) || (pIoPort->irpQueues[C0C_QUEUE_WRITE].pCurrent && !pIoPort->writeHolding) || (pIoPort->sendXonXoff && (pIoPort->writeHolding & ~SERIAL_TX_WAITING_FOR_XON) == 0)) { bits = C0C_MCR_RTS; } else { bits = 0; } SetModemControl(pIoPort, bits, C0C_MCR_RTS, pQueueToComplete); } }
NTSTATUS FdoPortClose(IN PC0C_FDOPORT_EXTENSION pDevExt, IN PIRP pIrp) { NTSTATUS status; LIST_ENTRY queueToComplete; KIRQL oldIrql; PC0C_IO_PORT pIoPort; pIoPort = pDevExt->pIoPortLocal; pIoPort->isOpen = FALSE; if (pIoPort->pIoPortRemote->plugInMode) IoInvalidateDeviceRelations(pIoPort->pIoPortRemote->pPhDevObj, BusRelations); InitializeListHead(&queueToComplete); KeAcquireSpinLock(pIoPort->pIoLock, &oldIrql); pIoPort->escapeChar = 0; pIoPort->writeHoldingRemote = 0; pIoPort->sendXonXoff = 0; SetModemControl(pIoPort, C0C_MCR_OUT2, C0C_MCR_MASK | C0C_MCR_OPEN, &queueToComplete); FreeBuffer(&pIoPort->readBuf); SetBreakHolding(pIoPort, FALSE, &queueToComplete); KeReleaseSpinLock(pIoPort->pIoLock, oldIrql); FdoPortCompleteQueue(&queueToComplete); status = FdoPortStartIrp(pIoPort, pIrp, C0C_QUEUE_CLOSE, StartIrpClose); if (status != STATUS_PENDING) { pIrp->IoStatus.Status = status; IoCompleteRequest(pIrp, IO_NO_INCREMENT); } return status; }
NTSTATUS FdoPortIoCtl( IN PC0C_FDOPORT_EXTENSION pDevExt, IN PIRP pIrp) { NTSTATUS status; PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp); ULONG code = pIrpStack->Parameters.DeviceIoControl.IoControlCode; KIRQL oldIrql; PC0C_IO_PORT pIoPortLocal; pIrp->IoStatus.Information = 0; pIoPortLocal = pDevExt->pIoPortLocal; if ((pIoPortLocal->handFlow.ControlHandShake & SERIAL_ERROR_ABORT) && pIoPortLocal->errors && code != IOCTL_SERIAL_GET_COMMSTATUS) { status = STATUS_CANCELLED; } else { status = STATUS_SUCCESS; switch (code) { case IOCTL_SERIAL_SET_RTS: case IOCTL_SERIAL_CLR_RTS: KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); switch (pIoPortLocal->handFlow.FlowReplace & SERIAL_RTS_MASK) { case SERIAL_RTS_HANDSHAKE: case SERIAL_TRANSMIT_TOGGLE: KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); status = STATUS_INVALID_PARAMETER; break; default: { LIST_ENTRY queueToComplete; InitializeListHead(&queueToComplete); SetModemControl( pIoPortLocal, code == IOCTL_SERIAL_SET_RTS ? C0C_MCR_RTS : 0, C0C_MCR_RTS, &queueToComplete); if (pIoPortLocal->pIoPortRemote->tryWrite || pIoPortLocal->tryWrite) { ReadWrite( pIoPortLocal, FALSE, pIoPortLocal->pIoPortRemote, FALSE, &queueToComplete); } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); FdoPortCompleteQueue(&queueToComplete); } } break; case IOCTL_SERIAL_SET_DTR: case IOCTL_SERIAL_CLR_DTR: KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); switch (pIoPortLocal->handFlow.ControlHandShake & SERIAL_DTR_MASK) { case SERIAL_DTR_HANDSHAKE: KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); status = STATUS_INVALID_PARAMETER; break; default: { LIST_ENTRY queueToComplete; InitializeListHead(&queueToComplete); SetModemControl( pIoPortLocal, code == IOCTL_SERIAL_SET_DTR ? C0C_MCR_DTR : 0, C0C_MCR_DTR, &queueToComplete); if (pIoPortLocal->pIoPortRemote->tryWrite || pIoPortLocal->tryWrite) { ReadWrite( pIoPortLocal, FALSE, pIoPortLocal->pIoPortRemote, FALSE, &queueToComplete); } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); FdoPortCompleteQueue(&queueToComplete); } } break; case IOCTL_SERIAL_SET_MODEM_CONTROL: { LIST_ENTRY queueToComplete; UCHAR mask; PUCHAR pSysBuf; ULONG InputBufferLength; InputBufferLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; if (InputBufferLength < sizeof(ULONG)) { status = STATUS_BUFFER_TOO_SMALL; break; } pSysBuf = (PUCHAR)pIrp->AssociatedIrp.SystemBuffer; if (InputBufferLength >= (sizeof(ULONG) + sizeof(ULONG) + C0CE_SIGNATURE_SIZE) && RtlEqualMemory(pSysBuf + sizeof(ULONG) + sizeof(ULONG), C0CE_SIGNATURE, C0CE_SIGNATURE_SIZE)) { mask = C0C_MCR_MASK & (UCHAR)*((PULONG)pSysBuf + 1); } else { mask = C0C_MCR_MASK; } InitializeListHead(&queueToComplete); KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); SetModemControl(pIoPortLocal, (UCHAR)*(PULONG)pSysBuf, mask, &queueToComplete); if (pIoPortLocal->pIoPortRemote->tryWrite || pIoPortLocal->tryWrite) { ReadWrite( pIoPortLocal, FALSE, pIoPortLocal->pIoPortRemote, FALSE, &queueToComplete); } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); FdoPortCompleteQueue(&queueToComplete); break; } case IOCTL_SERIAL_GET_MODEM_CONTROL: case IOCTL_SERIAL_GET_DTRRTS: { ULONG modemControl; PUCHAR pSysBuf; ULONG OutputBufferLength; OutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; if (OutputBufferLength < sizeof(ULONG)) { status = STATUS_BUFFER_TOO_SMALL; break; } KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); modemControl = pIoPortLocal->modemControl; KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); pSysBuf = (PUCHAR)pIrp->AssociatedIrp.SystemBuffer; if (code == IOCTL_SERIAL_GET_DTRRTS) { modemControl &= SERIAL_DTR_STATE | SERIAL_RTS_STATE; pIrp->IoStatus.Information = sizeof(ULONG); } else { ULONG InputBufferLength; InputBufferLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; if (OutputBufferLength >= (sizeof(ULONG) + C0CE_SIGNATURE_SIZE) && InputBufferLength >= C0CE_SIGNATURE_SIZE && RtlEqualMemory(pSysBuf, C0CE_SIGNATURE, C0CE_SIGNATURE_SIZE)) { RtlCopyMemory(pSysBuf + sizeof(PULONG), C0CE_SIGNATURE, C0CE_SIGNATURE_SIZE); if (OutputBufferLength > (sizeof(ULONG) + C0CE_SIGNATURE_SIZE)) { RtlZeroMemory(pSysBuf + sizeof(ULONG) + C0CE_SIGNATURE_SIZE, OutputBufferLength - (sizeof(ULONG) + C0CE_SIGNATURE_SIZE)); } pIrp->IoStatus.Information = OutputBufferLength; } else { pIrp->IoStatus.Information = sizeof(ULONG); } modemControl &= C0C_MCR_MASK; } *(PULONG)pSysBuf = modemControl; TraceIrp("FdoPortIoCtl", pIrp, &status, TRACE_FLAG_RESULTS); break; } case IOCTL_SERIAL_SET_XON: { LIST_ENTRY queueToComplete; InitializeListHead(&queueToComplete); KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); SetXonXoffHolding(pIoPortLocal, C0C_XCHAR_ON); if (pIoPortLocal->tryWrite) { ReadWrite( pIoPortLocal, FALSE, pIoPortLocal->pIoPortRemote, FALSE, &queueToComplete); } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); FdoPortCompleteQueue(&queueToComplete); break; } case IOCTL_SERIAL_SET_XOFF: KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); SetXonXoffHolding(pIoPortLocal, C0C_XCHAR_OFF); KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); break; case IOCTL_SERIAL_SET_BREAK_ON: { LIST_ENTRY queueToComplete; InitializeListHead(&queueToComplete); KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); SetBreakHolding(pIoPortLocal, TRUE, &queueToComplete); UpdateTransmitToggle(pIoPortLocal, &queueToComplete); ReadWrite( pIoPortLocal, FALSE, pIoPortLocal->pIoPortRemote, FALSE, &queueToComplete); KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); FdoPortCompleteQueue(&queueToComplete); break; } case IOCTL_SERIAL_SET_BREAK_OFF: { LIST_ENTRY queueToComplete; InitializeListHead(&queueToComplete); KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); SetBreakHolding(pIoPortLocal, FALSE, &queueToComplete); UpdateTransmitToggle(pIoPortLocal, &queueToComplete); if (pIoPortLocal->tryWrite || pIoPortLocal->pIoPortRemote->tryWrite) { ReadWrite( pIoPortLocal, FALSE, pIoPortLocal->pIoPortRemote, FALSE, &queueToComplete); } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); FdoPortCompleteQueue(&queueToComplete); break; } case IOCTL_SERIAL_GET_MODEMSTATUS: if (pIrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) { status = STATUS_BUFFER_TOO_SMALL; break; } KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); *(PULONG)pIrp->AssociatedIrp.SystemBuffer = pIoPortLocal->modemStatus; KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); pIrp->IoStatus.Information = sizeof(ULONG); TraceIrp("FdoPortIoCtl", pIrp, &status, TRACE_FLAG_RESULTS); break; case IOCTL_SERIAL_SET_WAIT_MASK: status = FdoPortSetWaitMask(pIoPortLocal, pIrp, pIrpStack); break; case IOCTL_SERIAL_GET_WAIT_MASK: status = FdoPortGetWaitMask(pIoPortLocal, pIrp, pIrpStack); TraceIrp("FdoPortIoCtl", pIrp, &status, TRACE_FLAG_RESULTS); break; case IOCTL_SERIAL_WAIT_ON_MASK: status = FdoPortWaitOnMask(pIoPortLocal, pIrp, pIrpStack); #if ENABLE_TRACING if (status == STATUS_SUCCESS) TraceIrp("FdoPortIoCtl", pIrp, &status, TRACE_FLAG_RESULTS); #endif /* ENABLE_TRACING */ break; case IOCTL_SERIAL_IMMEDIATE_CHAR: status = FdoPortImmediateChar(pIoPortLocal, pIrp, pIrpStack); break; case IOCTL_SERIAL_XOFF_COUNTER: status = FdoPortXoffCounter(pIoPortLocal, pIrp, pIrpStack); break; case IOCTL_SERIAL_PURGE: { LIST_ENTRY queueToComplete; PULONG pSysBuf; if (pIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG)) { status = STATUS_BUFFER_TOO_SMALL; break; } pSysBuf = (PULONG)pIrp->AssociatedIrp.SystemBuffer; if (*pSysBuf & ~( SERIAL_PURGE_TXABORT | SERIAL_PURGE_RXABORT | SERIAL_PURGE_TXCLEAR | SERIAL_PURGE_RXCLEAR )) { status = STATUS_INVALID_PARAMETER; break; } InitializeListHead(&queueToComplete); KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); if (*pSysBuf & SERIAL_PURGE_RXABORT) CancelQueue(&pIoPortLocal->irpQueues[C0C_QUEUE_READ], &queueToComplete); if (*pSysBuf & SERIAL_PURGE_TXABORT) CancelQueue(&pIoPortLocal->irpQueues[C0C_QUEUE_WRITE], &queueToComplete); if (*pSysBuf & SERIAL_PURGE_RXCLEAR) { PurgeBuffer(&pIoPortLocal->readBuf); UpdateHandFlow(pIoPortLocal, TRUE, &queueToComplete); if (pIoPortLocal->tryWrite || pIoPortLocal->pIoPortRemote->tryWrite) { ReadWrite( pIoPortLocal, FALSE, pIoPortLocal->pIoPortRemote, FALSE, &queueToComplete); } } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); FdoPortCompleteQueue(&queueToComplete); break; } case IOCTL_SERIAL_GET_COMMSTATUS: { PSERIAL_STATUS pSysBuf; PIRP pIrpWrite; if (pIrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SERIAL_STATUS)) { status = STATUS_BUFFER_TOO_SMALL; break; } pSysBuf = (PSERIAL_STATUS)pIrp->AssociatedIrp.SystemBuffer; RtlZeroMemory(pSysBuf, sizeof(*pSysBuf)); KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); pSysBuf->AmountInInQueue = (ULONG)C0C_BUFFER_BUSY(&pIoPortLocal->readBuf); pIrpWrite = pIoPortLocal->irpQueues[C0C_QUEUE_WRITE].pCurrent; if (pIrpWrite) { PIO_STACK_LOCATION pIrpStackWrite = IoGetCurrentIrpStackLocation(pIrpWrite); if (pIrpStackWrite->MajorFunction == IRP_MJ_DEVICE_CONTROL && pIrpStackWrite->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_IMMEDIATE_CHAR) { pSysBuf->WaitForImmediate = TRUE; } } pSysBuf->AmountInOutQueue = pIoPortLocal->amountInWriteQueue; pSysBuf->HoldReasons = pIoPortLocal->writeHolding; if ((pIoPortLocal->handFlow.ControlHandShake & SERIAL_DSR_SENSITIVITY) && (pIoPortLocal->modemStatus & C0C_MSB_DSR) == 0) { pSysBuf->HoldReasons |= SERIAL_RX_WAITING_FOR_DSR; } if (pIoPortLocal->writeHoldingRemote & SERIAL_TX_WAITING_FOR_XON) pSysBuf->HoldReasons |= SERIAL_TX_WAITING_XOFF_SENT; pSysBuf->Errors = pIoPortLocal->errors; pIoPortLocal->errors = 0; KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); pIrp->IoStatus.Information = sizeof(SERIAL_STATUS); TraceIrp("FdoPortIoCtl", pIrp, &status, TRACE_FLAG_RESULTS); break; } case IOCTL_SERIAL_SET_HANDFLOW: { LIST_ENTRY queueToComplete; PSERIAL_HANDFLOW pSysBuf; if (pIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SERIAL_HANDFLOW)) { status = STATUS_BUFFER_TOO_SMALL; break; } pSysBuf = (PSERIAL_HANDFLOW)pIrp->AssociatedIrp.SystemBuffer; if (pSysBuf->ControlHandShake & SERIAL_CONTROL_INVALID || pSysBuf->FlowReplace & SERIAL_FLOW_INVALID || (pSysBuf->ControlHandShake & SERIAL_DTR_MASK) == SERIAL_DTR_MASK || pSysBuf->XonLimit < 0 || pSysBuf->XoffLimit < 0) { status = STATUS_INVALID_PARAMETER; break; } InitializeListHead(&queueToComplete); KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); status = SetHandFlow(pIoPortLocal, pSysBuf, &queueToComplete); KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); FdoPortCompleteQueue(&queueToComplete); break; } case IOCTL_SERIAL_GET_HANDFLOW: if (pIrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SERIAL_HANDFLOW)) { status = STATUS_BUFFER_TOO_SMALL; break; } KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); *(PSERIAL_HANDFLOW)pIrp->AssociatedIrp.SystemBuffer = pIoPortLocal->handFlow; KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); pIrp->IoStatus.Information = sizeof(SERIAL_HANDFLOW); TraceIrp("FdoPortIoCtl", pIrp, &status, TRACE_FLAG_RESULTS); break; case IOCTL_SERIAL_SET_TIMEOUTS: status = FdoPortSetTimeouts(pIoPortLocal, pIrp, pIrpStack); break; case IOCTL_SERIAL_GET_TIMEOUTS: status = FdoPortGetTimeouts(pIoPortLocal, pIrp, pIrpStack); TraceIrp("FdoPortIoCtl", pIrp, &status, TRACE_FLAG_RESULTS); break; case IOCTL_SERIAL_SET_CHARS: { PSERIAL_CHARS pSysBuf; if (pIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SERIAL_CHARS)) { status = STATUS_BUFFER_TOO_SMALL; break; } pSysBuf = (PSERIAL_CHARS)pIrp->AssociatedIrp.SystemBuffer; KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); if (pIoPortLocal->escapeChar && ((pIoPortLocal->escapeChar == pSysBuf->XoffChar) || (pIoPortLocal->escapeChar == pSysBuf->XonChar))) { status = STATUS_INVALID_PARAMETER; } if (status == STATUS_SUCCESS) pIoPortLocal->specialChars = *pSysBuf; KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); break; } case IOCTL_SERIAL_GET_CHARS: if (pIrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SERIAL_CHARS)) { status = STATUS_BUFFER_TOO_SMALL; break; } KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); *(PSERIAL_CHARS)pIrp->AssociatedIrp.SystemBuffer = pIoPortLocal->specialChars; KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); pIrp->IoStatus.Information = sizeof(SERIAL_CHARS); TraceIrp("FdoPortIoCtl", pIrp, &status, TRACE_FLAG_RESULTS); break; case IOCTL_SERIAL_LSRMST_INSERT: { ULONG Information; ULONG optsAndBits; UCHAR escapeChar; PUCHAR pSysBuf; ULONG InputBufferLength; BOOLEAN extended; InputBufferLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; if (InputBufferLength < sizeof(UCHAR)) { status = STATUS_BUFFER_TOO_SMALL; break; } Information = 0; pSysBuf = (PUCHAR)pIrp->AssociatedIrp.SystemBuffer; escapeChar = *pSysBuf; if (InputBufferLength >= (sizeof(UCHAR) + C0CE_SIGNATURE_SIZE + sizeof(ULONG)) && RtlEqualMemory(pSysBuf + 1, C0CE_SIGNATURE, C0CE_SIGNATURE_SIZE)) { extended = TRUE; optsAndBits = *(ULONG *)(pSysBuf + 1 + C0CE_SIGNATURE_SIZE); #define C0CE_INSERT_OPTS ( \ C0CE_INSERT_IOCTL_GET| \ C0CE_INSERT_IOCTL_RXCLEAR) #define C0CE_INSERT_BITS ( \ C0CE_INSERT_ENABLE_LSR| \ C0CE_INSERT_ENABLE_MST| \ C0CE_INSERT_ENABLE_RBR| \ C0CE_INSERT_ENABLE_RLC| \ C0CE_INSERT_ENABLE_LSR_BI) #define C0CE_INSERT_CAPS (C0CE_INSERT_OPTS|C0CE_INSERT_BITS) if (optsAndBits == C0CE_INSERT_IOCTL_CAPS) { optsAndBits = 0; Information += C0CE_SIGNATURE_SIZE + sizeof(ULONG); if (pIrpStack->Parameters.DeviceIoControl.OutputBufferLength < Information) { status = STATUS_BUFFER_TOO_SMALL; break; } RtlCopyMemory(pSysBuf, C0CE_SIGNATURE, C0CE_SIGNATURE_SIZE); *(ULONG *)(pSysBuf + C0CE_SIGNATURE_SIZE) = C0CE_INSERT_CAPS; } else { if (optsAndBits & ~C0CE_INSERT_CAPS) { status = STATUS_INVALID_PARAMETER; break; } if (optsAndBits & C0CE_INSERT_IOCTL_GET) { if (optsAndBits & (C0CE_INSERT_ENABLE_LSR|C0CE_INSERT_ENABLE_LSR_BI)) Information += sizeof(UCHAR)*2 + sizeof(UCHAR); if (optsAndBits & C0CE_INSERT_ENABLE_MST) Information += sizeof(UCHAR)*2 + sizeof(UCHAR); if (optsAndBits & C0CE_INSERT_ENABLE_RBR) Information += sizeof(UCHAR)*2 + sizeof(ULONG); if (optsAndBits & C0CE_INSERT_ENABLE_RLC) Information += sizeof(UCHAR)*2 + sizeof(UCHAR)*3; } if (pIrpStack->Parameters.DeviceIoControl.OutputBufferLength < Information) { status = STATUS_BUFFER_TOO_SMALL; break; } } } else { extended = FALSE; optsAndBits = (C0CE_INSERT_ENABLE_LSR|C0CE_INSERT_ENABLE_MST); } KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); if (escapeChar && ((escapeChar == pIoPortLocal->specialChars.XoffChar) || (escapeChar == pIoPortLocal->specialChars.XonChar) || (pIoPortLocal->handFlow.FlowReplace & SERIAL_ERROR_CHAR))) { status = STATUS_INVALID_PARAMETER; KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); break; } pIoPortLocal->insertMask = optsAndBits & C0CE_INSERT_BITS; pIoPortLocal->escapeChar = escapeChar; if (extended) { LIST_ENTRY queueToComplete; PC0C_IO_PORT pIoPortRemote; InitializeListHead(&queueToComplete); pIoPortRemote = pIoPortLocal->pIoPortRemote; if (optsAndBits & C0CE_INSERT_IOCTL_GET) { if (optsAndBits & (C0CE_INSERT_ENABLE_LSR|C0CE_INSERT_ENABLE_LSR_BI)) { UCHAR lsr = 0; if (C0C_TX_BUFFER_THR_EMPTY(&pIoPortLocal->txBuf)) { lsr |= 0x20; /* transmit holding register empty */ if (C0C_TX_BUFFER_EMPTY(&pIoPortLocal->txBuf)) lsr |= 0x40; /* transmit holding register empty and line is idle */ } if ((optsAndBits & C0CE_INSERT_ENABLE_LSR_BI) != 0 && pIoPortLocal->rcvdBreak) lsr |= 0x10; /* break interrupt indicator */ *pSysBuf++ = escapeChar; *pSysBuf++ = SERIAL_LSRMST_LSR_NODATA; *pSysBuf++ = lsr; } if (optsAndBits & C0CE_INSERT_ENABLE_MST) { *pSysBuf++ = escapeChar; *pSysBuf++ = SERIAL_LSRMST_MST; *pSysBuf++ = pIoPortLocal->modemStatus; } if (optsAndBits & C0CE_INSERT_ENABLE_RBR) { *pSysBuf++ = escapeChar; *pSysBuf++ = C0CE_INSERT_RBR; *(ULONG *)pSysBuf = pIoPortRemote->baudRate.BaudRate; pSysBuf += sizeof(ULONG); } if (optsAndBits & C0CE_INSERT_ENABLE_RLC) { *pSysBuf++ = escapeChar; *pSysBuf++ = C0CE_INSERT_RLC; *pSysBuf++ = pIoPortRemote->lineControl.WordLength; *pSysBuf++ = pIoPortRemote->lineControl.Parity; *pSysBuf++ = pIoPortRemote->lineControl.StopBits; } } pIrp->IoStatus.Information = Information; if (optsAndBits & C0CE_INSERT_IOCTL_RXCLEAR) { PurgeBuffer(&pIoPortLocal->readBuf); UpdateHandFlow(pIoPortLocal, TRUE, &queueToComplete); if (pIoPortLocal->tryWrite || pIoPortRemote->tryWrite) { ReadWrite( pIoPortLocal, FALSE, pIoPortRemote, FALSE, &queueToComplete); } } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); FdoPortCompleteQueue(&queueToComplete); TraceIrp("FdoPortIoCtl", pIrp, &status, TRACE_FLAG_RESULTS); break; } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); break; } case IOCTL_SERIAL_SET_LINE_CONTROL: { PSERIAL_LINE_CONTROL pLineControl; if (pIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SERIAL_LINE_CONTROL)) { status = STATUS_BUFFER_TOO_SMALL; break; } pLineControl = (PSERIAL_LINE_CONTROL)pIrp->AssociatedIrp.SystemBuffer; switch (pLineControl->WordLength) { case 5: case 6: case 7: case 8: break; default: status = STATUS_INVALID_PARAMETER; } switch (pLineControl->Parity) { case NO_PARITY: case ODD_PARITY: case EVEN_PARITY: case MARK_PARITY: case SPACE_PARITY: break; default: status = STATUS_INVALID_PARAMETER; } switch (pLineControl->StopBits) { case STOP_BIT_1: case STOP_BITS_1_5: case STOP_BITS_2: break; default: status = STATUS_INVALID_PARAMETER; } if (status == STATUS_INVALID_PARAMETER) break; KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); if (pIoPortLocal->lineControl.StopBits != pLineControl->StopBits || pIoPortLocal->lineControl.Parity != pLineControl->Parity || pIoPortLocal->lineControl.WordLength != pLineControl->WordLength) { PC0C_IO_PORT pIoPortRemote; pIoPortLocal->lineControl = *pLineControl; SetWriteDelay(pIoPortLocal); pIoPortRemote = pIoPortLocal->pIoPortRemote; if (pIoPortRemote->escapeChar && (pIoPortRemote->insertMask & C0CE_INSERT_ENABLE_RLC)) { LIST_ENTRY queueToComplete; InitializeListHead(&queueToComplete); InsertRemoteLc(pIoPortRemote, &queueToComplete); if (pIoPortLocal->pIoPortRemote->tryWrite || pIoPortLocal->tryWrite) { ReadWrite( pIoPortLocal, FALSE, pIoPortLocal->pIoPortRemote, FALSE, &queueToComplete); } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); FdoPortCompleteQueue(&queueToComplete); break; } } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); break; } case IOCTL_SERIAL_GET_LINE_CONTROL: if (pIrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SERIAL_LINE_CONTROL)) { status = STATUS_BUFFER_TOO_SMALL; break; } KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); *(PSERIAL_LINE_CONTROL)pIrp->AssociatedIrp.SystemBuffer = pIoPortLocal->lineControl; KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); pIrp->IoStatus.Information = sizeof(SERIAL_LINE_CONTROL); TraceIrp("FdoPortIoCtl", pIrp, &status, TRACE_FLAG_RESULTS); break; case IOCTL_SERIAL_SET_BAUD_RATE: { PSERIAL_BAUD_RATE pBaudRate; if (pIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SERIAL_BAUD_RATE)) { status = STATUS_BUFFER_TOO_SMALL; break; } pBaudRate = (PSERIAL_BAUD_RATE)pIrp->AssociatedIrp.SystemBuffer; KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); if (pIoPortLocal->baudRate.BaudRate != pBaudRate->BaudRate) { PC0C_IO_PORT pIoPortRemote; pIoPortLocal->baudRate = *pBaudRate; SetWriteDelay(pIoPortLocal); pIoPortRemote = pIoPortLocal->pIoPortRemote; if (pIoPortRemote->escapeChar && (pIoPortRemote->insertMask & C0CE_INSERT_ENABLE_RBR)) { LIST_ENTRY queueToComplete; InitializeListHead(&queueToComplete); InsertRemoteBr(pIoPortRemote, &queueToComplete); if (pIoPortLocal->pIoPortRemote->tryWrite || pIoPortLocal->tryWrite) { ReadWrite( pIoPortLocal, FALSE, pIoPortLocal->pIoPortRemote, FALSE, &queueToComplete); } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); FdoPortCompleteQueue(&queueToComplete); break; } } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); break; } case IOCTL_SERIAL_GET_BAUD_RATE: if (pIrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SERIAL_BAUD_RATE)) { status = STATUS_BUFFER_TOO_SMALL; break; } KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); *(PSERIAL_BAUD_RATE)pIrp->AssociatedIrp.SystemBuffer = pIoPortLocal->baudRate; KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); pIrp->IoStatus.Information = sizeof(SERIAL_BAUD_RATE); TraceIrp("FdoPortIoCtl", pIrp, &status, TRACE_FLAG_RESULTS); break; case IOCTL_SERIAL_GET_PROPERTIES: { ULONG size; status = GetCommProp(pDevExt, pIrp->AssociatedIrp.SystemBuffer, pIrpStack->Parameters.DeviceIoControl.OutputBufferLength, &size); if (status == STATUS_SUCCESS) { pIrp->IoStatus.Information = size; TraceIrp("FdoPortIoCtl", pIrp, &status, TRACE_FLAG_RESULTS); } break; } case IOCTL_SERIAL_CONFIG_SIZE: if (pIrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG)) { status = STATUS_BUFFER_TOO_SMALL; break; } pIrp->IoStatus.Information = sizeof(ULONG); *(PULONG)pIrp->AssociatedIrp.SystemBuffer = 0; break; case IOCTL_SERIAL_SET_QUEUE_SIZE: { PSERIAL_QUEUE_SIZE pSysBuf = (PSERIAL_QUEUE_SIZE)pIrp->AssociatedIrp.SystemBuffer; LIST_ENTRY queueToComplete; PC0C_BUFFER pReadBuf; PUCHAR pBase; if (pIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SERIAL_QUEUE_SIZE)) { status = STATUS_BUFFER_TOO_SMALL; break; } pReadBuf = &pIoPortLocal->readBuf; KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); if (pSysBuf->InSize <= C0C_BUFFER_SIZE(pReadBuf)) { KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); break; } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); try { pBase = C0C_ALLOCATE_POOL_WITH_QUOTA(NonPagedPool, pSysBuf->InSize); } except (EXCEPTION_EXECUTE_HANDLER) { pBase = NULL; status = GetExceptionCode(); } if (!pBase) break; InitializeListHead(&queueToComplete); KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); if (SetNewBufferBase(pReadBuf, pBase, pSysBuf->InSize)) { pIoPortLocal->handFlow.XoffLimit = pSysBuf->InSize >> 3; pIoPortLocal->handFlow.XonLimit = pSysBuf->InSize >> 1; SetLimit(pIoPortLocal); UpdateHandFlow(pIoPortLocal, TRUE, &queueToComplete); if (pIoPortLocal->tryWrite || pIoPortLocal->pIoPortRemote->tryWrite) { ReadWrite( pIoPortLocal, FALSE, pIoPortLocal->pIoPortRemote, FALSE, &queueToComplete); } } KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); FdoPortCompleteQueue(&queueToComplete); break; } case IOCTL_SERIAL_GET_STATS: if (pIrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SERIALPERF_STATS)) { status = STATUS_BUFFER_TOO_SMALL; break; } KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); *(PSERIALPERF_STATS)pIrp->AssociatedIrp.SystemBuffer = pIoPortLocal->perfStats; KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); pIrp->IoStatus.Information = sizeof(SERIALPERF_STATS); TraceIrp("FdoPortIoCtl", pIrp, &status, TRACE_FLAG_RESULTS); break; case IOCTL_SERIAL_CLEAR_STATS: KeAcquireSpinLock(pIoPortLocal->pIoLock, &oldIrql); RtlZeroMemory(&pIoPortLocal->perfStats, sizeof(pIoPortLocal->perfStats)); KeReleaseSpinLock(pIoPortLocal->pIoLock, oldIrql); break; default: status = STATUS_INVALID_PARAMETER; } }
NTSTATUS FdoPortOpen(IN PC0C_FDOPORT_EXTENSION pDevExt) { LIST_ENTRY queueToComplete; PUCHAR pBase; ULONG size; KIRQL oldIrql; PC0C_IO_PORT pIoPort; if (InterlockedIncrement(&pDevExt->openCount) != 1) { InterlockedDecrement(&pDevExt->openCount); return STATUS_ACCESS_DENIED; } pIoPort = pDevExt->pIoPortLocal; if (pIoPort->plugInMode && !pIoPort->pIoPortRemote->isOpen) { InterlockedDecrement(&pDevExt->openCount); return STATUS_ACCESS_DENIED; } if (pIoPort->exclusiveMode) IoInvalidateDeviceRelations(pIoPort->pPhDevObj, BusRelations); switch (MmQuerySystemSize()) { case MmLargeSystem: size = 4096; pBase = (PUCHAR)C0C_ALLOCATE_POOL(NonPagedPool, size); if (pBase) break; case MmMediumSystem: size = 1024; pBase = (PUCHAR)C0C_ALLOCATE_POOL(NonPagedPool, size); if (pBase) break; case MmSmallSystem: size = 128; pBase = (PUCHAR)C0C_ALLOCATE_POOL(NonPagedPool, size); if (pBase) break; default: size = 0; pBase = NULL; } InitializeListHead(&queueToComplete); #if ENABLE_TRACING if (pIoPort->amountInWriteQueue) { NTSTATUS status; UNICODE_STRING msg; status = STATUS_SUCCESS; RtlInitUnicodeString(&msg, NULL); StrAppendStr0(&status, &msg, L"!!!WARNING!!! amountInWriteQueue = "); StrAppendNum(&status, &msg, pIoPort->amountInWriteQueue, 10); Trace0((PC0C_COMMON_EXTENSION)pDevExt, msg.Buffer); StrFree(&msg); } #endif /* ENABLE_TRACING */ KeAcquireSpinLock(pIoPort->pIoLock, &oldIrql); InitBuffer(&pIoPort->readBuf, pBase, size); pIoPort->amountInWriteQueue = 0; pIoPort->tryWrite = FALSE; pIoPort->errors = 0; pIoPort->waitMask = 0; pIoPort->eventMask = 0; RtlZeroMemory(&pIoPort->perfStats, sizeof(pIoPort->perfStats)); pIoPort->handFlow.XoffLimit = size >> 3; pIoPort->handFlow.XonLimit = size >> 1; pIoPort->pIoPortRemote->brokeIdleChars = 0; SetHandFlow(pIoPort, NULL, &queueToComplete); SetModemControl(pIoPort, C0C_MCR_OPEN, C0C_MCR_OPEN, &queueToComplete); if (pIoPort->pIoPortRemote->pWriteDelay && pIoPort->pIoPortRemote->brokeCharsProbability > 0) StartWriteDelayTimer(pIoPort->pIoPortRemote->pWriteDelay); KeReleaseSpinLock(pIoPort->pIoLock, oldIrql); pIoPort->isOpen = TRUE; if (pIoPort->pIoPortRemote->plugInMode) IoInvalidateDeviceRelations(pIoPort->pIoPortRemote->pPhDevObj, BusRelations); FdoPortCompleteQueue(&queueToComplete); return STATUS_SUCCESS; }
NTSTATUS SetHandFlow( PC0C_IO_PORT pIoPort, PSERIAL_HANDFLOW pHandFlow, PLIST_ENTRY pQueueToComplete) { UCHAR bits, mask; PC0C_BUFFER pReadBuf; PSERIAL_HANDFLOW pNewHandFlow; BOOLEAN setModemStatusHolding; pReadBuf = &pIoPort->readBuf; if (pHandFlow) { if ((pIoPort->escapeChar && (pHandFlow->FlowReplace & SERIAL_ERROR_CHAR)) || ((SIZE_T)pHandFlow->XonLimit > C0C_BUFFER_SIZE(pReadBuf)) || ((SIZE_T)pHandFlow->XoffLimit > C0C_BUFFER_SIZE(pReadBuf))) { return STATUS_INVALID_PARAMETER; } pNewHandFlow = pHandFlow; } else { pNewHandFlow = &pIoPort->handFlow; } // Set local side if (!pHandFlow) pIoPort->writeHolding &= ~SERIAL_TX_WAITING_FOR_XON; if (pHandFlow && ((pIoPort->handFlow.FlowReplace & SERIAL_AUTO_TRANSMIT) != 0) && ((pHandFlow->FlowReplace & SERIAL_AUTO_TRANSMIT) == 0)) { SetXonXoffHolding(pIoPort, C0C_XCHAR_ON); } if (!pHandFlow || (pIoPort->handFlow.ControlHandShake & SERIAL_OUT_HANDSHAKEMASK) != (pHandFlow->ControlHandShake & SERIAL_OUT_HANDSHAKEMASK)) { setModemStatusHolding = TRUE; } else { setModemStatusHolding = FALSE; } // Set remote side bits = mask = 0; if (!pHandFlow || (pIoPort->handFlow.FlowReplace & SERIAL_RTS_MASK) != (pHandFlow->FlowReplace & SERIAL_RTS_MASK)) { switch (pNewHandFlow->FlowReplace & SERIAL_RTS_MASK) { case 0: pIoPort->writeHoldingRemote &= ~SERIAL_TX_WAITING_FOR_CTS; mask |= C0C_MCR_RTS; break; case SERIAL_RTS_CONTROL: pIoPort->writeHoldingRemote &= ~SERIAL_TX_WAITING_FOR_CTS; bits |= C0C_MCR_RTS; mask |= C0C_MCR_RTS; break; case SERIAL_RTS_HANDSHAKE: if (C0C_BUFFER_BUSY(pReadBuf) > (C0C_BUFFER_SIZE(pReadBuf) - pNewHandFlow->XoffLimit)) { pIoPort->writeHoldingRemote |= SERIAL_TX_WAITING_FOR_CTS; mask |= C0C_MCR_RTS; } else if (pIoPort->writeHoldingRemote & SERIAL_TX_WAITING_FOR_CTS) { if (C0C_BUFFER_BUSY(pReadBuf) <= (SIZE_T)pNewHandFlow->XonLimit) { pIoPort->writeHoldingRemote &= ~SERIAL_TX_WAITING_FOR_CTS; bits |= C0C_MCR_RTS; mask |= C0C_MCR_RTS; } } else { bits |= C0C_MCR_RTS; mask |= C0C_MCR_RTS; } } } if (!pHandFlow || (pIoPort->handFlow.ControlHandShake & SERIAL_DTR_MASK) != (pHandFlow->ControlHandShake & SERIAL_DTR_MASK)) { switch (pNewHandFlow->ControlHandShake & SERIAL_DTR_MASK) { case 0: pIoPort->writeHoldingRemote &= ~SERIAL_TX_WAITING_FOR_DSR; mask |= C0C_MCR_DTR; break; case SERIAL_DTR_CONTROL: pIoPort->writeHoldingRemote &= ~SERIAL_TX_WAITING_FOR_DSR; bits |= C0C_MCR_DTR; mask |= C0C_MCR_DTR; break; case SERIAL_DTR_HANDSHAKE: if (C0C_BUFFER_BUSY(pReadBuf) > (C0C_BUFFER_SIZE(pReadBuf) - pNewHandFlow->XoffLimit)) { pIoPort->writeHoldingRemote |= SERIAL_TX_WAITING_FOR_DSR; mask |= C0C_MCR_DTR; } else if (pIoPort->writeHoldingRemote & SERIAL_TX_WAITING_FOR_DSR) { if (C0C_BUFFER_BUSY(pReadBuf) <= (SIZE_T)pNewHandFlow->XonLimit) { pIoPort->writeHoldingRemote &= ~SERIAL_TX_WAITING_FOR_DSR; bits |= C0C_MCR_DTR; mask |= C0C_MCR_DTR; } } else { bits |= C0C_MCR_DTR; mask |= C0C_MCR_DTR; } } } if (!pHandFlow || (pIoPort->handFlow.FlowReplace & SERIAL_AUTO_RECEIVE) != (pHandFlow->FlowReplace & SERIAL_AUTO_RECEIVE)) { if (pNewHandFlow->FlowReplace & SERIAL_AUTO_RECEIVE) { if (C0C_BUFFER_BUSY(pReadBuf) > (C0C_BUFFER_SIZE(pReadBuf) - pNewHandFlow->XoffLimit)) { pIoPort->writeHoldingRemote |= SERIAL_TX_WAITING_FOR_XON; if ((pNewHandFlow->FlowReplace & SERIAL_XOFF_CONTINUE) == 0) pIoPort->writeHolding |= SERIAL_TX_WAITING_FOR_XON; pIoPort->sendXonXoff = C0C_XCHAR_OFF; pIoPort->tryWrite = TRUE; } } else if (pIoPort->writeHoldingRemote & SERIAL_TX_WAITING_FOR_XON) { pIoPort->writeHoldingRemote &= ~SERIAL_TX_WAITING_FOR_XON; pIoPort->writeHolding &= ~SERIAL_TX_WAITING_FOR_XON; if (pIoPort->sendXonXoff != C0C_XCHAR_OFF) { // XOFF was sent so send XON pIoPort->sendXonXoff = C0C_XCHAR_ON; pIoPort->tryWrite = TRUE; } else { // XOFF still was not sent so cancel it pIoPort->sendXonXoff = 0; } } } if (pHandFlow) pIoPort->handFlow = *pHandFlow; if (setModemStatusHolding) SetModemStatusHolding(pIoPort); if (mask) SetModemControl(pIoPort, bits, mask, pQueueToComplete); UpdateTransmitToggle(pIoPort, pQueueToComplete); SetLimit(pIoPort->pIoPortRemote); SetLimit(pIoPort); if (pIoPort->pIoPortRemote->tryWrite) { ReadWrite( pIoPort, FALSE, pIoPort->pIoPortRemote, FALSE, pQueueToComplete); } if (pIoPort->tryWrite) { ReadWrite( pIoPort->pIoPortRemote, FALSE, pIoPort, FALSE, pQueueToComplete); } return STATUS_SUCCESS; }
VOID UpdateHandFlow( PC0C_IO_PORT pIoPort, BOOLEAN freed, PLIST_ENTRY pQueueToComplete) { UCHAR bits, mask; PC0C_BUFFER pReadBuf; PSERIAL_HANDFLOW pHandFlowLocal, pHandFlowRemote; pHandFlowLocal = &pIoPort->handFlow; pHandFlowRemote = &pIoPort->pIoPortRemote->handFlow; pReadBuf = &pIoPort->readBuf; bits = mask = 0; if (!pIoPort->writeHoldingRemote) { if (!freed && C0C_BUFFER_BUSY(pReadBuf) > (C0C_BUFFER_SIZE(pReadBuf) - pHandFlowLocal->XoffLimit)) { if ((pHandFlowLocal->FlowReplace & SERIAL_RTS_MASK) == SERIAL_RTS_HANDSHAKE) { pIoPort->writeHoldingRemote |= SERIAL_TX_WAITING_FOR_CTS; mask |= C0C_MCR_RTS; } if ((pHandFlowLocal->ControlHandShake & SERIAL_DTR_MASK) == SERIAL_DTR_HANDSHAKE) { pIoPort->writeHoldingRemote |= SERIAL_TX_WAITING_FOR_DSR; mask |= C0C_MCR_DTR; } if (pHandFlowLocal->FlowReplace & SERIAL_AUTO_RECEIVE) { pIoPort->writeHoldingRemote |= SERIAL_TX_WAITING_FOR_XON; if ((pHandFlowLocal->FlowReplace & SERIAL_XOFF_CONTINUE) == 0) pIoPort->writeHolding |= SERIAL_TX_WAITING_FOR_XON; pIoPort->sendXonXoff = C0C_XCHAR_OFF; pIoPort->tryWrite = TRUE; } } } else { if (freed && C0C_BUFFER_BUSY(pReadBuf) <= (SIZE_T)pHandFlowLocal->XonLimit) { if ((pHandFlowLocal->FlowReplace & SERIAL_RTS_MASK) == SERIAL_RTS_HANDSHAKE) { pIoPort->writeHoldingRemote &= ~SERIAL_TX_WAITING_FOR_CTS; bits |= C0C_MCR_RTS; mask |= C0C_MCR_RTS; } if ((pHandFlowLocal->ControlHandShake & SERIAL_DTR_MASK) == SERIAL_DTR_HANDSHAKE) { pIoPort->writeHoldingRemote &= ~SERIAL_TX_WAITING_FOR_DSR; bits |= C0C_MCR_DTR; mask |= C0C_MCR_DTR; } if (pHandFlowLocal->FlowReplace & SERIAL_AUTO_RECEIVE) { pIoPort->writeHoldingRemote &= ~SERIAL_TX_WAITING_FOR_XON; pIoPort->writeHolding &= ~SERIAL_TX_WAITING_FOR_XON; pIoPort->sendXonXoff = C0C_XCHAR_ON; pIoPort->tryWrite = TRUE; } } } if (mask) SetModemControl(pIoPort, bits, mask, pQueueToComplete); }