/*++ Copyright (c) 1990, 1991, 1992, 1993 Microsoft Corporation Module Name : serial.h Abstract: Type definitions and data for the serial port driver Author: Anthony V. Ercolano April 8, 1991 Revision History: --*/ #ifdef POOL_TAGGING #undef ExAllocatePool #undef ExAllocatePoolWithQuota #define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'XMOC') #define ExAllocatePoolWithQuota(a,b) ExAllocatePoolWithQuotaTag(a,b,'XMOC') #endif #if DBG #define SERDIAG1 ((ULONG)0x00000001) #define SERDIAG2 ((ULONG)0x00000002) #define SERDIAG3 ((ULONG)0x00000004) #define SERDIAG4 ((ULONG)0x00000008) #define SERDIAG5 ((ULONG)0x00000010) #define SERIRPPATH ((ULONG)0x00000020) #define SERFLOW ((ULONG)0x20000000) #define SERERRORS ((ULONG)0x40000000) #define SERBUGCHECK ((ULONG)0x80000000) extern ULONG SerialDebugLevel; #define SerialDump(LEVEL,STRING) \ do { \ ULONG _level = (LEVEL); \ if (SerialDebugLevel & _level) { \ DbgPrint STRING; \ } \ if (_level == SERBUGCHECK) { \ ASSERT(FALSE); \ } \ } while (0) #else #define SerialDump(LEVEL,STRING) do {NOTHING;} while (0) #endif // // This define gives the default Object directory // that we should use to insert the symbolic links // between the NT device name and namespace used by // that object directory. #define DEFAULT_DIRECTORY L"DosDevices" // // For the above directory, the serial port will // use the following name as the suffix of the serial // ports for that directory. It will also append // a number onto the end of the name. That number // will start at 1. #define DEFAULT_SERIAL_NAME L"COM" // // // This define gives the default NT name for // for serial ports detected by the firmware. // This name will be appended to Device prefix // with a number following it. The number is // incremented each time encounter a serial // port detected by the firmware. Note that // on a system with multiple busses, this means // that the first port on a bus is not necessarily // \Device\Serial0. // #define DEFAULT_NT_SUFFIX L"Serial" // // This value - which could be redefined at compile // time, define the stride between registers // #if !defined(SERIAL_REGISTER_STRIDE) #define SERIAL_REGISTER_STRIDE 1 #endif // // Offsets from the base register address of the // various registers for the 8250 family of UARTS. // #define RECEIVE_BUFFER_REGISTER ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE)) #define TRANSMIT_HOLDING_REGISTER ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE)) #define INTERRUPT_ENABLE_REGISTER ((ULONG)((0x01)*SERIAL_REGISTER_STRIDE)) #define INTERRUPT_IDENT_REGISTER ((ULONG)((0x02)*SERIAL_REGISTER_STRIDE)) #define FIFO_CONTROL_REGISTER ((ULONG)((0x02)*SERIAL_REGISTER_STRIDE)) #define LINE_CONTROL_REGISTER ((ULONG)((0x03)*SERIAL_REGISTER_STRIDE)) #define MODEM_CONTROL_REGISTER ((ULONG)((0x04)*SERIAL_REGISTER_STRIDE)) #define LINE_STATUS_REGISTER ((ULONG)((0x05)*SERIAL_REGISTER_STRIDE)) #define MODEM_STATUS_REGISTER ((ULONG)((0x06)*SERIAL_REGISTER_STRIDE)) #define DIVISOR_LATCH_LSB ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE)) #define DIVISOR_LATCH_MSB ((ULONG)((0x01)*SERIAL_REGISTER_STRIDE)) #define SERIAL_REGISTER_SPAN ((ULONG)(7*SERIAL_REGISTER_STRIDE)) // // If we have an interrupt status register this is its assumed // length. // #define SERIAL_STATUS_LENGTH ((ULONG)(1*SERIAL_REGISTER_STRIDE)) // // Bitmask definitions for accessing the 8250 device registers. // // // These bits define the number of data bits trasmitted in // the Serial Data Unit (SDU - Start,data, parity, and stop bits) // #define SERIAL_DATA_LENGTH_5 0x00 #define SERIAL_DATA_LENGTH_6 0x01 #define SERIAL_DATA_LENGTH_7 0x02 #define SERIAL_DATA_LENGTH_8 0x03 // // These masks define the interrupts that can be enabled or disabled. // // // This interrupt is used to notify that there is new incomming // data available. The SERIAL_RDA interrupt is enabled by this bit. // #define SERIAL_IER_RDA 0x01 // // This interrupt is used to notify that there is space available // in the transmitter for another character. The SERIAL_THR // interrupt is enabled by this bit. // #define SERIAL_IER_THR 0x02 // // This interrupt is used to notify that some sort of error occured // with the incomming data. The SERIAL_RLS interrupt is enabled by // this bit. #define SERIAL_IER_RLS 0x04 // // This interrupt is used to notify that some sort of change has // taken place in the modem control line. The SERIAL_MS interrupt is // enabled by this bit. // #define SERIAL_IER_MS 0x08 // // These masks define the values of the interrupt identification // register. The low bit must be clear in the interrupt identification // register for any of these interrupts to be valid. The interrupts // are defined in priority order, with the highest value being most // important. See above for a description of what each interrupt // implies. // #define SERIAL_IIR_RLS 0x06 #define SERIAL_IIR_RDA 0x04 #define SERIAL_IIR_CTI 0x0c #define SERIAL_IIR_THR 0x02 #define SERIAL_IIR_MS 0x00 // // This bit mask get the value of the high two bits of the // interrupt id register. If this is a 16550 class chip // these bits will be a one if the fifo's are enbled, otherwise // they will always be zero. // #define SERIAL_IIR_FIFOS_ENABLED 0xc0 // // If the low bit is logic one in the interrupt identification register // this implies that *NO* interrupts are pending on the device. // #define SERIAL_IIR_NO_INTERRUPT_PENDING 0x01 // // These masks define access to the fifo control register. // // // Enabling this bit in the fifo control register will turn // on the fifos. If the fifos are enabled then the high two // bits of the interrupt id register will be set to one. Note // that this only occurs on a 16550 class chip. If the high // two bits in the interrupt id register are not one then // we know we have a lower model chip. // // #define SERIAL_FCR_ENABLE ((UCHAR)0x01) #define SERIAL_FCR_RCVR_RESET ((UCHAR)0x02) #define SERIAL_FCR_TXMT_RESET ((UCHAR)0x04) // // This set of values define the high water marks (when the // interrupts trip) for the receive fifo. // #define SERIAL_1_BYTE_HIGH_WATER ((UCHAR)0x00) #define SERIAL_4_BYTE_HIGH_WATER ((UCHAR)0x40) #define SERIAL_8_BYTE_HIGH_WATER ((UCHAR)0x80) #define SERIAL_14_BYTE_HIGH_WATER ((UCHAR)0xc0) // // These masks define access to the line control register. // // // This defines the bit used to control the definition of the "first" // two registers for the 8250. These registers are the input/output // register and the interrupt enable register. When the DLAB bit is // enabled these registers become the least significant and most // significant bytes of the divisor value. // #define SERIAL_LCR_DLAB 0x80 // // This defines the bit used to control whether the device is sending // a break. When this bit is set the device is sending a space (logic 0). // // Most protocols will assume that this is a hangup. // #define SERIAL_LCR_BREAK 0x40 // // These defines are used to set the line control register. // #define SERIAL_5_DATA ((UCHAR)0x00) #define SERIAL_6_DATA ((UCHAR)0x01) #define SERIAL_7_DATA ((UCHAR)0x02) #define SERIAL_8_DATA ((UCHAR)0x03) #define SERIAL_DATA_MASK ((UCHAR)0x03) #define SERIAL_1_STOP ((UCHAR)0x00) #define SERIAL_1_5_STOP ((UCHAR)0x04) // Only valid for 5 data bits #define SERIAL_2_STOP ((UCHAR)0x04) // Not valid for 5 data bits #define SERIAL_STOP_MASK ((UCHAR)0x04) #define SERIAL_NONE_PARITY ((UCHAR)0x00) #define SERIAL_ODD_PARITY ((UCHAR)0x08) #define SERIAL_EVEN_PARITY ((UCHAR)0x18) #define SERIAL_MARK_PARITY ((UCHAR)0x28) #define SERIAL_SPACE_PARITY ((UCHAR)0x38) #define SERIAL_PARITY_MASK ((UCHAR)0x38) // // These masks define access the modem control register. // // // This bit controls the data terminal ready (DTR) line. When // this bit is set the line goes to logic 0 (which is then inverted // by normal hardware). This is normally used to indicate that // the device is available to be used. Some odd hardware // protocols (like the kernel debugger) use this for handshaking // purposes. // #define SERIAL_MCR_DTR 0x01 // // This bit controls the ready to send (RTS) line. When this bit // is set the line goes to logic 0 (which is then inverted by the normal // hardware). This is used for hardware handshaking. It indicates that // the hardware is ready to send data and it is waiting for the // receiving end to set clear to send (CTS). // #define SERIAL_MCR_RTS 0x02 // // This bit is used for general purpose output. // #define SERIAL_MCR_OUT1 0x04 // // This bit is used for general purpose output. // #define SERIAL_MCR_OUT2 0x08 // // This bit controls the loopback testing mode of the device. Basically // the outputs are connected to the inputs (and vice versa). // #define SERIAL_MCR_LOOP 0x10 // // These masks define access to the line status register. The line // status register contains information about the status of data // transfer. The first five bits deal with receive data and the // last two bits deal with transmission. An interrupt is generated // whenever bits 1 through 4 in this register are set. // // // This bit is the data ready indicator. It is set to indicate that // a complete character has been received. This bit is cleared whenever // the receive buffer register has been read. // #define SERIAL_LSR_DR 0x01 // // This is the overrun indicator. It is set to indicate that the receive // buffer register was not read befor a new character was transferred // into the buffer. This bit is cleared when this register is read. // #define SERIAL_LSR_OE 0x02 // // This is the parity error indicator. It is set whenever the hardware // detects that the incoming serial data unit does not have the correct // parity as defined by the parity select in the line control register. // This bit is cleared by reading this register. // #define SERIAL_LSR_PE 0x04 // // This is the framing error indicator. It is set whenever the hardware // detects that the incoming serial data unit does not have a valid // stop bit. This bit is cleared by reading this register. // #define SERIAL_LSR_FE 0x08 // // This is the break interrupt indicator. It is set whenever the data // line is held to logic 0 for more than the amount of time it takes // to send one serial data unit. This bit is cleared whenever the // this register is read. // #define SERIAL_LSR_BI 0x10 // // This is the transmit holding register empty indicator. It is set // to indicate that the hardware is ready to accept another character // for transmission. This bit is cleared whenever a character is // written to the transmit holding register. // #define SERIAL_LSR_THRE 0x20 // // This bit is the transmitter empty indicator. It is set whenever the // transmit holding buffer is empty and the transmit shift register // (a non-software accessable register that is used to actually put // the data out on the wire) is empty. Basically this means that all // data has been sent. It is cleared whenever the transmit holding or // the shift registers contain data. // #define SERIAL_LSR_TEMT 0x40 // // This bit indicates that there is at least one error in the fifo. // The bit will not be turned off until there are no more errors // in the fifo. // #define SERIAL_LSR_FIFOERR 0x80 // // These masks are used to access the modem status register. // Whenever one of the first four bits in the modem status // register changes state a modem status interrupt is generated. // // // This bit is the delta clear to send. It is used to indicate // that the clear to send bit (in this register) has *changed* // since this register was last read by the CPU. // #define SERIAL_MSR_DCTS 0x01 // // This bit is the delta data set ready. It is used to indicate // that the data set ready bit (in this register) has *changed* // since this register was last read by the CPU. // #define SERIAL_MSR_DDSR 0x02 // // This is the trailing edge ring indicator. It is used to indicate // that the ring indicator input has changed from a low to high state. // #define SERIAL_MSR_TERI 0x04 // // This bit is the delta data carrier detect. It is used to indicate // that the data carrier bit (in this register) has *changed* // since this register was last read by the CPU. // #define SERIAL_MSR_DDCD 0x08 // // This bit contains the (complemented) state of the clear to send // (CTS) line. // #define SERIAL_MSR_CTS 0x10 // // This bit contains the (complemented) state of the data set ready // (DSR) line. // #define SERIAL_MSR_DSR 0x20 // // This bit contains the (complemented) state of the ring indicator // (RI) line. // #define SERIAL_MSR_RI 0x40 // // This bit contains the (complemented) state of the data carrier detect // (DCD) line. // #define SERIAL_MSR_DCD 0x80 // // This should be more than enough space to hold then // numeric suffix of the device name. // #define DEVICE_NAME_DELTA 20 // // Up to 16 Ports Per card. However for sixteen // port cards the interrupt status register must me // the indexing kind rather then the bitmask kind. // // #define SERIAL_MAX_PORTS_INDEXED (16) #define SERIAL_MAX_PORTS_NONINDEXED (8) typedef struct _CONFIG_DATA { LIST_ENTRY ConfigList; LIST_ENTRY SameInterruptStatus; LIST_ENTRY SameInterrupt; UNICODE_STRING ObjectDirectory; UNICODE_STRING NtNameForPort; UNICODE_STRING SymbolicLinkName; PHYSICAL_ADDRESS Controller; PHYSICAL_ADDRESS InterruptStatus; ULONG SpanOfController; ULONG SpanOfInterruptStatus; ULONG PortIndex; ULONG ClockRate; ULONG BusNumber; ULONG AddressSpace; ULONG DisablePort; ULONG ForceFifoEnable; ULONG RxFIFO; ULONG TxFIFO; ULONG PermitShare; ULONG PermitSystemWideShare; ULONG LogFifo; ULONG MaskInverted; KINTERRUPT_MODE InterruptMode; INTERFACE_TYPE InterfaceType; ULONG OriginalVector; ULONG OriginalIrql; ULONG Indexed; BOOLEAN Jensen; } CONFIG_DATA,*PCONFIG_DATA; // // Default xon/xoff characters. // #define SERIAL_DEF_XON 0x11 #define SERIAL_DEF_XOFF 0x13 // // Reasons that recption may be held up. // #define SERIAL_RX_DTR ((ULONG)0x01) #define SERIAL_RX_XOFF ((ULONG)0x02) #define SERIAL_RX_RTS ((ULONG)0x04) #define SERIAL_RX_DSR ((ULONG)0x08) // // Reasons that transmission may be held up. // #define SERIAL_TX_CTS ((ULONG)0x01) #define SERIAL_TX_DSR ((ULONG)0x02) #define SERIAL_TX_DCD ((ULONG)0x04) #define SERIAL_TX_XOFF ((ULONG)0x08) #define SERIAL_TX_BREAK ((ULONG)0x10) // // These values are used by the routines that can be used // to complete a read (other than interval timeout) to indicate // to the interval timeout that it should complete. // #define SERIAL_COMPLETE_READ_CANCEL ((LONG)-1) #define SERIAL_COMPLETE_READ_TOTAL ((LONG)-2) #define SERIAL_COMPLETE_READ_COMPLETE ((LONG)-3) typedef struct _SERIAL_DEVICE_EXTENSION { // // This holds the isr that should be called from our own // dispatching isr for "cards" that are trying to share the // same interrupt. // PKSERVICE_ROUTINE TopLevelOurIsr; // // This holds the context that should be used when we // call the above service routine. // PVOID TopLevelOurIsrContext; // // This links together all of the different "cards" that are // trying to share the same interrupt of a non-mca machine. // LIST_ENTRY TopLevelSharers; // // This circular doubly linked list links together all // devices that are using the same interrupt object. // NOTE: This does not mean that they are using the // same interrupt "dispatching" routine. // LIST_ENTRY CommonInterruptObject; // // For reporting resource usage, we keep around the physical // address we got from the registry. // PHYSICAL_ADDRESS OriginalController; // // For reporting resource usage, we keep around the physical // address we got from the registry. // PHYSICAL_ADDRESS OriginalInterruptStatus; // // This value is set by the read code to hold the time value // used for read interval timing. We keep it in the extension // so that the interval timer dpc routine determine if the // interval time has passed for the IO. // LARGE_INTEGER IntervalTime; // // These two values hold the "constant" time that we should use // to delay for the read interval time. // LARGE_INTEGER ShortIntervalAmount; LARGE_INTEGER LongIntervalAmount; // // This holds the value that we use to determine if we should use // the long interval delay or the short interval delay. // LARGE_INTEGER CutOverAmount; // // This holds the system time when we last time we had // checked that we had actually read characters. Used // for interval timing. // LARGE_INTEGER LastReadTime; // // We keep a pointer around to our device name for dumps // and for creating "external" symbolic links to this // device. // UNICODE_STRING DeviceName; // // This points to the object directory that we will place // a symbolic link to our device name. // UNICODE_STRING ObjectDirectory; // // This points to the device name for this device // sans device prefix. // UNICODE_STRING NtNameForPort; // // This points to the symbolic link name that will be // linked to the actual nt device name. // UNICODE_STRING SymbolicLinkName; // // This points the the delta time that we should use to // delay for interval timing. // PLARGE_INTEGER IntervalTimeToUse; // // Points to the device object that contains // this device extension. // PDEVICE_OBJECT DeviceObject; // // After initialization of the driver is complete, this // will either be NULL or point to the routine that the // kernel will call when an interrupt occurs. // // If the pointer is null then this is part of a list // of ports that are sharing an interrupt and this isn't // the first port that we configured for this interrupt. // // If the pointer is non-null then this routine has some // kind of structure that will "eventually" get us into // the real serial isr with a pointer to this device extension. // // NOTE: On an MCA bus (except for multiport cards) this // is always a pointer to the "real" serial isr. PKSERVICE_ROUTINE OurIsr; // // This will generally point right to this device extension. // // However, when the port that this device extension is // "managing" was the first port initialized on a chain // of ports that were trying to share an interrupt, this // will point to a structure that will enable dispatching // to any port on the chain of sharers of this interrupt. // PVOID OurIsrContext; // // The base address for the set of device registers // of the serial port. // PUCHAR Controller; // // The base address for interrupt status register. // This is only defined in the root extension. // PUCHAR InterruptStatus; // // Points to the interrupt object for used by this device. // PKINTERRUPT Interrupt; // // This list head is used to contain the time ordered list // of read requests. Access to this list is protected by // the global cancel spinlock. // LIST_ENTRY ReadQueue; // // This list head is used to contain the time ordered list // of write requests. Access to this list is protected by // the global cancel spinlock. // LIST_ENTRY WriteQueue; // // This list head is used to contain the time ordered list // of set and wait mask requests. Access to this list is protected by // the global cancel spinlock. // LIST_ENTRY MaskQueue; // // Holds the serialized list of purge requests. // LIST_ENTRY PurgeQueue; // // This points to the irp that is currently being processed // for the read queue. This field is initialized by the open to // NULL. // // This value is only set at dispatch level. It may be // read at interrupt level. // PIRP CurrentReadIrp; // // This points to the irp that is currently being processed // for the write queue. // // This value is only set at dispatch level. It may be // read at interrupt level. // PIRP CurrentWriteIrp; // // Points to the irp that is currently being processed to // affect the wait mask operations. // PIRP CurrentMaskIrp; // // Points to the irp that is currently being processed to // purge the read/write queues and buffers. // PIRP CurrentPurgeIrp; // // Points to the current irp that is waiting on a comm event. // PIRP CurrentWaitIrp; // // Points to the irp that is being used to send an immediate // character. // PIRP CurrentImmediateIrp; // // Points to the irp that is being used to count the number // of characters received after an xoff (as currently defined // by the IOCTL_SERIAL_XOFF_COUNTER ioctl) is sent. // PIRP CurrentXoffIrp; // // Holds the number of bytes remaining in the current write // irp. // // This location is only accessed while at interrupt level. // ULONG WriteLength; // // Holds a pointer to the current character to be sent in // the current write. // // This location is only accessed while at interrupt level. // PUCHAR WriteCurrentChar; // // This is a buffer for the read processing. // // The buffer works as a ring. When the character is read from // the device it will be place at the end of the ring. // // Characters are only placed in this buffer at interrupt level // although character may be read at any level. The pointers // that manage this buffer may not be updated except at interrupt // level. // PUCHAR InterruptReadBuffer; // // This is a pointer to the first character of the buffer into // which the interrupt service routine is copying characters. // PUCHAR ReadBufferBase; // // This is a count of the number of characters in the interrupt // buffer. This value is set and read at interrupt level. Note // that this value is only *incremented* at interrupt level so // it is safe to read it at any level. When characters are // copied out of the read buffer, this count is decremented by // a routine that synchronizes with the ISR. // ULONG CharsInInterruptBuffer; // // Points to the first available position for a newly received // character. This variable is only accessed at interrupt level and // buffer initialization code. // PUCHAR CurrentCharSlot; // // This variable is used to contain the last available position // in the read buffer. It is updated at open and at interrupt // level when switching between the users buffer and the interrupt // buffer. // PUCHAR LastCharSlot; // // This marks the first character that is available to satisfy // a read request. Note that while this always points to valid // memory, it may not point to a character that can be sent to // the user. This can occur when the buffer is empty. // PUCHAR FirstReadableChar; // // Pointer to the lock variable returned for this extension when // locking down the driver // PVOID LockPtr; // // This variable holds the size of whatever buffer we are currently // using. // ULONG BufferSize; // // This variable holds .8 of BufferSize. We don't want to recalculate // this real often - It's needed when so that an application can be // "notified" that the buffer is getting full. // ULONG BufferSizePt8; // // This value holds the number of characters desired for a // particular read. It is initially set by read length in the // IRP. It is decremented each time more characters are placed // into the "users" buffer buy the code that reads characters // out of the typeahead buffer into the users buffer. If the // typeahead buffer is exhausted by the read, and the reads buffer // is given to the isr to fill, this value is becomes meaningless. // ULONG NumberNeededForRead; // // This mask will hold the bitmask sent down via the set mask // ioctl. It is used by the interrupt service routine to determine // if the occurence of "events" (in the serial drivers understanding // of the concept of an event) should be noted. // ULONG IsrWaitMask; // // This mask will always be a subset of the IsrWaitMask. While // at device level, if an event occurs that is "marked" as interesting // in the IsrWaitMask, the driver will turn on that bit in this // history mask. The driver will then look to see if there is a // request waiting for an event to occur. If there is one, it // will copy the value of the history mask into the wait irp, zero // the history mask, and complete the wait irp. If there is no // waiting request, the driver will be satisfied with just recording // that the event occured. If a wait request should be queued, // the driver will look to see if the history mask is non-zero. If // it is non-zero, the driver will copy the history mask into the // irp, zero the history mask, and then complete the irp. // ULONG HistoryMask; // // This is a pointer to the where the history mask should be // placed when completing a wait. It is only accessed at // device level. // // We have a pointer here to assist us to synchronize completing a wait. // If this is non-zero, then we have wait outstanding, and the isr still // knows about it. We make this pointer null so that the isr won't // attempt to complete the wait. // // We still keep a pointer around to the wait irp, since the actual // pointer to the wait irp will be used for the "common" irp completion // path. // ULONG *IrpMaskLocation; // // This mask holds all of the reason that transmission // is not proceeding. Normal transmission can not occur // if this is non-zero. // // This is only written from interrupt level. // This could be (but is not) read at any level. // ULONG TXHolding; // // This mask holds all of the reason that reception // is not proceeding. Normal reception can not occur // if this is non-zero. // // This is only written from interrupt level. // This could be (but is not) read at any level. // ULONG RXHolding; // // This holds the reasons that the driver thinks it is in // an error state. // // This is only written from interrupt level. // This could be (but is not) read at any level. // ULONG ErrorWord; // // This keeps a total of the number of characters that // are in all of the "write" irps that the driver knows // about. It is only accessed with the cancel spinlock // held. // ULONG TotalCharsQueued; // // This holds a count of the number of characters read // the last time the interval timer dpc fired. It // is a long (rather than a ulong) since the other read // completion routines use negative values to indicate // to the interval timer that it should complete the read // if the interval timer DPC was lurking in some DPC queue when // some other way to complete occurs. // LONG CountOnLastRead; // // This is a count of the number of characters read by the // isr routine. It is *ONLY* written at isr level. We can // read it at dispatch level. // ULONG ReadByIsr; // // This holds the current baud rate for the device. // ULONG CurrentBaud; // // This is the number of characters read since the XoffCounter // was started. This variable is only accessed at device level. // If it is greater than zero, it implies that there is an // XoffCounter ioctl in the queue. // LONG CountSinceXoff; // // This ulong is incremented each time something trys to start // the execution path that tries to lower the RTS line when // doing transmit toggling. If it "bumps" into another path // (indicated by a false return value from queueing a dpc // and a TRUE return value tring to start a timer) it will // decrement the count. These increments and decrements // are all done at device level. Note that in the case // of a bump while trying to start the timer, we have to // go up to device level to do the decrement. // ULONG CountOfTryingToLowerRTS; // // This ULONG is used to keep track of the "named" (in ntddser.h) // baud rates that this particular device supports. // ULONG SupportedBauds; // // This value holds the span (in units of bytes) of the register // set controlling this port. This is constant over the life // of the port. // ULONG SpanOfController; // // This value holds the span (in units of bytes) of the interrupt // status register associated with this port. This is constant // over the life of the port. // ULONG SpanOfInterruptStatus; // // Hold the clock rate input to the serial part. // ULONG ClockRate; // // The number of characters to push out if a fifo is present. // ULONG TxFifoAmount; // // Set to indicate that it is ok to share interrupts within the device. // ULONG PermitShare; // // Holds the timeout controls for the device. This value // is set by the Ioctl processing. // // It should only be accessed under protection of the control // lock since more than one request can be in the control dispatch // routine at one time. // SERIAL_TIMEOUTS Timeouts; // // This holds the various characters that are used // for replacement on errors and also for flow control. // // They are only set at interrupt level. // SERIAL_CHARS SpecialChars; // // This structure holds the handshake and control flow // settings for the serial driver. // // It is only set at interrupt level. It can be // be read at any level with the control lock held. // SERIAL_HANDFLOW HandFlow; // // Holds performance statistics that applications can query. // Reset on each open. Only set at device level. // SERIALPERF_STATS PerfStats; // // This holds what we beleive to be the current value of // the line control register. // // It should only be accessed under protection of the control // lock since more than one request can be in the control dispatch // routine at one time. // UCHAR LineControl; // // We keep track of whether the somebody has the device currently // opened with a simple boolean. We need to know this so that // spurious interrupts from the device (especially during initialization) // will be ignored. This value is only accessed in the ISR and // is only set via synchronization routines. We may be able // to get rid of this boolean when the code is more fleshed out. // BOOLEAN DeviceIsOpened; // // Set at intialization to indicate that on the current // architecture we need to unmap the base register address // when we unload the driver. // BOOLEAN UnMapRegisters; // // Set at intialization to indicate that on the current // architecture we need to unmap the interrupt status address // when we unload the driver. // BOOLEAN UnMapStatus; // // This is only accessed at interrupt level. It keeps track // of whether the holding register is empty. // BOOLEAN HoldingEmpty; // // This variable is only accessed at interrupt level. It // indicates that we want to transmit a character immediately. // That is - in front of any characters that could be transmitting // from a normal write. // BOOLEAN TransmitImmediate; // // This variable is only accessed at interrupt level. Whenever // a wait is initiated this variable is set to false. // Whenever any kind of character is written it is set to true. // Whenever the write queue is found to be empty the code that // is processing that completing irp will synchonize with the interrupt. // If this synchronization code finds that the variable is true and that // there is a wait on the transmit queue being empty then it is // certain that the queue was emptied and that it has happened since // the wait was initiated. // BOOLEAN EmptiedTransmit; // // This simply indicates that the port associated with this // extension is part of a multiport card. // BOOLEAN PortOnAMultiportCard; // // We keep the following values around so that we can connect // to the interrupt and report resources after the configuration // record is gone. // ULONG Vector; KIRQL Irql; ULONG OriginalVector; ULONG OriginalIrql; KINTERRUPT_MODE InterruptMode; KAFFINITY ProcessorAffinity; ULONG AddressSpace; ULONG BusNumber; INTERFACE_TYPE InterfaceType; // // We hold the character that should be transmitted immediately. // // Note that we can't use this to determine whether there is // a character to send because the character to send could be // zero. // UCHAR ImmediateChar; // // This holds the mask that will be used to mask off unwanted // data bits of the received data (valid data bits can be 5,6,7,8) // The mask will normally be 0xff. This is set while the control // lock is held since it wouldn't have adverse effects on the // isr if it is changed in the middle of reading characters. // (What it would do to the app is another question - but then // the app asked the driver to do it.) // UCHAR ValidDataMask; // // The application can turn on a mode,via the // IOCTL_SERIAL_LSRMST_INSERT ioctl, that will cause the // serial driver to insert the line status or the modem // status into the RX stream. The parameter with the ioctl // is a pointer to a UCHAR. If the value of the UCHAR is // zero, then no insertion will ever take place. If the // value of the UCHAR is non-zero (and not equal to the // xon/xoff characters), then the serial driver will insert. // UCHAR EscapeChar; // // These two booleans are used to indicate to the isr transmit // code that it should send the xon or xoff character. They are // only accessed at open and at interrupt level. // BOOLEAN SendXonChar; BOOLEAN SendXoffChar; // // This boolean will be true if a 16550 is present *and* enabled. // BOOLEAN FifoPresent; // // This denotes that this particular port is an on the motherboard // port for the Jensen hardware. On these ports the OUT2 bit // which is used to enable/disable interrupts is always hight. // BOOLEAN Jensen; // // This is the water mark that the rxfifo should be // set to when the fifo is turned on. This is not the actual // value, but the encoded value that goes into the register. // UCHAR RxFifoTrigger; // // Says whether this device can share interrupts with devices // other than serial devices. // BOOLEAN InterruptShareable; // // Records whether we actually created the symbolic link name // at driver load time. If we didn't create it, we won't try // to distry it when we unload. // BOOLEAN CreatedSymbolicLink; // // We place all of the kernel and Io subsystem "opaque" structures // at the end of the extension. We don't care about their contents. // // // This lock will be used to protect various fields in // the extension that are set (& read) in the extension // by the io controls. // KSPIN_LOCK ControlLock; // // This points to a DPC used to complete read requests. // KDPC CompleteWriteDpc; // // This points to a DPC used to complete read requests. // KDPC CompleteReadDpc; // // This dpc is fired off if the timer for the total timeout // for the read expires. It will execute a dpc routine that // will cause the current read to complete. // // KDPC TotalReadTimeoutDpc; // // This dpc is fired off if the timer for the interval timeout // expires. If no more characters have been read then the // dpc routine will cause the read to complete. However, if // more characters have been read then the dpc routine will // resubmit the timer. // KDPC IntervalReadTimeoutDpc; // // This dpc is fired off if the timer for the total timeout // for the write expires. It will execute a dpc routine that // will cause the current write to complete. // // KDPC TotalWriteTimeoutDpc; // // This dpc is fired off if a comm error occurs. It will // execute a dpc routine that will cancel all pending reads // and writes. // KDPC CommErrorDpc; // // This dpc is fired off if an event occurs and there was // a irp waiting on that event. A dpc routine will execute // that completes the irp. // KDPC CommWaitDpc; // // This dpc is fired off when the transmit immediate char // character is given to the hardware. It will simply complete // the irp. // KDPC CompleteImmediateDpc; // // This dpc is fired off if the transmit immediate char // character times out. The dpc routine will "grab" the // irp from the isr and time it out. // KDPC TotalImmediateTimeoutDpc; // // This dpc is fired off if the timer used to "timeout" counting // the number of characters received after the Xoff ioctl is started // expired. // KDPC XoffCountTimeoutDpc; // // This dpc is fired off if the xoff counter actually runs down // to zero. // KDPC XoffCountCompleteDpc; // // This dpc is fired off only from device level to start off // a timer that will queue a dpc to check if the RTS line // should be lowered when we are doing transmit toggling. // KDPC StartTimerLowerRTSDpc; // // This dpc is fired off when a timer expires (after one // character time), so that code can be invoked that will // check to see if we should lower the RTS line when // doing transmit toggling. // KDPC PerhapsLowerRTSDpc; // // This is the kernal timer structure used to handle // total read request timing. // KTIMER ReadRequestTotalTimer; // // This is the kernal timer structure used to handle // interval read request timing. // KTIMER ReadRequestIntervalTimer; // // This is the kernal timer structure used to handle // total time request timing. // KTIMER WriteRequestTotalTimer; // // This is the kernal timer structure used to handle // total time request timing. // KTIMER ImmediateTotalTimer; // // This timer is used to timeout the xoff counter // io. // KTIMER XoffCountTimer; // // This timer is used to invoke a dpc one character time // after the timer is set. That dpc will be used to check // whether we should lower the RTS line if we are doing // transmit toggling. // KTIMER LowerRTSTimer; } SERIAL_DEVICE_EXTENSION,*PSERIAL_DEVICE_EXTENSION; // // When dealing with a multi-port device (that is possibly // daisy chained with other multi-port device), the interrupt // service routine will actually be a routine that determines // which port on which board is actually causing the interrupt. // // The following structure is used so that only one device // extension will actually need to connect to the interrupt. // The following structure which is passed to the interrupt // service routine contains the addresses of all of the // interrupt status registers (there will be multiple // status registers when multi-port cards are chained). It // will contain the addresses of all the extensions whose // devices are being serviced by this interrupt. // typedef struct _SERIAL_MULTIPORT_DISPATCH { PUCHAR InterruptStatus; PSERIAL_DEVICE_EXTENSION Extensions[SERIAL_MAX_PORTS_INDEXED]; ULONG MaskInverted; UCHAR UsablePortMask; } SERIAL_MULTIPORT_DISPATCH,*PSERIAL_MULTIPORT_DISPATCH; // // Sets the divisor latch register. The divisor latch register // is used to control the baud rate of the 8250. // // As with all of these routines it is assumed that it is called // at a safe point to access the hardware registers. In addition // it also assumes that the data is correct. // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // DesiredDivisor - The value to which the divisor latch register should // be set. // #define WRITE_DIVISOR_LATCH(BaseAddress,DesiredDivisor) \ do \ { \ PUCHAR Address = BaseAddress; \ SHORT Divisor = DesiredDivisor; \ UCHAR LineControl; \ LineControl = READ_PORT_UCHAR(Address+LINE_CONTROL_REGISTER); \ WRITE_PORT_UCHAR( \ Address+LINE_CONTROL_REGISTER, \ (UCHAR)(LineControl | SERIAL_LCR_DLAB) \ ); \ WRITE_PORT_UCHAR( \ Address+DIVISOR_LATCH_LSB, \ (UCHAR)(Divisor & 0xff) \ ); \ WRITE_PORT_UCHAR( \ Address+DIVISOR_LATCH_MSB, \ (UCHAR)((Divisor & 0xff00) >> 8) \ ); \ WRITE_PORT_UCHAR( \ Address+LINE_CONTROL_REGISTER, \ LineControl \ ); \ } while (0) // // Reads the divisor latch register. The divisor latch register // is used to control the baud rate of the 8250. // // As with all of these routines it is assumed that it is called // at a safe point to access the hardware registers. In addition // it also assumes that the data is correct. // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // DesiredDivisor - A pointer to the 2 byte word which will contain // the value of the divisor. // #define READ_DIVISOR_LATCH(BaseAddress,PDesiredDivisor) \ do \ { \ PUCHAR Address = BaseAddress; \ PSHORT PDivisor = PDesiredDivisor; \ UCHAR LineControl; \ UCHAR Lsb; \ UCHAR Msb; \ LineControl = READ_PORT_UCHAR(Address+LINE_CONTROL_REGISTER); \ WRITE_PORT_UCHAR( \ Address+LINE_CONTROL_REGISTER, \ (UCHAR)(LineControl | SERIAL_LCR_DLAB) \ ); \ Lsb = READ_PORT_UCHAR(Address+DIVISOR_LATCH_LSB); \ Msb = READ_PORT_UCHAR(Address+DIVISOR_LATCH_MSB); \ *PDivisor = Lsb; \ *PDivisor = *PDivisor | (((USHORT)Msb) << 8); \ WRITE_PORT_UCHAR( \ Address+LINE_CONTROL_REGISTER, \ LineControl \ ); \ } while (0) // // This macro reads the interrupt enable register. // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // #define READ_INTERRUPT_ENABLE(BaseAddress) \ (READ_PORT_UCHAR((BaseAddress)+INTERRUPT_ENABLE_REGISTER)) // // This macro writes the interrupt enable register. // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // Values - The values to write to the interrupt enable register. // #define WRITE_INTERRUPT_ENABLE(BaseAddress,Values) \ do \ { \ WRITE_PORT_UCHAR( \ BaseAddress+INTERRUPT_ENABLE_REGISTER, \ Values \ ); \ } while (0) // // This macro disables all interrupts on the hardware. // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // #define DISABLE_ALL_INTERRUPTS(BaseAddress) \ do \ { \ WRITE_INTERRUPT_ENABLE(BaseAddress,0); \ } while (0) // // This macro enables all interrupts on the hardware. // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // #define ENABLE_ALL_INTERRUPTS(BaseAddress) \ do \ { \ \ WRITE_INTERRUPT_ENABLE( \ (BaseAddress), \ (UCHAR)(SERIAL_IER_RDA | SERIAL_IER_THR | \ SERIAL_IER_RLS | SERIAL_IER_MS) \ ); \ \ } while (0) // // This macro reads the interrupt identification register // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // Note that this routine potententially quites a transmitter // empty interrupt. This is because one way that the transmitter // empty interrupt is cleared is to simply read the interrupt id // register. // // #define READ_INTERRUPT_ID_REG(BaseAddress) \ (READ_PORT_UCHAR((BaseAddress)+INTERRUPT_IDENT_REGISTER)) // // This macro reads the modem control register // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // #define READ_MODEM_CONTROL(BaseAddress) \ (READ_PORT_UCHAR((BaseAddress)+MODEM_CONTROL_REGISTER)) // // This macro reads the modem status register // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // #define READ_MODEM_STATUS(BaseAddress) \ (READ_PORT_UCHAR((BaseAddress)+MODEM_STATUS_REGISTER)) // // This macro reads a value out of the receive buffer // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // #define READ_RECEIVE_BUFFER(BaseAddress) \ (READ_PORT_UCHAR((BaseAddress)+RECEIVE_BUFFER_REGISTER)) // // This macro reads the line status register // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // #define READ_LINE_STATUS(BaseAddress) \ (READ_PORT_UCHAR((BaseAddress)+LINE_STATUS_REGISTER)) // // This macro writes the line control register // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // #define WRITE_LINE_CONTROL(BaseAddress,NewLineControl) \ do \ { \ WRITE_PORT_UCHAR( \ (BaseAddress)+LINE_CONTROL_REGISTER, \ (NewLineControl) \ ); \ } while (0) // // This macro reads the line control register // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // #define READ_LINE_CONTROL(BaseAddress) \ (READ_PORT_UCHAR((BaseAddress)+LINE_CONTROL_REGISTER)) // // This macro writes to the transmit register // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // TransmitChar - The character to send down the wire. // // #define WRITE_TRANSMIT_HOLDING(BaseAddress,TransmitChar) \ do \ { \ WRITE_PORT_UCHAR( \ (BaseAddress)+TRANSMIT_HOLDING_REGISTER, \ (TransmitChar) \ ); \ } while (0) // // This macro writes to the transmit FIFO register // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // TransmitChars - Pointer to the characters to send down the wire. // // TxN - number of charactes to send. // // #define WRITE_TRANSMIT_FIFO_HOLDING(BaseAddress,TransmitChars,TxN) \ do \ { \ WRITE_PORT_BUFFER_UCHAR( \ (BaseAddress)+TRANSMIT_HOLDING_REGISTER, \ (TransmitChars), \ (TxN) \ ); \ } while (0) // // This macro writes to the control register // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // ControlValue - The value to set the fifo control register too. // // #define WRITE_FIFO_CONTROL(BaseAddress,ControlValue) \ do \ { \ WRITE_PORT_UCHAR( \ (BaseAddress)+FIFO_CONTROL_REGISTER, \ (ControlValue) \ ); \ } while (0) // // This macro writes to the modem control register // // Arguments: // // BaseAddress - A pointer to the address from which the hardware // device registers are located. // // ModemControl - The control bits to send to the modem control. // // #define WRITE_MODEM_CONTROL(BaseAddress,ModemControl) \ do \ { \ WRITE_PORT_UCHAR( \ (BaseAddress)+MODEM_CONTROL_REGISTER, \ (ModemControl) \ ); \ } while (0)