Esempio n. 1
0
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;
}
Esempio n. 2
0
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;
        }
    }
Esempio n. 3
0
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);
}