/*++ Copyright (c) 2014 Minoca Corp. All Rights Reserved Module Name: ser16550.c Abstract: This module implements a kernel driver for 16550-like UARTs. Author: Evan Green 21-Nov-2014 Environment: Kernel --*/ // // ------------------------------------------------------------------- Includes // #include #include #include // // --------------------------------------------------------------------- Macros // // // Macros to read from and write to 16550 registers. // #define SER16550_READ8(_Device, _Register) \ (_Device)->Read8((_Device), (_Register)) #define SER16550_WRITE8(_Device, _Register, _Value) \ (_Device)->Write8((_Device), (_Register), (_Value)) // // This macro returns the offset of a given register from its base. // #define SER16550_REGISTER_OFFSET(_Device, _Register) \ ((_Device)->RegisterOffset + ((_Register) << (_Device)->RegisterShift)) // // This macro evaluates to non-zero if the given Oxford device ID has two // UARTs. This matches against the Mode[2:0] bits being 101 and the UART_EN bit // being set. // #define SER16550_OXFORD_DUAL_UARTS(_DeviceId) \ (((_DeviceId) & 0x0078) == 0x0058) // // ---------------------------------------------------------------- Definitions // #define SER16550_ALLOCATION_TAG 0x36317253 #define SER16550_DEFAULT_BUFFER_SIZE 2048 #define SER16550_DEFAULT_BASE_BAUD 115200 #define SER16550A_FIFO_SIZE 16 #define SER16550_MAX_FIFO 256 #define SERIAL_PORT_DEVICE_ID_FORMAT "Serial%d" #define SERIAL_PORT_DEVICE_ID_SIZE 50 // // Standard 16x50 register definitions. // #define SER16550_LINE_CONTROL_5_DATA_BITS 0x00 #define SER16550_LINE_CONTROL_6_DATA_BITS 0x01 #define SER16550_LINE_CONTROL_7_DATA_BITS 0x02 #define SER16550_LINE_CONTROL_8_DATA_BITS 0x03 #define SER16550_LINE_CONTROL_2_STOP_BITS 0x04 #define SER16550_LINE_CONTROL_PARITY_ENABLE 0x08 #define SER16550_LINE_CONTROL_EVEN_PARITY 0x10 #define SER16550_LINE_CONTROL_SET_PARITY 0x20 #define SER16550_LINE_CONTROL_SET_BREAK 0x40 #define SER16550_LINE_CONTROL_DIVISOR_LATCH 0x80 #define SER16550_FIFO_CONTROL_ENABLE 0x01 #define SER16550_FIFO_CONTROL_CLEAR_RECEIVE 0x02 #define SER16550_FIFO_CONTROL_CLEAR_TRANSMIT 0x04 #define SER16550_FIFO_CONTROL_MULTI_DMA 0x08 #define SER16550_FIFO_CONTROL_64_BYTE_FIFO 0x20 #define SER16550_FIFO_CONTROL_RX_TRIGGER_1 (0 << 6) #define SER16550_FIFO_CONTROL_RX_TRIGGER_4 (1 << 6) #define SER16550_FIFO_CONTROL_RX_TRIGGER_8 (2 << 6) #define SER16550_FIFO_CONTROL_RX_TRIGGER_14 (3 << 6) #define SER16550_MODEM_CONTROL_DTR 0x01 #define SER16550_MODEM_CONTROL_RTS 0x02 #define SER16550_MODEM_CONTROL_OP1 0x04 #define SER16550_MODEM_CONTROL_ENABLE_INTERRUPT 0x08 #define SER16550_MODEM_CONTROL_LOOPBACK 0x10 #define SER16550_MODEM_CONTROL_ENABLE_FLOW_CONTROL 0x20 #define SER16550_INTERRUPT_ENABLE_RX_DATA 0x01 #define SER16550_INTERRUPT_ENABLE_TX_EMPTY 0x02 #define SER16550_INTERRUPT_ENABLE_RX_STATUS 0x04 #define SER16550_INTERRUPT_ENABLE_MODEM_STATUS 0x08 #define SER16550_INTERRUPT_STATUS_NONE_PENDING 0x01 #define SER16550_INTERRUPT_STATUS_RX_DATA_ERROR 0x06 #define SER16550_INTERRUPT_STATUS_RX_DATA_READY 0x04 #define SER16550_INTERRUPT_STATUS_RX_TIMEOUT 0x0C #define SER16550_INTERRUPT_STATUS_TX_EMPTY 0x02 #define SER16550_INTERRUPT_STATUS_MODEM_STATUS 0x00 #define SER16550_INTERRUPT_STATUS_MASK 0x0E #define SER16550_LINE_STATUS_RX_READY 0x01 #define SER16550_LINE_STATUS_OVERRUN_ERROR 0x02 #define SER16550_LINE_STATUS_PARITY_ERROR 0x04 #define SER16550_LINE_STATUS_FRAMING_ERROR 0x08 #define SER16550_LINE_STATUS_BREAK 0x10 #define SER16550_LINE_STATUS_TX_HOLDING_EMPTY 0x20 #define SER16550_LINE_STATUS_TX_EMPTY 0x40 #define SER16550_LINE_STATUS_FIFO_ERROR 0x80 #define SER16550_LINE_STATUS_ERROR_MASK \ (SER16550_LINE_STATUS_OVERRUN_ERROR | SER16550_LINE_STATUS_PARITY_ERROR | \ SER16550_LINE_STATUS_FRAMING_ERROR | SER16550_LINE_STATUS_FIFO_ERROR) #define SER16550_LINE_STATUS_RX_MASK \ (SER16550_LINE_STATUS_RX_READY | SER16550_LINE_STATUS_BREAK) // // Known vendors and devices. // #define SER16550_PCI_DEVICE_ID_FORMAT "VEN_%x&DEV_%x" #define SER16550_VENDOR_INTEL 0x8086 #define SER16550_INTEL_QUARK 0x0936 #define SER16550_VENDOR_OXFORD 0x1415 #define SER16550_OXFORD_UART_OFFSET 0x1000 #define SER16550_OXFORD_UART_STRIDE 0x200 #define SER16550_OXFORD_BASE_BAUD 3916800 // // Intel Quark UART information. // #define SER16550_INTEL_QUARK_UART_BASE_BAUD 2764800 #define SER16550_INTEL_QUARK_UART_REGISTER_SHIFT 2 // // ------------------------------------------------------ Data Type Definitions // typedef enum _SER16550_OBJECT_TYPE { Ser16550ObjectInvalid, Ser16550ObjectParent, Ser16550ObjectChild } SER16550_OBJECT_TYPE, *PSER16550_OBJECT_TYPE; typedef enum _SER16550_VARIANT { Ser16550VariantInvalid, Ser16550VariantGeneric, Ser16550VariantQuark, Ser16550VariantOxford, } SER16550_VARIANT, *PSER16550_VARIANT; typedef enum _SER16550_REGISTER { Ser16550Data = 0, Ser16550DivisorLow = 0, Ser16550InterruptEnable = 1, Ser16550DivisorHigh = 1, Ser16550InterruptStatus = 2, Ser16550FifoControl = 2, Ser16550LineControl = 3, Ser16550ModemControl = 4, Ser16550LineStatus = 5, Ser16550ModemStatus = 6, Ser16550Scratch = 7 } SER16550_REGISTER, *PSER16550_REGISTER; typedef struct _SER16550_CHILD SER16550_CHILD, *PSER16550_CHILD; typedef UCHAR (*PSER16550_READ8) ( PSER16550_CHILD Device, SER16550_REGISTER Register ); /*++ Routine Description: This routine reads a 16550 register. Arguments: Device - Supplies a pointer to the device context. Register - Supplies the register to read. Return Value: Returns the value at the register. --*/ typedef VOID (*PSER16550_WRITE8) ( PSER16550_CHILD Device, SER16550_REGISTER Register, UCHAR Value ); /*++ Routine Description: This routine writes to a 16550 register. Arguments: Device - Supplies a pointer to the device context. Register - Supplies the register to write to. Value - Supplies the value to write. Return Value: None. --*/ /*++ Structure Description: This structure stores the common header for a 16550 object. Members: Type - Stores the serial object type. ReferenceCount - Stores the reference count on the object. --*/ typedef struct _SER16550_OBJECT { SER16550_OBJECT_TYPE Type; ULONG ReferenceCount; } SER16550_OBJECT, *PSER16550_OBJECT; /*++ Structure Description: This structure stores information about a 16550 parent context. Members: Header - Store the common 16550 object header. Device - Stores the OS device this device belongs to. InterruptLine - Stores the interrupt line that this controller's interrupt comes in on. InterruptVector - Stores the interrupt vector that this controller's interrupt comes in on. InterruptResourcesFound - Stores a boolean indicating whether or not the interrupt line and interrupt vector fields are valid. InterruptHandle - Stores a pointer to the handle received when the interrupt was connected. InterruptLock - Stores the spin lock synchronizing access to the pending status bits. Variant - Stores the variant type of 16550 controller. VendorId - Stores the PCI vendor ID of the device, or 0 if this was not a PCI device. DeviceId - Stores the PCI device ID of thd device. ChildObjects - Stores an array of child objects. ChildDevices - Stores an array of child devices. ChildCount - Stores the count of the number of children. RegisterShift - Stores the number of bits to shift left to get from a 16550 register to an actual device register. By default this is applied to all children. BaseBaud - Stores the baud rate when the divisor is set to 1. Removed - Stores a boolean indicating that the device has been removed. --*/ typedef struct _SER16550_PARENT { SER16550_OBJECT Header; PDEVICE Device; ULONGLONG InterruptLine; ULONGLONG InterruptVector; BOOL InterruptResourcesFound; HANDLE InterruptHandle; KSPIN_LOCK InterruptLock; SER16550_VARIANT Variant; USHORT VendorId; USHORT DeviceId; PSER16550_CHILD ChildObjects; PDEVICE *ChildDevices; UINTN ChildCount; ULONG RegisterShift; ULONG BaseBaud; BOOL Removed; } SER16550_PARENT, *PSER16550_PARENT; /*++ Structure Description: This structure stores information about a 16550 port. Members: Header - Store the common 16550 object header. Parent - Stores a pointer to the parent structure. Read8 - Stores a pointer to a function used to perform an 8-bit register read. Write8 - Stores a pointer to a function used to perform an 8-bit register write. Index - Stores the index of this child into the parent. PhysicalAddress - Stores the base physical address of the register region, if the UART is memory mapped. MappedAddress - Stores the virtual address of the register region, if the UART is memory mapped. MappedSize - Stores the size of the mapping. IoPortAddress - Stores the I/O port address of the registers if the region is I/O port based. Terminal - Stores a pointer to the terminal device. TransmitBuffer - Stores the ring buffer of bytes waiting to be sent. TransmitStart - Stores the index of the next byte the hardware will send out. TransmitEnd - Stores the index of the next byte to be added to the buffer. TransmitSize - Stores the size of the transmit buffer in bytes. TransmitFifoSize - Stores the size of the hardware transmit FIFO in bytes. ReceiveBuffer - Stores the buffer of received bytes. ReceiveStart - Stores the index of the next byte software should read. ReceiveEnd - Stores the index of the next byte the hardware will add. ReceiveSize - Stores the size of the receive buffer in bytes. ControlFlags - Stores the currently set control flags. See TERMINAL_CONTROL_* definitions. BaudRate - Stores the currently set baud rate. This may be zero if the device is not configured. RegisterOffset - Stores the offset in bytes from the begining of the register region to the 16550ish registers. RegisterShift - Stores the number of bits to shift left to get from a 16550 register to an actual device register. TransmitLock - Stores a pointer to the lock serializing access to the transmit buffer. ReceiveLock - Stores a pointer to the lock serializing access to the receive buffer. TransmitReady - Stores an event that is signaled when the UART can accept more outgoing data. ReceiveReady - Stores an event that is signaled when this receive buffer has data. InterruptWorkPending - Stores a boolean indicating that the interrupt worker needs to process this child. ShouldUnmap - Stores a boolean indicating that this child owns the mapping and should unmap it to clean up. InterruptEnable - Stores a shadow copy of the interrupt enable register. --*/ struct _SER16550_CHILD { SER16550_OBJECT Header; PSER16550_PARENT Parent; PSER16550_READ8 Read8; PSER16550_WRITE8 Write8; UINTN Index; PRESOURCE_ALLOCATION Resource; PHYSICAL_ADDRESS PhysicalAddress; PVOID MappedAddress; UINTN MappedSize; USHORT IoPortAddress; PIO_HANDLE Terminal; PUCHAR TransmitBuffer; UINTN TransmitStart; UINTN TransmitEnd; UINTN TransmitSize; UINTN TransmitFifoSize; PUCHAR ReceiveBuffer; UINTN ReceiveStart; UINTN ReceiveEnd; UINTN ReceiveSize; LONG ControlFlags; ULONG BaudRate; ULONG RegisterOffset; ULONG RegisterShift; PQUEUED_LOCK TransmitLock; PQUEUED_LOCK ReceiveLock; PKEVENT TransmitReady; PKEVENT ReceiveReady; BOOL InterruptWorkPending; BOOL ShouldUnmap; UCHAR InterruptEnable; }; // // ----------------------------------------------- Internal Function Prototypes // KSTATUS Ser16550AddDevice ( PVOID Driver, PSTR DeviceId, PSTR ClassId, PSTR CompatibleIds, PVOID DeviceToken ); VOID Ser16550DispatchStateChange ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID Ser16550DispatchOpen ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID Ser16550DispatchClose ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID Ser16550DispatchIo ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID Ser16550DispatchSystemControl ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID Ser16550DispatchUserControl ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); INTERRUPT_STATUS Ser16550InterruptService ( PVOID Context ); INTERRUPT_STATUS Ser16550InterruptServiceWorker ( PVOID Context ); KSTATUS Ser16550pParentProcessResourceRequirements ( PIRP Irp, PSER16550_PARENT Device ); KSTATUS Ser16550pParentStartDevice ( PIRP Irp, PSER16550_PARENT Device ); KSTATUS Ser16550pInitializeChildrenGeneric ( PSER16550_PARENT Parent, PRESOURCE_ALLOCATION_LIST AllocationList ); KSTATUS Ser16550pInitializeChildrenOxford ( PSER16550_PARENT Parent, PRESOURCE_ALLOCATION_LIST AllocationList ); KSTATUS Ser16550pInitializeChild ( PSER16550_PARENT Parent, UINTN Index ); VOID Ser16550pParentEnumerateChildren ( PIRP Irp, PSER16550_PARENT Device ); VOID Ser16550pChildStartDevice ( PIRP Irp, PSER16550_CHILD Device ); VOID Ser16550pChildDispatchSystemControl ( PIRP Irp, PSER16550_CHILD Device ); VOID Ser16550pChildDispatchUserControl ( PIRP Irp, PSER16550_CHILD Device ); VOID Ser16550pStartTransmit ( PSER16550_CHILD Device ); VOID Ser16550pStopTransmit ( PSER16550_CHILD Device ); KSTATUS Ser16550pConfigureDevice ( PSER16550_CHILD Device, ULONG TerminalControlFlags, ULONG BaudRate ); ULONG Ser16550pGetFifoSize ( PSER16550_CHILD Device ); VOID Ser16550pAddReference ( PSER16550_OBJECT Object ); VOID Ser16550pReleaseReference ( PSER16550_OBJECT Object ); VOID Ser16550pDestroyDevice ( PSER16550_OBJECT Object ); UCHAR Ser16550pReadIo8 ( PSER16550_CHILD Device, SER16550_REGISTER Register ); VOID Ser16550pWriteIo8 ( PSER16550_CHILD Device, SER16550_REGISTER Register, UCHAR Value ); UCHAR Ser16550pReadMemory8 ( PSER16550_CHILD Device, SER16550_REGISTER Register ); VOID Ser16550pWriteMemory8 ( PSER16550_CHILD Device, SER16550_REGISTER Register, UCHAR Value ); // // -------------------------------------------------------------------- Globals // PDRIVER Ser16550Driver = NULL; // // ------------------------------------------------------------------ Functions // KSTATUS DriverEntry ( PDRIVER Driver ) /*++ Routine Description: This routine is the entry point for the 16550 driver. It registers its other dispatch functions, and performs driver-wide initialization. Arguments: Driver - Supplies a pointer to the driver object. Return Value: STATUS_SUCCESS on success. Failure code on error. --*/ { DRIVER_FUNCTION_TABLE FunctionTable; KSTATUS Status; Ser16550Driver = Driver; RtlZeroMemory(&FunctionTable, sizeof(DRIVER_FUNCTION_TABLE)); FunctionTable.Version = DRIVER_FUNCTION_TABLE_VERSION; FunctionTable.AddDevice = Ser16550AddDevice; FunctionTable.DispatchStateChange = Ser16550DispatchStateChange; FunctionTable.DispatchOpen = Ser16550DispatchOpen; FunctionTable.DispatchClose = Ser16550DispatchClose; FunctionTable.DispatchIo = Ser16550DispatchIo; FunctionTable.DispatchSystemControl = Ser16550DispatchSystemControl; FunctionTable.DispatchUserControl = Ser16550DispatchUserControl; Status = IoRegisterDriverFunctions(Driver, &FunctionTable); return Status; } KSTATUS Ser16550AddDevice ( PVOID Driver, PSTR DeviceId, PSTR ClassId, PSTR CompatibleIds, PVOID DeviceToken ) /*++ Routine Description: This routine is called when a device is detected for which the 16550 device acts as the function driver. The driver will attach itself to the stack. Arguments: Driver - Supplies a pointer to the driver being called. DeviceId - Supplies a pointer to a string with the device ID. ClassId - Supplies a pointer to a string containing the device's class ID. CompatibleIds - Supplies a pointer to a string containing device IDs that would be compatible with this device. DeviceToken - Supplies an opaque token that the driver can use to identify the device in the system. This token should be used when attaching to the stack. Return Value: STATUS_SUCCESS on success. Failure code if the driver was unsuccessful in attaching itself. --*/ { ULONG ItemsScanned; PSER16550_PARENT Parent; ULONG PciDeviceId; ULONG PciVendorId; KSTATUS Status; Parent = MmAllocateNonPagedPool(sizeof(SER16550_PARENT), SER16550_ALLOCATION_TAG); if (Parent == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(Parent, sizeof(SER16550_PARENT)); Parent->Header.Type = Ser16550ObjectParent; Parent->Device = DeviceToken; Parent->BaseBaud = SER16550_DEFAULT_BASE_BAUD; Parent->Variant = Ser16550VariantGeneric; Parent->InterruptHandle = INVALID_HANDLE; KeInitializeSpinLock(&(Parent->InterruptLock)); // // Detect variants by PCI vendor and device ID. // Status = RtlStringScan(DeviceId, -1, SER16550_PCI_DEVICE_ID_FORMAT, sizeof(SER16550_PCI_DEVICE_ID_FORMAT), CharacterEncodingDefault, &ItemsScanned, &PciVendorId, &PciDeviceId); if ((KSUCCESS(Status)) && (ItemsScanned == 2)) { Parent->VendorId = PciVendorId; Parent->DeviceId = PciDeviceId; switch (PciVendorId) { case SER16550_VENDOR_INTEL: if (PciDeviceId == SER16550_INTEL_QUARK) { Parent->Variant = Ser16550VariantQuark; Parent->RegisterShift = SER16550_INTEL_QUARK_UART_REGISTER_SHIFT; Parent->BaseBaud = SER16550_INTEL_QUARK_UART_BASE_BAUD; } break; case SER16550_VENDOR_OXFORD: Parent->Variant = Ser16550VariantOxford; break; default: break; } } Status = IoAttachDriverToDevice(Driver, DeviceToken, Parent); if (!KSUCCESS(Status)) { goto AddDeviceEnd; } AddDeviceEnd: if (!KSUCCESS(Status)) { if (Parent != NULL) { MmFreePagedPool(Parent); } } return Status; } VOID Ser16550DispatchStateChange ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles State Change IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { PSER16550_CHILD Child; PSER16550_OBJECT Object; PSER16550_PARENT Parent; KSTATUS Status; ASSERT(Irp->MajorCode == IrpMajorStateChange); if ((Irp->Direction == IrpUp) && (!KSUCCESS(IoGetIrpStatus(Irp)))) { return; } Object = DeviceContext; switch (Object->Type) { // // In this case the driver is the functional driver for the controller. // case Ser16550ObjectParent: Parent = PARENT_STRUCTURE(Object, SER16550_PARENT, Header); switch (Irp->MinorCode) { case IrpMinorQueryResources: // // On the way up, filter the resource requirements to add interrupt // vectors to any lines. // if (Irp->Direction == IrpUp) { Status = Ser16550pParentProcessResourceRequirements(Irp, Parent); if (!KSUCCESS(Status)) { IoCompleteIrp(Ser16550Driver, Irp, Status); } } break; case IrpMinorStartDevice: // // Attempt to fire the thing up if the bus has already started it. // if (Irp->Direction == IrpUp) { Status = Ser16550pParentStartDevice(Irp, Parent); if (!KSUCCESS(Status)) { IoCompleteIrp(Ser16550Driver, Irp, Status); } } break; case IrpMinorQueryChildren: if (Irp->Direction == IrpUp) { Ser16550pParentEnumerateChildren(Irp, Parent); } break; case IrpMinorRemoveDevice: Ser16550pReleaseReference(Object); IoCompleteIrp(Ser16550Driver, Irp, STATUS_SUCCESS); break; default: break; } break; // // In this case the object is the bus driver for an individual port. // case Ser16550ObjectChild: Child = PARENT_STRUCTURE(Object, SER16550_CHILD, Header); switch (Irp->MinorCode) { case IrpMinorQueryResources: IoCompleteIrp(Ser16550Driver, Irp, STATUS_SUCCESS); break; case IrpMinorStartDevice: Ser16550pChildStartDevice(Irp, Child); break; case IrpMinorQueryChildren: IoCompleteIrp(Ser16550Driver, Irp, STATUS_SUCCESS); break; case IrpMinorRemoveDevice: ASSERT(Child->Parent->Removed != FALSE); KeAcquireQueuedLock(Child->ReceiveLock); KeAcquireQueuedLock(Child->TransmitLock); if (Child->Terminal != NULL) { IoTerminalSetDevice(Child->Terminal, NULL); IoClose(Child->Terminal); Child->Terminal = NULL; } if (Child->TransmitReady != NULL) { KeSignalEvent(Child->TransmitReady, SignalOptionSignalAll); } if (Child->ReceiveReady != NULL) { KeSignalEvent(Child->ReceiveReady, SignalOptionSignalAll); } KeReleaseQueuedLock(Child->ReceiveLock); KeReleaseQueuedLock(Child->TransmitLock); Ser16550pReleaseReference(Object); IoCompleteIrp(Ser16550Driver, Irp, STATUS_SUCCESS); break; default: break; } break; default: ASSERT(FALSE); break; } return; } VOID Ser16550DispatchOpen ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles Open IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { PSER16550_OBJECT Object; Object = DeviceContext; if (Object->Type == Ser16550ObjectChild) { Ser16550pAddReference(Object); Irp->U.Open.DeviceContext = PARENT_STRUCTURE(Object, SER16550_CHILD, Header); IoCompleteIrp(Ser16550Driver, Irp, STATUS_SUCCESS); } return; } VOID Ser16550DispatchClose ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles Close IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { PSER16550_CHILD Child; Child = Irp->U.Close.DeviceContext; ASSERT(Child->Header.Type == Ser16550ObjectChild); Ser16550pReleaseReference(&(Child->Header)); IoCompleteIrp(Ser16550Driver, Irp, STATUS_SUCCESS); return; } VOID Ser16550DispatchIo ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles I/O IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { UINTN BytesRemaining; PSER16550_CHILD Child; UINTN CopySize; UINTN CopyStart; PIO_BUFFER IoBuffer; UINTN IoBufferOffset; PQUEUED_LOCK Lock; UINTN Next; RUNLEVEL OldRunLevel; PSER16550_PARENT Parent; UINTN ReceiveEnd; UINTN ReceiveStart; KSTATUS Status; UINTN TransmitEnd; UINTN TransmitStart; ASSERT(Irp->Direction == IrpDown); Child = Irp->U.ReadWrite.DeviceContext; IoBuffer = Irp->U.ReadWrite.IoBuffer; IoBufferOffset = 0; BytesRemaining = Irp->U.ReadWrite.IoSizeInBytes; Parent = Child->Parent; ASSERT(Child->Header.Type == Ser16550ObjectChild); if (Irp->MinorCode == IrpMinorIoRead) { Lock = Child->ReceiveLock; } else { ASSERT(Irp->MinorCode == IrpMinorIoWrite); Lock = Child->TransmitLock; } KeAcquireQueuedLock(Lock); if (Irp->MinorCode == IrpMinorIoWrite) { while (BytesRemaining != 0) { if (Parent->Removed != FALSE) { Status = STATUS_DEVICE_NOT_CONNECTED; goto DispatchIoEnd; } TransmitEnd = Child->TransmitEnd; TransmitStart = Child->TransmitStart; if (TransmitEnd >= TransmitStart) { CopySize = Child->TransmitSize - TransmitEnd; } else { CopySize = TransmitStart - TransmitEnd; } // // If the transmit buffer is full, then wait until a byte can be // added. // if (CopySize == 0) { // // If the transmitter isn't working at all, kick off the // process. // OldRunLevel = IoRaiseToInterruptRunLevel( Parent->InterruptHandle); KeAcquireSpinLock(&(Parent->InterruptLock)); Ser16550pStartTransmit(Child); KeReleaseSpinLock(&(Parent->InterruptLock)); KeLowerRunLevel(OldRunLevel); KeSignalEvent(Child->TransmitReady, SignalOptionUnsignal); KeReleaseQueuedLock(Lock); KeWaitForEvent(Child->TransmitReady, FALSE, WAIT_TIME_INDEFINITE); KeAcquireQueuedLock(Lock); continue; } if (CopySize > BytesRemaining) { CopySize = BytesRemaining; } CopyStart = TransmitEnd; Next = CopyStart + CopySize; ASSERT(Next <= Child->TransmitSize); if (Next == Child->TransmitSize) { Next = 0; } if (Next == TransmitStart) { CopySize -= 1; if (Next == 0) { Next = Child->TransmitSize - 1; } else { Next -= 1; } } Status = MmCopyIoBufferData(IoBuffer, &(Child->TransmitBuffer[CopyStart]), IoBufferOffset, CopySize, FALSE); if (!KSUCCESS(Status)) { goto DispatchIoEnd; } IoBufferOffset += CopySize; BytesRemaining -= CopySize; Child->TransmitEnd = Next; } // // Kick off the transfer if needed. // OldRunLevel = IoRaiseToInterruptRunLevel(Parent->InterruptHandle); KeAcquireSpinLock(&(Parent->InterruptLock)); Ser16550pStartTransmit(Child); KeReleaseSpinLock(&(Parent->InterruptLock)); KeLowerRunLevel(OldRunLevel); } else { ASSERT(Irp->MinorCode == IrpMinorIoRead); while (BytesRemaining != 0) { if (Child->Parent->Removed != FALSE) { Status = STATUS_DEVICE_NOT_CONNECTED; goto DispatchIoEnd; } ReceiveEnd = Child->ReceiveEnd; ReceiveStart = Child->ReceiveStart; if (ReceiveEnd > ReceiveStart) { CopySize = ReceiveEnd - ReceiveStart; } else { CopySize = Child->ReceiveSize - ReceiveStart; } // // Handle an empty receive buffer. // if (CopySize == 0) { // // If some bytes were read in, then return them now. // if (BytesRemaining != Irp->U.ReadWrite.IoSizeInBytes) { break; } // // Block waiting for more bytes to come in. // KeSignalEvent(Child->ReceiveReady, SignalOptionUnsignal); KeReleaseQueuedLock(Lock); KeWaitForEvent(Child->ReceiveReady, FALSE, WAIT_TIME_INDEFINITE); KeAcquireQueuedLock(Lock); continue; } CopyStart = ReceiveStart; if (CopySize > BytesRemaining) { CopySize = BytesRemaining; } ASSERT((CopySize < Child->ReceiveSize) && (CopyStart + CopySize <= Child->ReceiveSize)); Status = MmCopyIoBufferData(IoBuffer, &(Child->ReceiveBuffer[CopyStart]), IoBufferOffset, CopySize, TRUE); if (!KSUCCESS(Status)) { goto DispatchIoEnd; } IoBufferOffset += CopySize; BytesRemaining -= CopySize; CopyStart += CopySize; if (CopyStart == Child->ReceiveSize) { CopyStart = 0; } Child->ReceiveStart = CopyStart; } } Status = STATUS_SUCCESS; DispatchIoEnd: KeReleaseQueuedLock(Lock); Irp->U.ReadWrite.IoBytesCompleted = Irp->U.ReadWrite.IoSizeInBytes - BytesRemaining; IoCompleteIrp(Ser16550Driver, Irp, Status); return; } VOID Ser16550DispatchSystemControl ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles System Control IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { PSER16550_CHILD Child; ASSERT(Irp->MajorCode == IrpMajorSystemControl); Child = DeviceContext; switch (Child->Header.Type) { case Ser16550ObjectParent: break; case Ser16550ObjectChild: Ser16550pChildDispatchSystemControl(Irp, Child); break; default: ASSERT(FALSE); break; } return; } VOID Ser16550DispatchUserControl ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles User Control IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { PSER16550_CHILD Child; ASSERT(Irp->MajorCode == IrpMajorUserControl); Child = DeviceContext; switch (Child->Header.Type) { case Ser16550ObjectParent: break; case Ser16550ObjectChild: Ser16550pChildDispatchUserControl(Irp, Child); break; default: ASSERT(FALSE); break; } return; } INTERRUPT_STATUS Ser16550InterruptService ( PVOID Context ) /*++ Routine Description: This routine implements the EHCI interrupt service routine. Arguments: Context - Supplies the context pointer given to the system when the interrupt was connected. In this case, this points to the EHCI controller. Return Value: Interrupt status. --*/ { UCHAR Byte; PSER16550_CHILD Child; UINTN ChildIndex; ULONG Count; BOOL DidSomething; UCHAR InterruptRegister; INTERRUPT_STATUS InterruptStatus; UCHAR LineStatus; UINTN Next; PSER16550_PARENT Parent; InterruptStatus = InterruptStatusNotClaimed; Parent = Context; // // Loop over every serial port this interrupt services. // for (ChildIndex = 0; ChildIndex < Parent->ChildCount; ChildIndex += 1) { Child = &(Parent->ChildObjects[ChildIndex]); // // Skip invalid children. // if (Child->Header.Type != Ser16550ObjectChild) { continue; } // // Quickly exit if the UART is not interrupting. // InterruptRegister = SER16550_READ8(Child, Ser16550InterruptStatus); if ((InterruptRegister & SER16550_INTERRUPT_STATUS_NONE_PENDING) != 0) { continue; } InterruptStatus = InterruptStatusClaimed; do { DidSomething = FALSE; LineStatus = SER16550_READ8(Child, Ser16550LineStatus); if ((LineStatus & SER16550_LINE_STATUS_ERROR_MASK) != 0) { DidSomething = TRUE; // // TODO: Actually handle 16550 line status errors. // if ((LineStatus & SER16550_LINE_STATUS_OVERRUN_ERROR) != 0) { RtlDebugPrint("16550: Overrun Error.\n"); } else if ((LineStatus & SER16550_LINE_STATUS_PARITY_ERROR) != 0) { RtlDebugPrint("16550: Parity Error.\n"); } else if ((LineStatus & SER16550_LINE_STATUS_FRAMING_ERROR) != 0) { RtlDebugPrint("16550: Framing Error.\n"); } else if ((LineStatus & SER16550_LINE_STATUS_FIFO_ERROR) != 0) { RtlDebugPrint("16550: Fifo Error.\n"); } } // // Transmit more stuff if possible. // if ((LineStatus & SER16550_LINE_STATUS_TX_HOLDING_EMPTY) != 0) { KeAcquireSpinLock(&(Child->Parent->InterruptLock)); for (Count = 0; Count < Child->TransmitFifoSize; Count += 1) { if (Child->TransmitStart == Child->TransmitEnd) { Ser16550pStopTransmit(Child); break; } else { Byte = Child->TransmitBuffer[Child->TransmitStart]; SER16550_WRITE8(Child, Ser16550Data, Byte); Next = Child->TransmitStart + 1; if (Next == Child->TransmitSize) { Next = 0; } Child->TransmitStart = Next; DidSomething = TRUE; } } KeReleaseSpinLock(&(Child->Parent->InterruptLock)); Child->InterruptWorkPending = TRUE; } // // Receive a byte data if possible. // if ((LineStatus & SER16550_LINE_STATUS_RX_MASK) != 0) { DidSomething = TRUE; // // TODO: Actually handle a 16550 break. // if ((LineStatus & SER16550_LINE_STATUS_BREAK) != 0) { RtlDebugPrint("16550: Break\n"); } else if ((LineStatus & SER16550_LINE_STATUS_RX_READY) != 0) { Byte = SER16550_READ8(Child, Ser16550Data); Next = Child->ReceiveEnd + 1; if (Next == Child->ReceiveSize) { Next = 0; } if (Next == Child->ReceiveStart) { RtlDebugPrint("Uart RX Overflow\n"); } else { Child->ReceiveBuffer[Child->ReceiveEnd] = Byte; Child->ReceiveEnd = Next; } } } } while (DidSomething != FALSE); } return InterruptStatus; } INTERRUPT_STATUS Ser16550InterruptServiceWorker ( PVOID Context ) /*++ Routine Description: This routine processes interrupts for the 16550 UART at low level. Arguments: Context - Supplies a context pointer, which in this case points to the parent controller. Return Value: Interrupt status. --*/ { UINTN BytesCompleted; PSER16550_CHILD Child; UINTN ChildIndex; INTERRUPT_STATUS InterruptStatus; PIO_BUFFER IoBuffer; UINTN Next; PSER16550_PARENT Parent; UINTN ReceiveEnd; UINTN ReceiveStart; UINTN Size; KSTATUS Status; Parent = Context; ASSERT(KeGetRunLevel() == RunLevelLow); InterruptStatus = InterruptStatusNotClaimed; // // Loop over every serial port this interrupt services. // for (ChildIndex = 0; ChildIndex < Parent->ChildCount; ChildIndex += 1) { Child = &(Parent->ChildObjects[ChildIndex]); if (Child->InterruptWorkPending == FALSE) { continue; } Child->InterruptWorkPending = FALSE; InterruptStatus = InterruptStatusClaimed; // // Signal the transmit ready event if some data was processed by now. // KeAcquireQueuedLock(Child->TransmitLock); Next = Child->TransmitEnd + 1; if (Next == Child->TransmitSize) { Next = 0; } if (Next != Child->TransmitStart) { KeSignalEvent(Child->TransmitReady, SignalOptionSignalAll); } KeReleaseQueuedLock(Child->TransmitLock); // // If there's a terminal, feed the terminal. Otherwise, maintain the // event. // KeAcquireQueuedLock(Child->ReceiveLock); ReceiveEnd = Child->ReceiveEnd; ReceiveStart = Child->ReceiveStart; while (ReceiveStart != ReceiveEnd) { if (ReceiveEnd >= ReceiveStart) { Size = ReceiveEnd - ReceiveStart; } else { Size = Child->ReceiveSize - ReceiveStart; } Status = MmCreateIoBuffer(Child->ReceiveBuffer + ReceiveStart, Size, IO_BUFFER_FLAG_KERNEL_MODE_DATA, &IoBuffer); BytesCompleted = 0; if (KSUCCESS(Status)) { if (Child->Terminal != NULL) { Status = IoWrite(Child->Terminal, IoBuffer, Size, 0, 0, &BytesCompleted); } else { KeSignalEvent(Child->ReceiveReady, SignalOptionSignalAll); } MmFreeIoBuffer(IoBuffer); if (!KSUCCESS(Status)) { RtlDebugPrint("Ser16550: Failed terminal write: %x\n", Status); } } ASSERT(ReceiveStart + BytesCompleted <= Child->ReceiveSize); ReceiveStart += BytesCompleted; if (ReceiveStart == Child->ReceiveSize) { ReceiveStart = 0; } Child->ReceiveStart = ReceiveStart; if (BytesCompleted == 0) { break; } ReceiveEnd = Child->ReceiveEnd; ReceiveStart = Child->ReceiveStart; } KeReleaseQueuedLock(Child->ReceiveLock); } return InterruptStatus; } // // --------------------------------------------------------- Internal Functions // KSTATUS Ser16550pParentProcessResourceRequirements ( PIRP Irp, PSER16550_PARENT Device ) /*++ Routine Description: This routine filters through the resource requirements presented by the bus for a 16550 parent device. It adds an interrupt vector requirement for any interrupt line requested. Arguments: Irp - Supplies a pointer to the I/O request packet. Device - Supplies a pointer to this parent device. Return Value: Status code. --*/ { PRESOURCE_CONFIGURATION_LIST Requirements; KSTATUS Status; RESOURCE_REQUIREMENT VectorRequirement; ASSERT((Irp->MajorCode == IrpMajorStateChange) && (Irp->MinorCode == IrpMinorQueryResources)); // // Initialize a nice interrupt vector requirement in preparation. // RtlZeroMemory(&VectorRequirement, sizeof(RESOURCE_REQUIREMENT)); VectorRequirement.Type = ResourceTypeInterruptVector; VectorRequirement.Minimum = 0; VectorRequirement.Maximum = -1; VectorRequirement.Length = 1; Requirements = Irp->U.QueryResources.ResourceRequirements; Status = IoCreateAndAddInterruptVectorsForLines(Requirements, &VectorRequirement); if (!KSUCCESS(Status)) { goto ParentProcessResourceRequirementsEnd; } ParentProcessResourceRequirementsEnd: return Status; } KSTATUS Ser16550pParentStartDevice ( PIRP Irp, PSER16550_PARENT Device ) /*++ Routine Description: This routine starts up the 16550 controller. Arguments: Irp - Supplies a pointer to the I/O request packet. Device - Supplies a pointer to this 16550 device. Return Value: Status code. --*/ { PRESOURCE_ALLOCATION Allocation; PRESOURCE_ALLOCATION_LIST AllocationList; UINTN ChildCount; IO_CONNECT_INTERRUPT_PARAMETERS Connect; PRESOURCE_ALLOCATION LineAllocation; KSTATUS Status; ChildCount = 0; // // Loop through the allocated resources to get the interrupt and count the // number of BARs. // AllocationList = Irp->U.StartDevice.ProcessorLocalResources; Allocation = IoGetNextResourceAllocation(AllocationList, NULL); while (Allocation != NULL) { // // If the resource is an interrupt vector, then it should have an // owning interrupt line allocation. // if (Allocation->Type == ResourceTypeInterruptVector) { // // Currently only one interrupt resource is expected. // ASSERT(Device->InterruptResourcesFound == FALSE); ASSERT(Allocation->OwningAllocation != NULL); // // Save the line and vector number. // LineAllocation = Allocation->OwningAllocation; Device->InterruptLine = LineAllocation->Allocation; Device->InterruptVector = Allocation->Allocation; Device->InterruptResourcesFound = TRUE; } else if (Allocation->Type == ResourceTypePhysicalAddressSpace) { ChildCount += 1; } else if (Allocation->Type == ResourceTypeIoPort) { ChildCount += 1; } // // Get the next allocation in the list. // Allocation = IoGetNextResourceAllocation(AllocationList, Allocation); } // // Some variants override the child count. // switch (Device->Variant) { case Ser16550VariantQuark: ChildCount = 1; break; case Ser16550VariantOxford: // // Oxford devices just have a single UART, except for a couple // (specified by the mode bits encoded in the device ID) that have two. // ASSERT(Device->VendorId == SER16550_VENDOR_OXFORD); ChildCount = 1; if (SER16550_OXFORD_DUAL_UARTS(Device->DeviceId) != FALSE) { ChildCount = 2; } break; default: break; } if (ChildCount == 0) { Status = STATUS_NOT_CONFIGURED; goto ParentStartDeviceEnd; } // // Allocate the child arrays. // if (ChildCount != Device->ChildCount) { ASSERT(Device->ChildCount == 0); Device->ChildDevices = MmAllocatePagedPool(ChildCount * sizeof(PDEVICE), SER16550_ALLOCATION_TAG); if (Device->ChildDevices == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ParentStartDeviceEnd; } RtlZeroMemory(Device->ChildDevices, ChildCount * sizeof(PDEVICE)); Device->ChildObjects = MmAllocatePagedPool( ChildCount * sizeof(SER16550_CHILD), SER16550_ALLOCATION_TAG); if (Device->ChildObjects == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto ParentStartDeviceEnd; } RtlZeroMemory(Device->ChildObjects, ChildCount * sizeof(SER16550_CHILD)); Device->ChildCount = ChildCount; } // // Initialize the child devices with their correct resources. // switch (Device->Variant) { case Ser16550VariantOxford: Status = Ser16550pInitializeChildrenOxford(Device, AllocationList); break; case Ser16550VariantQuark: case Ser16550VariantGeneric: Status = Ser16550pInitializeChildrenGeneric(Device, AllocationList); break; default: ASSERT(FALSE); Status = STATUS_INVALID_CONFIGURATION; } if (!KSUCCESS(Status)) { goto ParentStartDeviceEnd; } // // Attempt to connect the interrupt. // if ((Device->InterruptResourcesFound != FALSE) && (Device->InterruptHandle == INVALID_HANDLE)) { RtlZeroMemory(&Connect, sizeof(IO_CONNECT_INTERRUPT_PARAMETERS)); Connect.Version = IO_CONNECT_INTERRUPT_PARAMETERS_VERSION; Connect.Device = Irp->Device; Connect.LineNumber = Device->InterruptLine; Connect.Vector = Device->InterruptVector; Connect.InterruptServiceRoutine = Ser16550InterruptService; Connect.LowLevelServiceRoutine = Ser16550InterruptServiceWorker; Connect.Context = Device; Connect.Interrupt = &(Device->InterruptHandle); Status = IoConnectInterrupt(&Connect); if (!KSUCCESS(Status)) { goto ParentStartDeviceEnd; } } Status = STATUS_SUCCESS; ParentStartDeviceEnd: if (!KSUCCESS(Status)) { if (Device->InterruptHandle != INVALID_HANDLE) { IoDisconnectInterrupt(Device->InterruptHandle); Device->InterruptHandle = INVALID_HANDLE; } } return Status; } KSTATUS Ser16550pInitializeChildrenGeneric ( PSER16550_PARENT Parent, PRESOURCE_ALLOCATION_LIST AllocationList ) /*++ Routine Description: This routine initializes the child device structures for a standard 16550 UART device. In a standard device, each BAR is assumed to correspond to a UART, up to the number of children. Arguments: Parent - Supplies a pointer to the parent. AllocationList - Supplies a pointer to the resource allocation list. Return Value: Status code. --*/ { PRESOURCE_ALLOCATION Allocation; UINTN ChildIndex; PSER16550_CHILD ChildObject; UINTN ReleaseCount; KSTATUS Status; Allocation = IoGetNextResourceAllocation(AllocationList, NULL); ChildIndex = 0; while (ChildIndex < Parent->ChildCount) { ASSERT(Allocation != NULL); // // For each BAR found, initialize a child device. // if ((Allocation->Type == ResourceTypePhysicalAddressSpace) || (Allocation->Type == ResourceTypeIoPort)) { ChildObject = &(Parent->ChildObjects[ChildIndex]); Status = Ser16550pInitializeChild(Parent, ChildIndex); if (!KSUCCESS(Status)) { goto InitializeChildrenGenericEnd; } if (Allocation->Type == ResourceTypePhysicalAddressSpace) { ChildObject->MappedSize = Allocation->Length; ChildObject->PhysicalAddress = Allocation->Allocation; ChildObject->MappedAddress = MmMapPhysicalAddress( Allocation->Allocation, Allocation->Length, TRUE, FALSE, TRUE); if (ChildObject->MappedAddress == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeChildrenGenericEnd; } ChildObject->Read8 = Ser16550pReadMemory8; ChildObject->Write8 = Ser16550pWriteMemory8; ChildObject->ShouldUnmap = TRUE; } else { ChildObject->IoPortAddress = (USHORT)(Allocation->Allocation); ChildObject->Read8 = Ser16550pReadIo8; ChildObject->Write8 = Ser16550pWriteIo8; } ChildIndex += 1; } // // Get the next allocation in the list. // Allocation = IoGetNextResourceAllocation(AllocationList, Allocation); } Status = STATUS_SUCCESS; InitializeChildrenGenericEnd: if (!KSUCCESS(Status)) { ASSERT(ChildIndex != Parent->ChildCount); ReleaseCount = ChildIndex; for (ChildIndex = 0; ChildIndex <= ReleaseCount; ChildIndex += 1) { ASSERT(Parent->ChildObjects[ChildIndex].Header.ReferenceCount == 1); Ser16550pReleaseReference( &(Parent->ChildObjects[ChildIndex].Header)); } } return Status; } KSTATUS Ser16550pInitializeChildrenOxford ( PSER16550_PARENT Parent, PRESOURCE_ALLOCATION_LIST AllocationList ) /*++ Routine Description: This routine initializes the child device structures for an Oxford UART. Arguments: Parent - Supplies a pointer to the parent. AllocationList - Supplies a pointer to the resource allocation list. Return Value: Status code. --*/ { PRESOURCE_ALLOCATION Allocation; UINTN ChildIndex; PSER16550_CHILD ChildObject; UINTN ReleaseCount; KSTATUS Status; // // Find the first BAR, which is where the single or dual UART resides. // Allocation = IoGetNextResourceAllocation(AllocationList, NULL); while (Allocation != NULL) { if ((Allocation->Type == ResourceTypePhysicalAddressSpace) || (Allocation->Type == ResourceTypeIoPort)) { break; } // // Get the next allocation in the list. // Allocation = IoGetNextResourceAllocation(AllocationList, Allocation); } if (Allocation == NULL) { return STATUS_NOT_CONFIGURED; } // // If the BAR is an I/O port, then this is a legacy compatible controller. // if (Allocation->Type == ResourceTypeIoPort) { ChildIndex = 0; ChildObject = &(Parent->ChildObjects[0]); Status = Ser16550pInitializeChild(Parent, 0); if (!KSUCCESS(Status)) { goto InitializeChildrenGenericEnd; } ChildObject->IoPortAddress = (USHORT)(Allocation->Allocation); ChildObject->Read8 = Ser16550pReadIo8; ChildObject->Write8 = Ser16550pWriteIo8; // // This is a native UART. // } else { Parent->BaseBaud = SER16550_OXFORD_BASE_BAUD; for (ChildIndex = 0; ChildIndex < Parent->ChildCount; ChildIndex += 1) { ChildObject = &(Parent->ChildObjects[ChildIndex]); Status = Ser16550pInitializeChild(Parent, ChildIndex); if (!KSUCCESS(Status)) { goto InitializeChildrenGenericEnd; } ChildObject->MappedSize = Allocation->Length; ChildObject->PhysicalAddress = Allocation->Allocation; if (ChildIndex == 0) { ChildObject->MappedAddress = MmMapPhysicalAddress( Allocation->Allocation, Allocation->Length, TRUE, FALSE, TRUE); if (ChildObject->MappedAddress == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeChildrenGenericEnd; } ChildObject->ShouldUnmap = TRUE; } else { ChildObject->MappedAddress = Parent->ChildObjects[0].MappedAddress; } ChildObject->RegisterOffset = SER16550_OXFORD_UART_OFFSET + (ChildIndex * SER16550_OXFORD_UART_STRIDE); ChildObject->Read8 = Ser16550pReadMemory8; ChildObject->Write8 = Ser16550pWriteMemory8; } } Status = STATUS_SUCCESS; InitializeChildrenGenericEnd: if (!KSUCCESS(Status)) { ASSERT(ChildIndex != Parent->ChildCount); ReleaseCount = ChildIndex; for (ChildIndex = 0; ChildIndex <= ReleaseCount; ChildIndex += 1) { ASSERT(Parent->ChildObjects[ChildIndex].Header.ReferenceCount == 1); Ser16550pReleaseReference( &(Parent->ChildObjects[ChildIndex].Header)); } } return Status; } KSTATUS Ser16550pInitializeChild ( PSER16550_PARENT Parent, UINTN Index ) /*++ Routine Description: This routine creates the resources associated with a 16550 UART child. Arguments: Parent - Supplies a pointer to the parent. Index - Supplies the child index within the parent. Return Value: Status code. --*/ { PSER16550_CHILD ChildObject; KSTATUS Status; ChildObject = &(Parent->ChildObjects[Index]); ChildObject->Header.Type = Ser16550ObjectChild; ChildObject->Header.ReferenceCount = 1; ChildObject->Parent = Parent; ChildObject->Index = Index; ChildObject->RegisterOffset = 0; ChildObject->RegisterShift = Parent->RegisterShift; if (ChildObject->TransmitBuffer == NULL) { ChildObject->TransmitBuffer = MmAllocateNonPagedPool( SER16550_DEFAULT_BUFFER_SIZE, SER16550_ALLOCATION_TAG); if (ChildObject->TransmitBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeChildEnd; } ChildObject->TransmitSize = SER16550_DEFAULT_BUFFER_SIZE; ChildObject->TransmitStart = 0; ChildObject->TransmitEnd = 0; } if (ChildObject->ReceiveBuffer == NULL) { ChildObject->ReceiveBuffer = MmAllocateNonPagedPool( SER16550_DEFAULT_BUFFER_SIZE, SER16550_ALLOCATION_TAG); if (ChildObject->ReceiveBuffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeChildEnd; } ChildObject->ReceiveSize = SER16550_DEFAULT_BUFFER_SIZE; ChildObject->ReceiveStart = 0; ChildObject->ReceiveEnd = 0; } if (ChildObject->TransmitLock == NULL) { ChildObject->TransmitLock = KeCreateQueuedLock(); if (ChildObject->TransmitLock == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeChildEnd; } } if (ChildObject->ReceiveLock == NULL) { ChildObject->ReceiveLock = KeCreateQueuedLock(); if (ChildObject->ReceiveLock == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeChildEnd; } } if (ChildObject->TransmitReady == NULL) { ChildObject->TransmitReady = KeCreateEvent(NULL); if (ChildObject->TransmitReady == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeChildEnd; } KeSignalEvent(ChildObject->TransmitReady, SignalOptionSignalAll); } if (ChildObject->ReceiveReady == NULL) { ChildObject->ReceiveReady = KeCreateEvent(NULL); if (ChildObject->ReceiveReady == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeChildEnd; } } Status = STATUS_SUCCESS; InitializeChildEnd: return Status; } VOID Ser16550pParentEnumerateChildren ( PIRP Irp, PSER16550_PARENT Device ) /*++ Routine Description: This routine enumerates all ports in the serial controller. Arguments: Irp - Supplies a pointer to the I/O request packet. Device - Supplies a pointer to this 16550 device. Return Value: None. --*/ { UINTN ChildIndex; CHAR DeviceId[SERIAL_PORT_DEVICE_ID_SIZE]; KSTATUS Status; // // Create child devices for each child. // for (ChildIndex = 0; ChildIndex < Device->ChildCount; ChildIndex += 1) { ASSERT(Device->ChildObjects[ChildIndex].Header.Type == Ser16550ObjectChild); if (Device->ChildDevices[ChildIndex] == NULL) { RtlPrintToString(DeviceId, SERIAL_PORT_DEVICE_ID_SIZE, CharacterEncodingDefault, SERIAL_PORT_DEVICE_ID_FORMAT, ChildIndex); Status = IoCreateDevice(Ser16550Driver, &(Device->ChildObjects[ChildIndex]), Device->Device, DeviceId, CHARACTER_CLASS_ID, NULL, &(Device->ChildDevices[ChildIndex])); if (!KSUCCESS(Status)) { goto ParentEnumerateChildrenEnd; } } } Status = IoMergeChildArrays(Irp, Device->ChildDevices, Device->ChildCount, SER16550_ALLOCATION_TAG); if (!KSUCCESS(Status)) { goto ParentEnumerateChildrenEnd; } ParentEnumerateChildrenEnd: IoCompleteIrp(Ser16550Driver, Irp, Status); return; } VOID Ser16550pChildStartDevice ( PIRP Irp, PSER16550_CHILD Device ) /*++ Routine Description: This routine starts an individual 16550serial port. Arguments: Irp - Supplies a pointer to the I/O request packet. Device - Supplies a pointer to this 16550 device. Return Value: None. --*/ { PIO_HANDLE DeviceHandle; PDEBUG_HANDOFF_DATA HandoffData; KSTATUS Status; DeviceHandle = NULL; // // Determine if this UART is being used by the kernel debug transport, and // fail to start up if it is (as the kernel debugger owns it). // Status = KdGetDeviceInformation(&HandoffData); if ((KSUCCESS(Status)) && (HandoffData != NULL) && (HandoffData->PortType == DEBUG_PORT_TYPE_SERIAL) && ((HandoffData->PortSubType == DEBUG_PORT_SERIAL_16550) || (HandoffData->PortSubType == DEBUG_PORT_SERIAL_16550_COMPATIBLE))) { if (HandoffData->Identifier == Device->PhysicalAddress) { Status = STATUS_RESOURCE_IN_USE; goto ChildStartDeviceEnd; } } // // Create the terminal object. // if (Device->Terminal == NULL) { Status = IoCreateTerminal(TRUE, NULL, NULL, NULL, 0, NULL, 0, IO_ACCESS_READ | IO_ACCESS_WRITE, OPEN_FLAG_NO_CONTROLLING_TERMINAL, TERMINAL_DEFAULT_PERMISSIONS, TERMINAL_DEFAULT_PERMISSIONS, &(Device->Terminal)); if (!KSUCCESS(Status)) { goto ChildStartDeviceEnd; } // // Open a handle to this very device. // Status = IoOpenDevice(Irp->Device, IO_ACCESS_READ | IO_ACCESS_WRITE, 0, &DeviceHandle, NULL, NULL, NULL); if (!KSUCCESS(Status)) { goto ChildStartDeviceEnd; } // // Associate the hardware device with the terminal. The terminal now // owns the handle. // Status = IoTerminalSetDevice(Device->Terminal, DeviceHandle); if (!KSUCCESS(Status)) { goto ChildStartDeviceEnd; } DeviceHandle = NULL; } Status = STATUS_SUCCESS; ChildStartDeviceEnd: if (DeviceHandle != NULL) { IoClose(DeviceHandle); } if (!KSUCCESS(Status)) { if (Device->Terminal != NULL) { IoClose(Device->Terminal); Device->Terminal = NULL; } } IoCompleteIrp(Ser16550Driver, Irp, Status); return; } VOID Ser16550pChildDispatchSystemControl ( PIRP Irp, PSER16550_CHILD Device ) /*++ Routine Description: This routine handles system control IRPs for the 16550 child device. Arguments: Irp - Supplies a pointer to the I/O request packet. Device - Supplies a pointer to this 16550 device. Return Value: None. The IRP will be completed appropriately. --*/ { PVOID Context; PSYSTEM_CONTROL_FILE_OPERATION FileOperation; PSYSTEM_CONTROL_LOOKUP Lookup; PFILE_PROPERTIES Properties; ULONGLONG PropertiesFileSize; KSTATUS Status; Context = Irp->U.SystemControl.SystemContext; switch (Irp->MinorCode) { case IrpMinorSystemControlLookup: Lookup = (PSYSTEM_CONTROL_LOOKUP)Context; Status = STATUS_PATH_NOT_FOUND; if (Lookup->Root != FALSE) { // // Enable opening of the root as a character device. // Properties = &(Lookup->Properties); Properties->FileId = 0; Properties->Type = IoObjectCharacterDevice; Properties->HardLinkCount = 1; Properties->BlockSize = 0; Properties->BlockCount = 0; WRITE_INT64_SYNC(&(Properties->FileSize), 0); Status = STATUS_SUCCESS; } IoCompleteIrp(Ser16550Driver, Irp, Status); break; // // Fail if the properties being written are different. // case IrpMinorSystemControlWriteFileProperties: FileOperation = (PSYSTEM_CONTROL_FILE_OPERATION)Context; Properties = FileOperation->FileProperties; READ_INT64_SYNC(&(Properties->FileSize), &PropertiesFileSize); if ((Properties->FileId != 0) || (Properties->Type != IoObjectCharacterDevice) || (Properties->HardLinkCount != 1) || (Properties->BlockSize != 0) || (Properties->BlockCount != 0) || (PropertiesFileSize != 0)) { Status = STATUS_NOT_SUPPORTED; } else { Status = STATUS_SUCCESS; } IoCompleteIrp(Ser16550Driver, Irp, Status); break; // // Do not support hard disk device truncation. // case IrpMinorSystemControlTruncate: IoCompleteIrp(Ser16550Driver, Irp, STATUS_NOT_SUPPORTED); break; // // Gather and return device information. // case IrpMinorSystemControlDeviceInformation: break; // // Send all pending output data. // TODO: Wait for all pending output data to be complete. // case IrpMinorSystemControlSynchronize: Status = STATUS_SUCCESS; IoCompleteIrp(Ser16550Driver, Irp, Status); break; // // Ignore everything unrecognized. // default: break; } return; } VOID Ser16550pChildDispatchUserControl ( PIRP Irp, PSER16550_CHILD Device ) /*++ Routine Description: This routine handles user control IRPs for the 16550 child device. Arguments: Irp - Supplies a pointer to the I/O request packet. Device - Supplies a pointer to this 16550 device. Return Value: None. The IRP will be completed appropriately. --*/ { TERMINAL_USER_CONTROL_CODE ControlCode; KSTATUS Status; TERMINAL_SETTINGS TerminalSettings; TERMINAL_SETTINGS_OLD TerminalSettingsOld; Status = STATUS_NOT_HANDLED; ControlCode = Irp->MinorCode; switch (ControlCode) { case TerminalControlSetAttributesFlush: // // Flush the input. // Device->ReceiveStart = Device->ReceiveEnd; // // Fall through. // case TerminalControlSetAttributesDrain: // // TODO: Flush the output. // // // Fall through. // case TerminalControlSetAttributes: if (Irp->U.UserControl.FromKernelMode != FALSE) { RtlCopyMemory(&TerminalSettings, Irp->U.UserControl.UserBuffer, sizeof(TERMINAL_SETTINGS)); Status = STATUS_SUCCESS; } else { Status = MmCopyFromUserMode(&TerminalSettings, Irp->U.UserControl.UserBuffer, sizeof(TERMINAL_SETTINGS)); if (!KSUCCESS(Status)) { break; } } if (TerminalSettings.InputSpeed != TerminalSettings.OutputSpeed) { Status = STATUS_NOT_SUPPORTED; break; } if ((TerminalSettings.ControlFlags != Device->ControlFlags) || (TerminalSettings.OutputSpeed != Device->BaudRate)) { Status = Ser16550pConfigureDevice(Device, TerminalSettings.ControlFlags, TerminalSettings.OutputSpeed); if (!KSUCCESS(Status)) { break; } Device->ControlFlags = TerminalSettings.ControlFlags; Device->BaudRate = TerminalSettings.OutputSpeed; } break; case TerminalControlSetAttributesFlushOld: // // Flush the input. // Device->ReceiveStart = Device->ReceiveEnd; // // Fall through. // case TerminalControlSetAttributesDrainOld: // // TODO: Flush the output. // // // Fall through. // case TerminalControlSetAttributesOld: if (Irp->U.UserControl.FromKernelMode != FALSE) { RtlCopyMemory(&TerminalSettingsOld, Irp->U.UserControl.UserBuffer, sizeof(TERMINAL_SETTINGS_OLD)); Status = STATUS_SUCCESS; } else { Status = MmCopyFromUserMode(&TerminalSettingsOld, Irp->U.UserControl.UserBuffer, sizeof(TERMINAL_SETTINGS_OLD)); if (!KSUCCESS(Status)) { break; } } if (TerminalSettingsOld.ControlFlags != Device->ControlFlags) { Status = Ser16550pConfigureDevice(Device, TerminalSettingsOld.ControlFlags, Device->BaudRate); if (!KSUCCESS(Status)) { break; } Device->ControlFlags = TerminalSettingsOld.ControlFlags; } break; case TerminalControlSendBreak: // // TODO: Send a serial break. // break; case TerminalControlFlowControl: // // TODO: Handle serial flow control. // break; case TerminalControlFlush: // // TODO: Handle serial flush. // break; case TerminalControlGetModemStatus: // // TODO: Get serial modem status. // break; case TerminalControlOrModemStatus: // // TODO: Get serial control/modem status. // break; case TerminalControlClearModemStatus: // // TODO: Clear serial modem status. // break; case TerminalControlSetModemStatus: // // TODO: Set serial modem status. // break; case TerminalControlGetSoftCarrier: case TerminalControlSetSoftCarrier: // // TODO: Get/set serial soft carrier status. // break; case TerminalControlSendBreakPosix: case TerminalControlStartBreak: case TerminalControlStopBreak: // // TODO: Send a serial break. // Status = STATUS_SUCCESS; break; case TerminalControlGetAttributes: case TerminalControlGetAttributesOld: case TerminalControlSetExclusive: case TerminalControlClearExclusive: case TerminalControlGetOutputQueueSize: case TerminalControlGetInputQueueSize: case TerminalControlInsertInInputQueue: case TerminalControlGetWindowSize: case TerminalControlSetWindowSize: case TerminalControlRedirectLocalConsole: case TerminalControlSetPacketMode: case TerminalControlGiveUpControllingTerminal: case TerminalControlSetControllingTerminal: case TerminalControlGetProcessGroup: case TerminalControlSetProcessGroup: case TerminalControlGetCurrentSessionId: default: break; } if (Status != STATUS_NOT_HANDLED) { IoCompleteIrp(Ser16550Driver, Irp, Status); } return; } VOID Ser16550pStartTransmit ( PSER16550_CHILD Device ) /*++ Routine Description: This routine starts transmission on the 16550 device. Arguments: Device - Supplies a pointer to the device. Return Value: None. --*/ { if ((Device->InterruptEnable & SER16550_INTERRUPT_ENABLE_TX_EMPTY) == 0) { Device->InterruptEnable |= SER16550_INTERRUPT_ENABLE_TX_EMPTY; SER16550_WRITE8(Device, Ser16550InterruptEnable, Device->InterruptEnable); } return; } VOID Ser16550pStopTransmit ( PSER16550_CHILD Device ) /*++ Routine Description: This routine stops transmission on the 16550 device. Arguments: Device - Supplies a pointer to the device. Return Value: None. --*/ { if ((Device->InterruptEnable & SER16550_INTERRUPT_ENABLE_TX_EMPTY) != 0) { Device->InterruptEnable &= ~SER16550_INTERRUPT_ENABLE_TX_EMPTY; SER16550_WRITE8(Device, Ser16550InterruptEnable, Device->InterruptEnable); } return; } KSTATUS Ser16550pConfigureDevice ( PSER16550_CHILD Device, ULONG TerminalControlFlags, ULONG BaudRate ) /*++ Routine Description: This routine configures the serial device, including baud rate, data bits, stop bits, and parity. Arguments: Device - Supplies a pointer to the device. TerminalControlFlags - Supplies the bitfield of terminal control flags governing parity, data width, and stop bits. See TERMINAL_CONTROL_* definitions. BaudRate - Supplies the requested baud rate. Return Value: Status code. --*/ { ULONG CurrentBaud; ULONG Divisor; UCHAR Value; // // Compute the appropriate divisor. // Divisor = 1; if (BaudRate > Device->Parent->BaseBaud) { return STATUS_NOT_SUPPORTED; } Divisor = 1; while (TRUE) { CurrentBaud = Device->Parent->BaseBaud / Divisor; if ((CurrentBaud <= BaudRate) || (CurrentBaud == 0)) { break; } Divisor += 1; } if ((CurrentBaud == 0) || (Divisor > MAX_USHORT)) { return STATUS_NOT_SUPPORTED; } // // Disable all interrupts. // Device->InterruptEnable = 0; SER16550_WRITE8(Device, Ser16550InterruptEnable, Device->InterruptEnable); // // Set the divisor latch enable bit to get at the divisor registers. // Value = SER16550_LINE_CONTROL_DIVISOR_LATCH; SER16550_WRITE8(Device, Ser16550LineControl, Value); // // Write the computed divisor value. // SER16550_WRITE8(Device, Ser16550DivisorLow, (UCHAR)(Divisor & 0x00FF)); SER16550_WRITE8(Device, Ser16550DivisorHigh, (UCHAR)((Divisor >> 8) & 0x00FF)); // // Enable the FIFOs. // SER16550_WRITE8(Device, Ser16550LineControl, 0); Value = SER16550_FIFO_CONTROL_ENABLE; SER16550_WRITE8(Device, Ser16550FifoControl, Value); // // Figure out the appropriate line control register value. // Value = 0; switch (TerminalControlFlags & TERMINAL_CONTROL_CHARACTER_SIZE_MASK) { case TERMINAL_CONTROL_5_BITS_PER_CHARACTER: Value |= SER16550_LINE_CONTROL_5_DATA_BITS; break; case TERMINAL_CONTROL_6_BITS_PER_CHARACTER: Value |= SER16550_LINE_CONTROL_6_DATA_BITS; break; case TERMINAL_CONTROL_7_BITS_PER_CHARACTER: Value |= SER16550_LINE_CONTROL_7_DATA_BITS; break; case TERMINAL_CONTROL_8_BITS_PER_CHARACTER: Value |= SER16550_LINE_CONTROL_8_DATA_BITS; break; default: return STATUS_NOT_SUPPORTED; } if ((TerminalControlFlags & TERMINAL_CONTROL_2_STOP_BITS) != 0) { Value |= SER16550_LINE_CONTROL_2_STOP_BITS; } if ((TerminalControlFlags & TERMINAL_CONTROL_ENABLE_PARITY) != 0) { Value |= SER16550_LINE_CONTROL_PARITY_ENABLE | SER16550_LINE_CONTROL_SET_PARITY; if ((TerminalControlFlags & TERMINAL_CONTROL_ODD_PARITY) == 0) { Value |= SER16550_LINE_CONTROL_EVEN_PARITY; } } // // Write the line control, which also flips the divisor registers back to // their normal registers. // SER16550_WRITE8(Device, Ser16550LineControl, Value); // // Initialize the modem control register, which includes flow control // (currently disabled). // Value = 0; SER16550_WRITE8(Device, Ser16550ModemControl, Value); // // Initialize the FIFO size. // Device->TransmitFifoSize = Ser16550pGetFifoSize(Device); // // Enable interrupts. // Device->InterruptEnable = SER16550_INTERRUPT_ENABLE_RX_DATA | SER16550_INTERRUPT_ENABLE_RX_STATUS; SER16550_WRITE8(Device, Ser16550InterruptEnable, Device->InterruptEnable); return STATUS_SUCCESS; } ULONG Ser16550pGetFifoSize ( PSER16550_CHILD Device ) /*++ Routine Description: This routine determines the size of the serial port FIFO. Arguments: Device - Supplies a pointer to the device. Return Value: Returns the FIFO size. --*/ { UCHAR DivisorHigh; UCHAR DivisorLow; ULONG Index; UCHAR LineControl; UCHAR ModemControl; UCHAR Value; LineControl = SER16550_READ8(Device, Ser16550LineControl); SER16550_WRITE8(Device, Ser16550LineControl, 0); ModemControl = SER16550_READ8(Device, Ser16550ModemControl); Value = SER16550_FIFO_CONTROL_ENABLE | SER16550_FIFO_CONTROL_CLEAR_TRANSMIT | SER16550_FIFO_CONTROL_CLEAR_RECEIVE; SER16550_WRITE8(Device, Ser16550FifoControl, Value); SER16550_WRITE8(Device, Ser16550ModemControl, SER16550_MODEM_CONTROL_LOOPBACK); Value = SER16550_LINE_CONTROL_DIVISOR_LATCH; SER16550_WRITE8(Device, Ser16550LineControl, Value); DivisorLow = SER16550_READ8(Device, Ser16550DivisorLow); DivisorHigh = SER16550_READ8(Device, Ser16550DivisorHigh); SER16550_WRITE8(Device, Ser16550DivisorLow, 1); SER16550_WRITE8(Device, Ser16550DivisorHigh, 0); Value = SER16550_LINE_CONTROL_8_DATA_BITS; SER16550_WRITE8(Device, Ser16550LineControl, Value); for (Index = 0; Index < SER16550_MAX_FIFO; Index += 1) { SER16550_WRITE8(Device, Ser16550Data, Index); } KeDelayExecution(FALSE, FALSE, 10 * MICROSECONDS_PER_MILLISECOND); for (Index = 0; Index < SER16550_MAX_FIFO; Index += 1) { if ((SER16550_READ8(Device, Ser16550LineStatus) & SER16550_LINE_STATUS_RX_READY) == 0) { break; } SER16550_READ8(Device, Ser16550Data); } SER16550_WRITE8(Device, Ser16550ModemControl, ModemControl); Value = SER16550_LINE_CONTROL_DIVISOR_LATCH; SER16550_WRITE8(Device, Ser16550LineControl, Value); SER16550_WRITE8(Device, Ser16550DivisorLow, DivisorLow); SER16550_WRITE8(Device, Ser16550DivisorHigh, DivisorHigh); SER16550_WRITE8(Device, Ser16550LineControl, LineControl); return Index; } VOID Ser16550pAddReference ( PSER16550_OBJECT Object ) /*++ Routine Description: This routine adds a reference on the given 16550 context. Arguments: Object - Supplies a pointer to the 16550 object. Return Value: None. --*/ { ULONG OldReferenceCount; OldReferenceCount = RtlAtomicAdd32(&(Object->ReferenceCount), 1); ASSERT(OldReferenceCount < 0x10000000); return; } VOID Ser16550pReleaseReference ( PSER16550_OBJECT Object ) /*++ Routine Description: This routine releases a reference on a 16550 object. Arguments: Object - Supplies a pointer to the 16550 object. Return Value: None. --*/ { ULONG OldReferenceCount; OldReferenceCount = RtlAtomicAdd32(&(Object->ReferenceCount), (ULONG)-1); ASSERT((OldReferenceCount != 0) && (OldReferenceCount < 0x10000000)); if (OldReferenceCount == 1) { Ser16550pDestroyDevice(Object); } return; } VOID Ser16550pDestroyDevice ( PSER16550_OBJECT Object ) /*++ Routine Description: This routine destroys a 16550 object. Arguments: Object - Supplies a pointer to the 16550 object. Return Value: None. --*/ { PSER16550_CHILD Child; PSER16550_PARENT Parent; switch (Object->Type) { case Ser16550ObjectParent: Parent = PARENT_STRUCTURE(Object, SER16550_PARENT, Header); if (Parent->ChildDevices != NULL) { MmFreePagedPool(Parent->ChildDevices); Parent->ChildDevices = NULL; } if (Parent->ChildObjects != NULL) { MmFreePagedPool(Parent->ChildObjects); Parent->ChildObjects = NULL; } Parent->Header.Type = Ser16550ObjectInvalid; MmFreeNonPagedPool(Parent); break; case Ser16550ObjectChild: Child = PARENT_STRUCTURE(Object, SER16550_CHILD, Header); Child->Header.Type = Ser16550ObjectInvalid; if (Child->Terminal != NULL) { IoTerminalSetDevice(Child->Terminal, NULL); IoClose(Child->Terminal); Child->Terminal = NULL; } if ((Child->MappedAddress != NULL) && (Child->ShouldUnmap != FALSE)) { ASSERT(Child->MappedSize != 0); MmUnmapAddress(Child->MappedAddress, Child->MappedSize); Child->MappedAddress = NULL; Child->MappedSize = 0; } if (Child->TransmitBuffer != NULL) { MmFreeNonPagedPool(Child->TransmitBuffer); Child->TransmitBuffer = NULL; Child->TransmitSize = 0; Child->TransmitStart = 0; Child->TransmitEnd = 0; } if (Child->ReceiveBuffer != NULL) { MmFreeNonPagedPool(Child->ReceiveBuffer); Child->ReceiveBuffer = NULL; Child->ReceiveSize = 0; Child->ReceiveStart = 0; Child->ReceiveEnd = 0; } if (Child->TransmitLock != NULL) { KeDestroyQueuedLock(Child->TransmitLock); Child->TransmitLock = NULL; } if (Child->ReceiveLock != NULL) { KeDestroyQueuedLock(Child->ReceiveLock); Child->ReceiveLock = NULL; } if (Child->TransmitReady == NULL) { KeDestroyEvent(Child->TransmitReady); Child->TransmitReady = NULL; } if (Child->ReceiveReady == NULL) { KeDestroyEvent(Child->ReceiveReady); Child->ReceiveReady = NULL; } break; default: ASSERT(FALSE); return; } return; } UCHAR Ser16550pReadIo8 ( PSER16550_CHILD Device, SER16550_REGISTER Register ) /*++ Routine Description: This routine reads an I/O port based 16550 register. Arguments: Device - Supplies a pointer to the device context. Register - Supplies the register to read. Return Value: Returns the value at the register. --*/ { USHORT Port; Port = Device->IoPortAddress + SER16550_REGISTER_OFFSET(Device, Register); return HlIoPortInByte(Port); } VOID Ser16550pWriteIo8 ( PSER16550_CHILD Device, SER16550_REGISTER Register, UCHAR Value ) /*++ Routine Description: This routine writes to an I/O port based 16550 register. Arguments: Device - Supplies a pointer to the device context. Register - Supplies the register to write to. Value - Supplies the value to write. Return Value: None. --*/ { USHORT Port; Port = Device->IoPortAddress + SER16550_REGISTER_OFFSET(Device, Register); HlIoPortOutByte(Port, Value); return; } UCHAR Ser16550pReadMemory8 ( PSER16550_CHILD Device, SER16550_REGISTER Register ) /*++ Routine Description: This routine reads a memory-based 16550 register. Arguments: Device - Supplies a pointer to the device context. Register - Supplies the register to read. Return Value: Returns the value at the register. --*/ { PVOID Address; Address = Device->MappedAddress + SER16550_REGISTER_OFFSET(Device, Register); return HlReadRegister8(Address); } VOID Ser16550pWriteMemory8 ( PSER16550_CHILD Device, SER16550_REGISTER Register, UCHAR Value ) /*++ Routine Description: This routine writes to a memory-based 16550 register. Arguments: Device - Supplies a pointer to the device context. Register - Supplies the register to write to. Value - Supplies the value to write. Return Value: None. --*/ { PVOID Address; Address = Device->MappedAddress + SER16550_REGISTER_OFFSET(Device, Register); HlWriteRegister8(Address, Value); return; }