BOOLEAN SerialSetLineControl( IN PVOID Context ) /*++ Routine Description: This routine is used to set the buad rate of the device. Arguments: Context - Pointer to the device extension. Return Value: This routine always returns FALSE. --*/ { PSERIAL_DEVICE_EXTENSION Extension = Context; WRITE_LINE_CONTROL( Extension->Controller, Extension->LineControl ); return FALSE; }
BOOLEAN SerialTurnOnBreak( IN PVOID Context ) /*++ Routine Description: This routine will turn on break in the hardware and record the fact the break is on, in the extension variable that holds reasons that transmission is stopped. Arguments: Context - Really a pointer to the device extension. Return Value: This routine always returns FALSE. --*/ { PSERIAL_DEVICE_EXTENSION Extension = Context; UCHAR OldLineControl; if ((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK) == SERIAL_TRANSMIT_TOGGLE) { SerialSetRTS(Extension); } OldLineControl = READ_LINE_CONTROL(Extension->Controller); OldLineControl |= SERIAL_LCR_BREAK; WRITE_LINE_CONTROL( Extension->Controller, OldLineControl ); Extension->TXHolding |= SERIAL_TX_BREAK; return FALSE; }
/////////////////////////////////////////////////////////////////////////////// // Does : Initializes the device. // Parameters : port - Port of a 8250 // baudrate - Baudrate to init 8250 to // dataBits - Number of data bits (5..8) // stopBits - Number of stop bits (1..2) // parity - 0 == NONE, 1 == ODD, 2 == EVEN // Returns : Status of the operation. // Postcondition : Ready to start interrupt processing when returning // STATUS_SUCCESS. /////////////////////////////////////////////////////////////////////////////// NTSTATUS Hw8250Init(PUCHAR port, ULONG baudRate, ULONG dataBits, ULONG stopBits, ULONG parity) { NTSTATUS result = STATUS_SUCCESS; ULONG divisor; UCHAR dataFormat; // disable all interrupts WRITE_INTERRUPT_ENABLE(port, 0x00); // set up baudrate divisor = Hw8550InternalGetDivisor(baudRate); WRITE_LINE_CONTROL(port, LCR_DLAB); WRITE_DIVISOR_LATCH(port, divisor); // set up data bits (defaults to 8 data bits) switch(dataBits) { case 5: dataFormat = LCR_DATA_5; break; case 6: dataFormat = LCR_DATA_6; break; case 7: dataFormat = LCR_DATA_7; break; case 8: default: dataFormat = LCR_DATA_8; break; } // set up stop bits (defaults to 1 stop bit) if(stopBits == 2) { dataFormat |= LCR_STOP_2; } else { dataFormat |= LCR_STOP_1; } // set up parity (defaults to none) switch(parity) { case 4: dataFormat |= LCR_PARITY_SPACE; break; case 3: dataFormat |= LCR_PARITY_MARK; break; case 2: dataFormat |= LCR_PARITY_EVEN; break; case 1: dataFormat |= LCR_PARITY_ODD; break; case 0: default: dataFormat |= LCR_PARITY_NONE; break; } WRITE_LINE_CONTROL(port, dataFormat); WRITE_MODEM_CONTROL(port, MCR_GPO1 | MCR_RTS | MCR_DTR); return result; } // end Hw8250Init
NTSTATUS SerialEvtDeviceD0Entry( IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE PreviousState ) /*++ Routine Description: EvtDeviceD0Entry event callback must perform any operations that are necessary before the specified device is used. It will be called every time the hardware needs to be (re-)initialized. This includes after IRP_MN_START_DEVICE, IRP_MN_CANCEL_STOP_DEVICE, IRP_MN_CANCEL_REMOVE_DEVICE, IRP_MN_SET_POWER-D0. This function is not marked pageable because this function is in the device power up path. When a function is marked pagable and the code section is paged out, it will generate a page fault which could impact the fast resume behavior because the client driver will have to wait until the system drivers can service this page fault. This function runs at PASSIVE_LEVEL, even though it is not paged. A driver can optionally make this function pageable if DO_POWER_PAGABLE is set. Even if DO_POWER_PAGABLE isn't set, this function still runs at PASSIVE_LEVEL. In this case, though, the function absolutely must not do anything that will cause a page fault. Arguments: Device - Handle to a framework device object. PreviousState - Device power state which the device was in most recently. If the device is being newly started, this will be PowerDeviceUnspecified. Return Value: NTSTATUS --*/ { PSERIAL_DEVICE_EXTENSION deviceExtension; PSERIAL_DEVICE_STATE pDevState; SHORT divisor; SERIAL_IOCTL_SYNC S; SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER, "-->SerialEvtDeviceD0Entry - coming from %s\n", DbgDevicePowerString(PreviousState)); deviceExtension = SerialGetDeviceExtension (Device); pDevState = &deviceExtension->DeviceState; // // Restore the state of the UART. First, that involves disabling // interrupts both via OUT2 and IER. // WRITE_MODEM_CONTROL(deviceExtension, deviceExtension->Controller, 0); DISABLE_ALL_INTERRUPTS(deviceExtension, deviceExtension->Controller); // // Set the baud rate // SerialGetDivisorFromBaud(deviceExtension->ClockRate, deviceExtension->CurrentBaud, &divisor); S.Extension = deviceExtension; S.Data = (PVOID) (ULONG_PTR) divisor; #pragma prefast(suppress: __WARNING_INFERRED_IRQ_TOO_LOW, "PFD warning that we are calling interrupt synchronize routine directly. Suppress it because interrupt is disabled above.") SerialSetBaud(deviceExtension->WdfInterrupt, &S); // // Reset / Re-enable the FIFO's // if (deviceExtension->FifoPresent) { WRITE_FIFO_CONTROL(deviceExtension, deviceExtension->Controller, (UCHAR)0); READ_RECEIVE_BUFFER(deviceExtension, deviceExtension->Controller); WRITE_FIFO_CONTROL(deviceExtension, deviceExtension->Controller, (UCHAR)(SERIAL_FCR_ENABLE | deviceExtension->RxFifoTrigger | SERIAL_FCR_RCVR_RESET | SERIAL_FCR_TXMT_RESET)); } else { WRITE_FIFO_CONTROL(deviceExtension, deviceExtension->Controller, (UCHAR)0); } // // Restore a couple more registers // WRITE_INTERRUPT_ENABLE(deviceExtension, deviceExtension->Controller, pDevState->IER); WRITE_LINE_CONTROL(deviceExtension, deviceExtension->Controller, pDevState->LCR); // // Clear out any stale interrupts // READ_INTERRUPT_ID_REG(deviceExtension, deviceExtension->Controller); READ_LINE_STATUS(deviceExtension, deviceExtension->Controller); READ_MODEM_STATUS(deviceExtension, deviceExtension->Controller); // // TODO: move this code to EvtInterruptEnable. // if (deviceExtension->DeviceState.Reopen == TRUE) { SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER, "Reopening device\n"); SetDeviceIsOpened(deviceExtension, TRUE, FALSE); // // This enables interrupts on the device! // WRITE_MODEM_CONTROL(deviceExtension, deviceExtension->Controller, (UCHAR)(pDevState->MCR | SERIAL_MCR_OUT2)); // // Refire the state machine // DISABLE_ALL_INTERRUPTS(deviceExtension, deviceExtension->Controller); ENABLE_ALL_INTERRUPTS(deviceExtension, deviceExtension->Controller); } SerialDbgPrintEx(TRACE_LEVEL_INFORMATION, DBG_POWER, "<--SerialEvtDeviceD0Entry\n"); return STATUS_SUCCESS; }
BOOLEAN SerialTurnOffBreak( IN PVOID Context ) /*++ Routine Description: This routine will turn off break in the hardware and record the fact the break is off, in the extension variable that holds reasons that transmission is stopped. Arguments: Context - Really a pointer to the device extension. Return Value: This routine always returns FALSE. --*/ { PSERIAL_DEVICE_EXTENSION Extension = Context; UCHAR OldLineControl; if (Extension->TXHolding & SERIAL_TX_BREAK) { // // We actually have a good reason for testing if transmission // is holding instead of blindly clearing the bit. // // If transmission actually was holding and the result of // clearing the bit is that we should restart transmission // then we will poke the interrupt enable bit, which will // cause an actual interrupt and transmission will then // restart on its own. // // If transmission wasn't holding and we poked the bit // then we would interrupt before a character actually made // it out and we could end up over writing a character in // the transmission hardware. OldLineControl = READ_LINE_CONTROL(Extension->Controller); OldLineControl &= ~SERIAL_LCR_BREAK; WRITE_LINE_CONTROL( Extension->Controller, OldLineControl ); Extension->TXHolding &= ~SERIAL_TX_BREAK; if (!Extension->TXHolding && (Extension->TransmitImmediate || Extension->WriteLength) && Extension->HoldingEmpty) { DISABLE_ALL_INTERRUPTS(Extension->Controller); ENABLE_ALL_INTERRUPTS(Extension->Controller); } else { // // The following routine will lower the rts if we // are doing transmit toggleing and there is no // reason to keep it up. // Extension->CountOfTryingToLowerRTS++; SerialPerhapsLowerRTS(Extension); } } return FALSE; }
// // Internal Function: UartCtlSetLineControl // VOID UartCtlSetLineControl( _In_ WDFDEVICE Device, _In_ WDFREQUEST Request, _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength ) { NTSTATUS status = STATUS_UNSUCCESSFUL; PUART_DEVICE_EXTENSION pDevExt = UartGetDeviceExtension(Device); UCHAR lineControlRegister = 0; PSERIAL_LINE_CONTROL pLineControl = NULL; UNREFERENCED_PARAMETER(OutputBufferLength); UNREFERENCED_PARAMETER(InputBufferLength); FuncEntry(TRACE_FLAG_CONTROL); status = WdfRequestRetrieveInputBuffer(Request, sizeof(SERIAL_LINE_CONTROL), (PVOID*)(& pLineControl), NULL); if (!NT_SUCCESS(status)) { TraceMessage( TRACE_LEVEL_ERROR, TRACE_FLAG_CONTROL, "Failed to retrieve input buffer for WDFREQUEST %p - " "%!STATUS!", Request, status); } if (NT_SUCCESS(status)) { status = UartRegStructToLCR(pLineControl, &lineControlRegister); if (!NT_SUCCESS(status)) { TraceMessage( TRACE_LEVEL_ERROR, TRACE_FLAG_CONTROL, "Failed to calculate LCR from SERIAL_LINE_CONTROL %p - " "%!STATUS!", pLineControl, status); } } if (NT_SUCCESS(status)) { WdfInterruptAcquireLock(pDevExt->WdfInterrupt); // Set line control, save break setting lineControlRegister = lineControlRegister | (READ_LINE_CONTROL(pDevExt, pDevExt->Controller) & SERIAL_LCR_BREAK); WRITE_LINE_CONTROL(pDevExt, pDevExt->Controller, lineControlRegister); WdfInterruptReleaseLock(pDevExt->WdfInterrupt); } TraceMessage(TRACE_LEVEL_INFORMATION, TRACE_FLAG_CONTROL, "WdfRequestComplete( %!HANDLE! => %!STATUS! )", Request, status); WdfRequestComplete(Request, status); FuncExit(TRACE_FLAG_CONTROL); }