You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2971 lines
96 KiB
2971 lines
96 KiB
/*++
|
|
|
|
Copyright (c) 1990, 1991, 1992, 1993 - 1997 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
serial.h
|
|
|
|
Abstract:
|
|
|
|
Type definitions and data for the serial port driver
|
|
|
|
Author:
|
|
|
|
Anthony V. Ercolano April 8, 1991
|
|
|
|
--*/
|
|
|
|
#ifdef POOL_TAGGING
|
|
#undef ExAllocatePool
|
|
#undef ExAllocatePoolWithQuota
|
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'XMOC')
|
|
#define ExAllocatePoolWithQuota(a,b) ExAllocatePoolWithQuotaTag(a,b,'XMOC')
|
|
#endif
|
|
|
|
|
|
//
|
|
// The following definition is used to include/exclude changes made for power
|
|
// support in the driver. If non-zero the support is included. If zero the
|
|
// support is excluded.
|
|
//
|
|
|
|
#define POWER_SUPPORT 1
|
|
|
|
//
|
|
// The following is used to tell the serial driver to perform legacy detection
|
|
// and initialization until PnP functionality can be fully implemented.
|
|
//
|
|
// #define FAKE_IT 1
|
|
|
|
#define RM_PNP_CODE
|
|
|
|
|
|
#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 SERINITCODE ((ULONG)0x00000040)
|
|
#define SERTRACECALLS ((ULONG)0x00000040)
|
|
#define SERPNPPOWER ((ULONG)0x00000100)
|
|
#define SERFLOW ((ULONG)0x20000000)
|
|
#define SERERRORS ((ULONG)0x40000000)
|
|
#define SERBUGCHECK ((ULONG)0x80000000)
|
|
#define SERDBGALL ((ULONG)0xFFFFFFFF)
|
|
|
|
#define SER_DBG_DEFAULT SERDBGALL
|
|
|
|
|
|
extern ULONG SerialDebugLevel;
|
|
#if defined(NEC_98)
|
|
//
|
|
// DbgPrint header changed from SERIAL to SER101
|
|
//
|
|
#define SerialDump(LEVEL,STRING) \
|
|
do { \
|
|
ULONG _level = (LEVEL); \
|
|
if (SerialDebugLevel & _level) { \
|
|
DbgPrint ("SER101: "); \
|
|
DbgPrint STRING; \
|
|
} \
|
|
if (_level == SERBUGCHECK) { \
|
|
ASSERT(FALSE); \
|
|
} \
|
|
} while (0)
|
|
#else
|
|
#define SerialDump(LEVEL,STRING) \
|
|
do { \
|
|
ULONG _level = (LEVEL); \
|
|
if (SerialDebugLevel & _level) { \
|
|
DbgPrint STRING; \
|
|
} \
|
|
if (_level == SERBUGCHECK) { \
|
|
ASSERT(FALSE); \
|
|
} \
|
|
} while (0)
|
|
#endif //defined(NEC_98)
|
|
#else
|
|
#define SerialDump(LEVEL,STRING) do {NOTHING;} while (0)
|
|
#endif
|
|
|
|
|
|
//
|
|
// Some default driver values. We will check the registry for
|
|
// them first.
|
|
//
|
|
#define SERIAL_UNINITIALIZED_DEFAULT 1234567
|
|
#define SERIAL_FORCE_FIFO_DEFAULT 1
|
|
#define SERIAL_RX_FIFO_DEFAULT 8
|
|
#define SERIAL_TX_FIFO_DEFAULT 14
|
|
#define SERIAL_PERMIT_SHARE_DEFAULT 0
|
|
#define SERIAL_LOG_FIFO_DEFAULT 0
|
|
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
#if defined(NEC_98)
|
|
#define DEFAULT_NT_SUFFIX L"Ser101"
|
|
#else
|
|
#define DEFAULT_NT_SUFFIX L"Serial"
|
|
#endif //defined(NEC_98)
|
|
|
|
#if defined(NEC_98)
|
|
//
|
|
// Out of offsets from the base register address of the
|
|
// various registers for the 71051.
|
|
//
|
|
#define SYSTEM_PORT_C 0x35
|
|
#define TIMER_MODE_REGISTER 0x77
|
|
#define TIMER_COUNT_REGISTER 0x75
|
|
#define COMMAND_SET 0x32
|
|
#define MODE_SET 0x32
|
|
#define IO_DELAY_REGISTER 0x5f
|
|
#define CONFIG_INDEX_REGISTER 0x411
|
|
#define CONFIG_DATA_REGISTER 0x413
|
|
#define TRANSFER_CLOCK_ENABLE_ADDRESS (UCHAR)0x83
|
|
#else
|
|
#endif //defined(NEC_98)
|
|
//
|
|
// 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))
|
|
#if defined(NEC_98)
|
|
#define INTERRUPT_ENABLE_REGISTER ((ULONG)((0x08)*SERIAL_REGISTER_STRIDE))
|
|
#define INTERRUPT_IDENT_REGISTER ((ULONG)((0x06)*SERIAL_REGISTER_STRIDE))
|
|
#define FIFO_CONTROL_REGISTER ((ULONG)((0x08)*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)((0x02)*SERIAL_REGISTER_STRIDE))
|
|
#define MODEM_STATUS_REGISTER ((ULONG)((0x04)*SERIAL_REGISTER_STRIDE))
|
|
#define DIVISOR_LATCH_LSB ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE))
|
|
#define DIVISOR_LATCH_MSB ((ULONG)((0x01)*SERIAL_REGISTER_STRIDE))
|
|
#define VFAST_BAUDCLK_REGISTER ((ULONG)((0x0a)*SERIAL_REGISTER_STRIDE))
|
|
#define SERIAL_REGISTER_SPAN ((ULONG)(10*SERIAL_REGISTER_STRIDE))
|
|
#else
|
|
#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))
|
|
#endif //defined(NEC_98)
|
|
|
|
//
|
|
// If we have an interrupt status register this is its assumed
|
|
// length.
|
|
//
|
|
#define SERIAL_STATUS_LENGTH ((ULONG)(1*SERIAL_REGISTER_STRIDE))
|
|
|
|
#if defined(NEC_98)
|
|
//
|
|
// Offset from the base register address of the
|
|
// INDEX registers for the PC-9801-101(on the 8251 family controler)
|
|
//
|
|
|
|
#define INDEX_REGISTER_OFFSET ((ULONG)((0x04)*SERIAL_REGISTER_STRIDE))
|
|
#define INDEX_DATA_REGISTER_OFFSET ((ULONG)((0x05)*SERIAL_REGISTER_STRIDE))
|
|
|
|
//
|
|
// 1stCCU Transfer Clock Enable Port
|
|
//
|
|
#define TXC_PERMIT ((UCHAR)0x00)
|
|
#define TXC_PROHIBIT ((UCHAR)0x01)
|
|
|
|
//
|
|
// V.Fast mode buad rate clock for 101
|
|
//
|
|
#define VFAST_BAUD_0075 ((SHORT)0x03)
|
|
#define VFAST_BAUD_0150 ((SHORT)0x04)
|
|
#define VFAST_BAUD_0300 ((SHORT)0x05)
|
|
#define VFAST_BAUD_0600 ((SHORT)0x06)
|
|
#define VFAST_BAUD_1200 ((SHORT)0x07)
|
|
#define VFAST_BAUD_2400 ((SHORT)0x08)
|
|
#define VFAST_BAUD_4800 ((SHORT)0x09)
|
|
#define VFAST_BAUD_9600 ((SHORT)0x0a)
|
|
#define VFAST_BAUD_14400 ((SHORT)0x0b)
|
|
#define VFAST_BAUD_19200 ((SHORT)0x0c)
|
|
#define VFAST_BAUD_38400 ((SHORT)0x0d)
|
|
#define VFAST_BAUD_57600 ((SHORT)0x0e)
|
|
#define VFAST_BAUD_115200 ((SHORT)0x0f)
|
|
|
|
//
|
|
// Communication mode default(ASYNC)
|
|
|
|
//
|
|
#define COMMUNICATION_MODE 0x00
|
|
|
|
//
|
|
// default control word to Timer Mode register
|
|
//
|
|
#define TIMER_DEFAULT_CONTROL ((UCHAR)0xb6)
|
|
|
|
//
|
|
// default out data to command set register
|
|
//
|
|
#define COMMAND_DEFAULT_SET ((UCHAR)0x15)
|
|
#define COMMAND_ERROR_RESET ((UCHAR)0x40)
|
|
|
|
//
|
|
// default baud rate mode(x16) to Mode Set register
|
|
//
|
|
#define BAUDRATE_DEFAULT_MODE ((UCHAR)0x02)
|
|
|
|
//
|
|
// default out data to Io Delay register
|
|
//
|
|
#define IO_DELAY_DATA ((UCHAR)0x00)
|
|
|
|
//
|
|
// Fifo mode enable
|
|
//
|
|
#define DISABLE_FIFO_MODE ((UCHAR)0x00)
|
|
#define SUPPORT_FIFO_MODE ((UCHAR)0x40)
|
|
#define SUPPORT_FIFO_MASK ((UCHAR)0x60)
|
|
|
|
//
|
|
// Interrupt enable bit
|
|
//
|
|
#define MODEM_STATUS_INTERRUPT ((UCHAR)0x10)
|
|
#define LINE_STATUS_INTERRUPT ((UCHAR)0x08)
|
|
#define TRANSMIT_FIFO_INTERRUPT ((UCHAR)0x04)
|
|
#define RECEIVE_FIFO_INTERRUPT ((UCHAR)0x01)
|
|
|
|
//
|
|
// These defines are used to set the line control register.
|
|
//
|
|
#define SER71051_PARITY_MASK ((UCHAR)0x18)
|
|
#define SER71051_1_STOP ((UCHAR)0x40)
|
|
#define SER71051_1_5_STOP ((UCHAR)0x80)
|
|
#define SER71051_2_STOP ((UCHAR)0xc0)
|
|
|
|
// 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).
|
|
//
|
|
#define SER71051_SEND_BREAK 0x08
|
|
#else
|
|
#endif //defined(NEC_98)
|
|
//
|
|
// 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 {
|
|
PHYSICAL_ADDRESS Controller;
|
|
PHYSICAL_ADDRESS TrController;
|
|
PHYSICAL_ADDRESS InterruptStatus;
|
|
PHYSICAL_ADDRESS TrInterruptStatus;
|
|
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 TrVector;
|
|
ULONG TrIrql;
|
|
ULONG Affinity;
|
|
ULONG Indexed;
|
|
BOOLEAN Jensen;
|
|
} CONFIG_DATA,*PCONFIG_DATA;
|
|
|
|
//
|
|
// Serial ISR switch structure
|
|
//
|
|
|
|
typedef struct _SERIAL_CISR_SW {
|
|
BOOLEAN (*IsrFunc)(PKINTERRUPT, PVOID);
|
|
PVOID Context;
|
|
LIST_ENTRY SharerList;
|
|
} SERIAL_CISR_SW, *PSERIAL_CISR_SW;
|
|
|
|
|
|
//
|
|
// This structure contains configuration data, much of which
|
|
// is read from the registry.
|
|
//
|
|
typedef struct _SERIAL_FIRMWARE_DATA {
|
|
PDRIVER_OBJECT DriverObject;
|
|
ULONG ControllersFound;
|
|
ULONG ForceFifoEnableDefault;
|
|
ULONG DebugLevel;
|
|
ULONG ShouldBreakOnEntry;
|
|
ULONG RxFIFODefault;
|
|
ULONG TxFIFODefault;
|
|
ULONG PermitShareDefault;
|
|
ULONG PermitSystemWideShare;
|
|
ULONG LogFifoDefault;
|
|
UNICODE_STRING Directory;
|
|
UNICODE_STRING NtNameSuffix;
|
|
UNICODE_STRING DirectorySymbolicName;
|
|
LIST_ENTRY ConfigList;
|
|
BOOLEAN JensenDetected;
|
|
} SERIAL_FIRMWARE_DATA,*PSERIAL_FIRMWARE_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)
|
|
|
|
//
|
|
// These are default values that shouldn't appear in the registry
|
|
//
|
|
#define SERIAL_BAD_VALUE ((ULONG)-1)
|
|
|
|
|
|
typedef struct _SERIAL_DEVICE_STATE {
|
|
//
|
|
// TRUE if we need to set the state to open
|
|
// on a powerup
|
|
//
|
|
|
|
BOOLEAN Reopen;
|
|
|
|
//
|
|
// Hardware registers
|
|
//
|
|
|
|
UCHAR IER;
|
|
// FCR is known by other values
|
|
UCHAR LCR;
|
|
UCHAR MCR;
|
|
// LSR is never written
|
|
// MSR is never written
|
|
// SCR is either scratch or interrupt status
|
|
|
|
|
|
} SERIAL_DEVICE_STATE, *PSERIAL_DEVICE_STATE;
|
|
|
|
|
|
#if DBG
|
|
#define SerialLockPagableSectionByHandle(_secHandle) \
|
|
{ \
|
|
MmLockPagableSectionByHandle((_secHandle)); \
|
|
InterlockedIncrement(&SerialGlobals.PAGESER_Count); \
|
|
}
|
|
|
|
#define SerialUnlockPagableImageSection(_secHandle) \
|
|
{ \
|
|
InterlockedDecrement(&SerialGlobals.PAGESER_Count); \
|
|
MmUnlockPagableImageSection(_secHandle); \
|
|
}
|
|
|
|
|
|
#define SERIAL_LOCKED_PAGED_CODE() \
|
|
if ((KeGetCurrentIrql() > APC_LEVEL) \
|
|
&& (SerialGlobals.PAGESER_Count == 0)) { \
|
|
KdPrint(("SERIAL: Pageable code called at IRQL %d without lock \n", \
|
|
KeGetCurrentIrql())); \
|
|
ASSERT(FALSE); \
|
|
}
|
|
|
|
#else
|
|
#define SerialLockPagableSectionByHandle(_secHandle) \
|
|
{ \
|
|
MmLockPagableSectionByHandle((_secHandle)); \
|
|
}
|
|
|
|
#define SerialUnlockPagableImageSection(_secHandle) \
|
|
{ \
|
|
MmUnlockPagableImageSection(_secHandle); \
|
|
}
|
|
|
|
#define SERIAL_LOCKED_PAGED_CODE()
|
|
#endif // DBG
|
|
|
|
|
|
|
|
#define SerialRemoveQueueDpc(_dpc, _pExt) \
|
|
{ \
|
|
if (KeRemoveQueueDpc((_dpc))) { \
|
|
InterlockedDecrement(&(_pExt)->DpcCount); \
|
|
} \
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
//
|
|
// This is to link together ports on the same multiport card
|
|
//
|
|
|
|
LIST_ENTRY MultiportSiblings;
|
|
|
|
//
|
|
// This links together all devobjs that this driver owns.
|
|
// It is needed to search when starting a new device.
|
|
//
|
|
LIST_ENTRY AllDevObjs;
|
|
|
|
//
|
|
// 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 to the pure "COMx" name
|
|
//
|
|
UNICODE_STRING DosName;
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
//
|
|
// Translated vector
|
|
//
|
|
|
|
ULONG Vector;
|
|
|
|
//
|
|
// Translated Irql
|
|
//
|
|
|
|
KIRQL Irql;
|
|
|
|
|
|
//
|
|
// Untranslated vector
|
|
//
|
|
|
|
ULONG OriginalVector;
|
|
|
|
|
|
//
|
|
// Untranslated irql
|
|
//
|
|
|
|
ULONG OriginalIrql;
|
|
|
|
|
|
//
|
|
// Address space
|
|
//
|
|
|
|
ULONG AddressSpace;
|
|
|
|
|
|
//
|
|
// Bus number
|
|
//
|
|
|
|
ULONG BusNumber;
|
|
|
|
|
|
//
|
|
// Interface type
|
|
//
|
|
|
|
INTERFACE_TYPE InterfaceType;
|
|
|
|
|
|
//
|
|
// Port index no for multiport devices
|
|
//
|
|
|
|
ULONG PortIndex;
|
|
|
|
|
|
//
|
|
// Indexed flag for multiport devices
|
|
//
|
|
|
|
BOOLEAN Indexed;
|
|
|
|
//
|
|
// Mask inverted mask for multiport devices
|
|
//
|
|
|
|
ULONG MaskInverted;
|
|
|
|
//
|
|
// Needed to add new devices to multiport boards
|
|
//
|
|
|
|
ULONG NewPortIndex;
|
|
ULONG NewMaskInverted;
|
|
PVOID NewExtension;
|
|
|
|
//
|
|
// 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 destroy it when we unload.
|
|
//
|
|
BOOLEAN CreatedSymbolicLink;
|
|
|
|
//
|
|
// Records whether we actually created an entry in SERIALCOMM
|
|
// at driver load time. If we didn't create it, we won't try
|
|
// to destroy it when the device is removed.
|
|
//
|
|
BOOLEAN CreatedSerialCommEntry;
|
|
|
|
//
|
|
// 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 lock will be used to protect the accept / reject state
|
|
// transitions and flags of the driver It must be acquired
|
|
// before a cancel lock
|
|
//
|
|
|
|
KSPIN_LOCK FlagsLock;
|
|
|
|
//
|
|
// 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 DPC is fired to set an event stating that all other
|
|
// DPC's have been finish for this device extension so that
|
|
// paged code may be unlocked.
|
|
//
|
|
|
|
KDPC IsrUnlockPagesDpc;
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// This is a pointer to the next lower device in the IRP stack.
|
|
//
|
|
|
|
PDEVICE_OBJECT LowerDeviceObject;
|
|
|
|
//
|
|
// This is where keep track of the power state the device is in.
|
|
//
|
|
|
|
DEVICE_POWER_STATE PowerState;
|
|
|
|
//
|
|
// Pointer to the driver object
|
|
//
|
|
|
|
PDRIVER_OBJECT DriverObject;
|
|
|
|
|
|
//
|
|
// Event used to do some synchronization with the devices underneath me
|
|
// (namely ACPI)
|
|
//
|
|
|
|
KEVENT SerialSyncEvent;
|
|
|
|
|
|
//
|
|
// String where we keep the symbolic link that is returned to us when we
|
|
// register our device under the COMM class with the Plug and Play manager.
|
|
//
|
|
|
|
UNICODE_STRING DeviceClassSymbolicName;
|
|
|
|
|
|
//
|
|
// Serial ISR switch structure
|
|
//
|
|
|
|
PSERIAL_CISR_SW CIsrSw;
|
|
|
|
//
|
|
// Count of pending IRP's
|
|
//
|
|
|
|
ULONG PendingIRPCnt;
|
|
|
|
//
|
|
// Accepting requests?
|
|
//
|
|
|
|
ULONG DevicePNPAccept;
|
|
|
|
//
|
|
// No IRP's pending event
|
|
//
|
|
|
|
KEVENT PendingIRPEvent;
|
|
|
|
//
|
|
// PNP State
|
|
//
|
|
|
|
ULONG PNPState;
|
|
|
|
//
|
|
// Misc Flags
|
|
//
|
|
|
|
ULONG Flags;
|
|
|
|
//
|
|
// Open count
|
|
//
|
|
|
|
LONG OpenCount;
|
|
|
|
//
|
|
// Start sync event
|
|
//
|
|
|
|
KEVENT SerialStartEvent;
|
|
|
|
//
|
|
// Current state during powerdown
|
|
//
|
|
|
|
SERIAL_DEVICE_STATE DeviceState;
|
|
|
|
//
|
|
// Device stack capabilites
|
|
//
|
|
|
|
DEVICE_POWER_STATE DeviceStateMap[PowerSystemMaximum];
|
|
|
|
//
|
|
// Event to signal transition to D0 completion
|
|
//
|
|
|
|
KEVENT PowerD0Event;
|
|
|
|
//
|
|
// List of stalled IRP's
|
|
//
|
|
|
|
LIST_ENTRY StalledIrpQueue;
|
|
|
|
//
|
|
// Mutex on open status
|
|
//
|
|
|
|
FAST_MUTEX OpenMutex;
|
|
|
|
//
|
|
// Mutex on close
|
|
//
|
|
|
|
FAST_MUTEX CloseMutex;
|
|
|
|
//
|
|
// TRUE if we own power policy
|
|
//
|
|
|
|
BOOLEAN OwnsPowerPolicy;
|
|
|
|
//
|
|
// SystemWake from devcaps
|
|
//
|
|
|
|
SYSTEM_POWER_STATE SystemWake;
|
|
|
|
//
|
|
// DeviceWake from devcaps
|
|
//
|
|
|
|
DEVICE_POWER_STATE DeviceWake;
|
|
|
|
//
|
|
// Our PDO
|
|
//
|
|
|
|
PDEVICE_OBJECT Pdo;
|
|
|
|
//
|
|
// Should we enable wakeup
|
|
//
|
|
|
|
BOOLEAN SendWaitWake;
|
|
|
|
//
|
|
// Pending wait wake IRP
|
|
//
|
|
|
|
PIRP PendingWakeIrp;
|
|
|
|
//
|
|
// WMI Information
|
|
//
|
|
|
|
WMILIB_CONTEXT WmiLibInfo;
|
|
|
|
//
|
|
// Name to use as WMI identifier
|
|
//
|
|
|
|
UNICODE_STRING WmiIdentifier;
|
|
|
|
//
|
|
// WMI Comm Data
|
|
//
|
|
|
|
SERIAL_WMI_COMM_DATA WmiCommData;
|
|
|
|
//
|
|
// WMI HW Data
|
|
//
|
|
|
|
SERIAL_WMI_HW_DATA WmiHwData;
|
|
|
|
//
|
|
// WMI Performance Data
|
|
//
|
|
|
|
SERIAL_WMI_PERF_DATA WmiPerfData;
|
|
|
|
//
|
|
// Pending DPC count
|
|
//
|
|
|
|
ULONG DpcCount;
|
|
|
|
//
|
|
// Pending DPC event
|
|
//
|
|
|
|
KEVENT PendingDpcEvent;
|
|
|
|
//
|
|
// Should we expose external interfaces?
|
|
//
|
|
|
|
ULONG SkipNaming;
|
|
|
|
|
|
#if defined(NEC_98)
|
|
//
|
|
// write data to divisor latch register (for 16550)
|
|
//
|
|
|
|
SHORT DivisorLatch16550;
|
|
|
|
//
|
|
// write data to modem control register (for 16550)
|
|
//
|
|
|
|
UCHAR ModemControl16550;
|
|
|
|
//
|
|
// write data to line control register (for 16550)
|
|
//
|
|
|
|
UCHAR LineControl16550;
|
|
|
|
//
|
|
// out data to mode set register (for 71051)
|
|
//
|
|
|
|
UCHAR ModeSet71051;
|
|
|
|
//
|
|
// out data to command set register (for 71051)
|
|
//
|
|
|
|
UCHAR CommandSet71051;
|
|
|
|
//
|
|
// out data to Interrupt enable register for 101
|
|
// bit0 = Recive Data Available
|
|
// bit1 = Transmit Holding Register Empty
|
|
//
|
|
|
|
UCHAR InterruptEnable16550;
|
|
|
|
//
|
|
// Common base address for 101
|
|
// CH2 = 0x0yB0
|
|
// CH3 = 0x0yB2
|
|
//
|
|
|
|
PUCHAR CommonBaseAddress;
|
|
|
|
//
|
|
// CCU Mode set register address for 101
|
|
// CH2 = 0x0yB3
|
|
// CH3 = 0x0yBB
|
|
|
|
PUCHAR ModeSetRegisterAddress;
|
|
|
|
//
|
|
// CCU Command set register address for 101
|
|
// CH2 = 0x0yB3
|
|
// CH3 = 0x0yBB
|
|
|
|
PUCHAR CommandSetRegisterAddress;
|
|
|
|
#else
|
|
#endif //defined(NEC_98)
|
|
} SERIAL_DEVICE_EXTENSION,*PSERIAL_DEVICE_EXTENSION;
|
|
|
|
#define SERIAL_PNPACCEPT_OK 0x0L
|
|
#define SERIAL_PNPACCEPT_REMOVING 0x1L
|
|
#define SERIAL_PNPACCEPT_STOPPING 0x2L
|
|
#define SERIAL_PNPACCEPT_STOPPED 0x4L
|
|
#define SERIAL_PNPACCEPT_SURPRISE_REMOVING 0x8L
|
|
|
|
#define SERIAL_PNP_ADDED 0x0L
|
|
#define SERIAL_PNP_STARTED 0x1L
|
|
#define SERIAL_PNP_QSTOP 0x2L
|
|
#define SERIAL_PNP_STOPPING 0x3L
|
|
#define SERIAL_PNP_QREMOVE 0x4L
|
|
#define SERIAL_PNP_REMOVING 0x5L
|
|
#define SERIAL_PNP_RESTARTING 0x6L
|
|
|
|
#define SERIAL_FLAGS_CLEAR 0x0L
|
|
#define SERIAL_FLAGS_STARTED 0x1L
|
|
#define SERIAL_FLAGS_STOPPED 0x2L
|
|
#define SERIAL_FLAGS_BROKENHW 0x4L
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// 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;
|
|
#if defined(NEC_98)
|
|
//
|
|
// The value of INDEX for the access of various registers.
|
|
// For the PC-9801-101
|
|
//
|
|
#define INDEX_FOR_DATA_REGISTER ((ULONG)((0x00)*SERIAL_REGISTER_STRIDE))
|
|
#define INDEX_FOR_INTERRUPT_ENABLE_REGISTER ((ULONG)((0x04)*SERIAL_REGISTER_STRIDE))
|
|
#define INDEX_FOR_INTERRUPT_IDENT_REGISTER ((ULONG)((0x03)*SERIAL_REGISTER_STRIDE))
|
|
#define INDEX_FOR_FIFO_CONTROL_REGISTER ((ULONG)((0x04)*SERIAL_REGISTER_STRIDE))
|
|
#define INDEX_FOR_LINE_CONTROL_REGISTER ((ULONG)((0x06)*SERIAL_REGISTER_STRIDE))
|
|
#define INDEX_FOR_LINE_STATUS_REGISTER ((ULONG)((0x01)*SERIAL_REGISTER_STRIDE))
|
|
#define INDEX_FOR_MODEM_STATUS_REGISTER ((ULONG)((0x02)*SERIAL_REGISTER_STRIDE))
|
|
#define INDEX_FOR_VFAST_BAUDCLK_REGISTER ((ULONG)((0x05)*SERIAL_REGISTER_STRIDE))
|
|
|
|
|
|
//
|
|
// The inline process is access for the various registers on the PC-9801-101
|
|
//
|
|
|
|
#define WRITE_IO_DELAY(Counter) \
|
|
do \
|
|
{ \
|
|
ULONG i; \
|
|
\
|
|
for (i = 0; (i <= Counter) && (Counter >= 1); i++) { \
|
|
WRITE_PORT_UCHAR((PUCHAR)IO_DELAY_REGISTER, IO_DELAY_DATA); \
|
|
} \
|
|
} while (0)
|
|
|
|
_inline
|
|
UCHAR
|
|
ReadDataRegister(
|
|
IN PUCHAR BaseAddress
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_DATA_REGISTER);
|
|
WRITE_IO_DELAY(1);
|
|
return (READ_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET));
|
|
}
|
|
_inline
|
|
VOID
|
|
WriteDataRegister(
|
|
IN PUCHAR BaseAddress,
|
|
IN UCHAR Value
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_DATA_REGISTER);
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET, Value);
|
|
}
|
|
|
|
_inline
|
|
UCHAR
|
|
ReadInterruptEnableRegister(
|
|
IN PUCHAR BaseAddress
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_INTERRUPT_ENABLE_REGISTER);
|
|
WRITE_IO_DELAY(1);
|
|
return (READ_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET));
|
|
}
|
|
|
|
_inline
|
|
VOID
|
|
WriteInterruptEnableRegister(
|
|
IN PUCHAR BaseAddress,
|
|
IN UCHAR Value
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_INTERRUPT_ENABLE_REGISTER);
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET, Value);
|
|
}
|
|
|
|
_inline
|
|
UCHAR
|
|
ReadInterruptIdentRegister(
|
|
IN PUCHAR BaseAddress
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_INTERRUPT_IDENT_REGISTER);
|
|
WRITE_IO_DELAY(1);
|
|
return (READ_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET));
|
|
}
|
|
|
|
_inline
|
|
VOID
|
|
WriteInterruptIdentRegister(
|
|
IN PUCHAR BaseAddress,
|
|
IN UCHAR Value
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_INTERRUPT_IDENT_REGISTER);
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET, Value);
|
|
}
|
|
|
|
_inline
|
|
UCHAR
|
|
ReadFifoControlRegister(
|
|
IN PUCHAR BaseAddress
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_FIFO_CONTROL_REGISTER);
|
|
WRITE_IO_DELAY(1);
|
|
return (READ_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET));
|
|
}
|
|
|
|
_inline
|
|
VOID
|
|
WriteFifoControlRegister(
|
|
IN PUCHAR BaseAddress,
|
|
IN UCHAR Value
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_FIFO_CONTROL_REGISTER);
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET, Value);
|
|
}
|
|
|
|
_inline
|
|
UCHAR
|
|
ReadLineControlRegister(
|
|
IN PUCHAR BaseAddress
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_LINE_CONTROL_REGISTER);
|
|
WRITE_IO_DELAY(1);
|
|
return (READ_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET));
|
|
}
|
|
|
|
_inline
|
|
VOID
|
|
WriteLineControlRegister(
|
|
IN PUCHAR BaseAddress,
|
|
IN UCHAR Value
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_LINE_CONTROL_REGISTER);
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET, Value);
|
|
}
|
|
|
|
_inline
|
|
UCHAR
|
|
ReadLineStatusRegister(
|
|
IN PUCHAR BaseAddress
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_LINE_STATUS_REGISTER);
|
|
WRITE_IO_DELAY(1);
|
|
return (READ_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET));
|
|
}
|
|
|
|
_inline
|
|
UCHAR
|
|
ReadModemStatusRegister(
|
|
IN PUCHAR BaseAddress
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_MODEM_STATUS_REGISTER);
|
|
WRITE_IO_DELAY(1);
|
|
return (READ_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET));
|
|
}
|
|
|
|
_inline
|
|
UCHAR
|
|
ReadVFASTBaudclkRegister(
|
|
IN PUCHAR BaseAddress
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_VFAST_BAUDCLK_REGISTER);
|
|
WRITE_IO_DELAY(1);
|
|
return (READ_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET));
|
|
}
|
|
|
|
_inline
|
|
VOID
|
|
WriteVFASTBaudclkRegister(
|
|
IN PUCHAR BaseAddress,
|
|
IN UCHAR Value
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_REGISTER_OFFSET,INDEX_FOR_VFAST_BAUDCLK_REGISTER);
|
|
WRITE_PORT_UCHAR((BaseAddress)+INDEX_DATA_REGISTER_OFFSET, Value);
|
|
}
|
|
|
|
|
|
_inline
|
|
UCHAR
|
|
SerialGetInterruptEnable(
|
|
IN PUCHAR BaseAddress,
|
|
IN PSERIAL_DEVICE_EXTENSION Extension
|
|
)
|
|
{
|
|
UCHAR InterruptEnable = 0;
|
|
UCHAR FifoControl;
|
|
|
|
FifoControl = ReadInterruptEnableRegister(BaseAddress);
|
|
|
|
//
|
|
// D3: Modem Status D4:INTERRUPT_ENABLE_REG
|
|
// D2: Receiver Line Status D3:INTERRUPT_ENABLE_REG
|
|
// D1: Transmit Holding Register Empty D1:
|
|
// D0: Received Data Available D0:
|
|
//
|
|
|
|
InterruptEnable |= (FifoControl & (MODEM_STATUS_INTERRUPT | LINE_STATUS_INTERRUPT)) >> 1;
|
|
InterruptEnable |= (Extension->InterruptEnable16550);
|
|
|
|
return(InterruptEnable);
|
|
}
|
|
|
|
|
|
|
|
_inline
|
|
UCHAR
|
|
SerialGetLineStatus(
|
|
IN PUCHAR BaseAddress
|
|
)
|
|
{
|
|
UCHAR LineStatus = 0;
|
|
UCHAR FifoStatus;
|
|
|
|
FifoStatus = ReadLineStatusRegister(BaseAddress);
|
|
|
|
//
|
|
// D7: Error in Receive FIFO D7:LINE_STATUS_REG
|
|
// D6: Transmitter shift Register empty D0:LINE_STATUS_REG
|
|
// D5: Transmitter holding Register empty D1:LINE_STATUS_REG
|
|
// D4: Break interrupt D6:LINE_STATUS_REG
|
|
// D3: Framing error D5:LINE_STATUS_REG
|
|
// D2: Parity error D3:LINE_STATUS_REG
|
|
// D1: Overrun error D4:LINE_STATUS_REG
|
|
// D0: Receive Data Ready D2:LINE_STATUS_REG
|
|
//
|
|
|
|
LineStatus |= (FifoStatus & 0x80); // Error in Receive FIFO
|
|
LineStatus |= (FifoStatus & 0x01) << 6; // Transmitter shift Register empty
|
|
LineStatus |= (FifoStatus & 0x02) << 4; // Transmitter holding Register empty
|
|
LineStatus |= (FifoStatus & 0x40) >> 2; // Break interrupt
|
|
LineStatus |= (FifoStatus & 0x20) >> 2; // Framing error
|
|
LineStatus |= (FifoStatus & 0x08) >> 1; // Parity error
|
|
LineStatus |= (FifoStatus & 0x10) >> 3; // Overrun error
|
|
LineStatus |= (FifoStatus & 0x04) >> 2; // Receive Data Ready
|
|
|
|
return(LineStatus);
|
|
}
|
|
|
|
|
|
|
|
/* #define WRITE_PORT_UCHAR(BaseAddress, Value) */ \
|
|
/* do */ \
|
|
/* { */ \
|
|
/* WRITE_PORT_UCHAR(BaseAddress, Value); */ \
|
|
/* WRITE_PORT_UCHAR( */ \
|
|
/* (PUCHAR)IO_DELAY_REGISTER, */ \
|
|
/* IO_DELAY_DATA */ \
|
|
/* ); */ \
|
|
/* } while (0) */
|
|
|
|
|
|
#define READ_DIVISOR_LATCH(BaseAddress,PDesiredDivisor) \
|
|
do \
|
|
{ \
|
|
*PDesiredDivisor = Extension->DivisorLatch16550; \
|
|
} while (0)
|
|
|
|
#define READ_INTERRUPT_ENABLE(BaseAddress) \
|
|
(SerialGetInterruptEnable(BaseAddress,Extension))
|
|
|
|
#define WRITE_INTERRUPT_ENABLE(BaseAddress,Values) \
|
|
do \
|
|
{ \
|
|
UCHAR FifoControl = 0; \
|
|
UCHAR FifoInterruptControl = 0; \
|
|
UCHAR OutValues = 0; \
|
|
FifoControl = ReadInterruptEnableRegister(BaseAddress); \
|
|
if (Values & SERIAL_IER_MS) { \
|
|
FifoControl |= MODEM_STATUS_INTERRUPT; \
|
|
} else { \
|
|
FifoControl &= ~MODEM_STATUS_INTERRUPT; \
|
|
} \
|
|
if (Values & SERIAL_IER_RLS) { \
|
|
FifoControl |= LINE_STATUS_INTERRUPT; \
|
|
} else { \
|
|
FifoControl &= ~LINE_STATUS_INTERRUPT; \
|
|
} \
|
|
WriteInterruptEnableRegister(BaseAddress, FifoControl); \
|
|
FifoInterruptControl = (Values & (SERIAL_IER_THR | SERIAL_IER_RDA)); \
|
|
OutValues = (Values & (SERIAL_IER_THR | SERIAL_IER_RDA)); \
|
|
if (OutValues & SERIAL_IER_THR) { \
|
|
OutValues |= 0x04; \
|
|
} else { \
|
|
OutValues &= ~0x04; \
|
|
} \
|
|
WRITE_PORT_UCHAR(BaseAddress, OutValues); \
|
|
Extension->InterruptEnable16550 = FifoInterruptControl; \
|
|
} while (0)
|
|
|
|
|
|
#define DISABLE_ALL_INTERRUPTS(BaseAddress) \
|
|
do \
|
|
{ \
|
|
WRITE_INTERRUPT_ENABLE(BaseAddress,0); \
|
|
} while (0)
|
|
|
|
#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)
|
|
|
|
|
|
#define READ_INTERRUPT_ID_REG(BaseAddress) \
|
|
(((ReadInterruptIdentRegister(BaseAddress)) & 0x0f) | SERIAL_IIR_FIFOS_ENABLED)
|
|
|
|
#define READ_MODEM_CONTROL(BaseAddress) \
|
|
(Extension->ModemControl16550)
|
|
|
|
#define READ_MODEM_STATUS(BaseAddress) \
|
|
(ReadModemStatusRegister(BaseAddress))
|
|
|
|
#define READ_RECEIVE_BUFFER(BaseAddress) \
|
|
(ReadDataRegister(BaseAddress))
|
|
|
|
#define READ_LINE_STATUS(BaseAddress) \
|
|
(SerialGetLineStatus(BaseAddress))
|
|
|
|
#define READ_LINE_CONTROL(BaseAddress) \
|
|
(Extension->LineControl16550)
|
|
|
|
#define WRITE_TRANSMIT_HOLDING(BaseAddress,TransmitChar) \
|
|
do \
|
|
{ \
|
|
WriteDataRegister( \
|
|
BaseAddress, \
|
|
(TransmitChar) \
|
|
); \
|
|
} while (0)
|
|
|
|
|
|
#define WRITE_TRANSMIT_FIFO_HOLDING(BaseAddress,TransmitChars,TxN) \
|
|
do \
|
|
{ \
|
|
PUCHAR TransmitBuffer = TransmitChars; \
|
|
ULONG i; \
|
|
for (i = 0; i < TxN; i++, TransmitBuffer++) { \
|
|
WriteDataRegister( \
|
|
BaseAddress, \
|
|
(UCHAR)(*TransmitBuffer) \
|
|
); \
|
|
} \
|
|
} while (0)
|
|
|
|
|
|
|
|
#define ENTER_LEGACY_MODE(BaseAddress, InterruptEnable, FifoControl) \
|
|
do \
|
|
{ \
|
|
*InterruptEnable = READ_INTERRUPT_ENABLE(BaseAddress); \
|
|
DISABLE_ALL_INTERRUPTS(BaseAddress); \
|
|
*FifoControl = ReadFifoControlRegister(BaseAddress); \
|
|
WriteFifoControlRegister(BaseAddress, DISABLE_FIFO_MODE); \
|
|
WRITE_IO_DELAY(3); \
|
|
} while (0)
|
|
|
|
#define EXIT_LEGACY_MODE(BaseAddress, InterruptEnable, FifoControl) \
|
|
do \
|
|
{ \
|
|
WriteFifoControlRegister( \
|
|
(BaseAddress), \
|
|
(UCHAR)(*FifoControl) \
|
|
); \
|
|
WRITE_INTERRUPT_ENABLE( \
|
|
(BaseAddress), \
|
|
(UCHAR)(*InterruptEnable), \
|
|
); \
|
|
} while (0)
|
|
|
|
#define RESET_71051(ModeSet) \
|
|
do \
|
|
{ \
|
|
WRITE_PORT_UCHAR((PUCHAR)Extension->CommandSetRegisterAddress, (UCHAR)0); \
|
|
WRITE_IO_DELAY(1); \
|
|
WRITE_PORT_UCHAR((PUCHAR)Extension->CommandSetRegisterAddress, (UCHAR)0); \
|
|
WRITE_IO_DELAY(1); \
|
|
WRITE_PORT_UCHAR((PUCHAR)Extension->CommandSetRegisterAddress, (UCHAR)0); \
|
|
WRITE_IO_DELAY(1); \
|
|
WRITE_PORT_UCHAR((PUCHAR)Extension->CommandSetRegisterAddress, COMMAND_ERROR_RESET); \
|
|
WRITE_IO_DELAY(2); \
|
|
WRITE_PORT_UCHAR( \
|
|
(PUCHAR)Extension->ModeSetRegisterAddress, \
|
|
(ModeSet) \
|
|
); \
|
|
WRITE_IO_DELAY(1); \
|
|
WRITE_PORT_UCHAR( \
|
|
(PUCHAR)Extension->CommandSetRegisterAddress, \
|
|
(UCHAR)Extension->CommandSet71051 \
|
|
); \
|
|
WRITE_IO_DELAY(1); \
|
|
} while (0)
|
|
|
|
|
|
|
|
#define WRITE_DIVISOR_LATCH(BaseAddress,DesiredDivisor) \
|
|
do \
|
|
{ \
|
|
SHORT Divisor = DesiredDivisor; \
|
|
ULONG denominator; \
|
|
LONG DesiredBaud; \
|
|
Extension->DivisorLatch16550 = Divisor; \
|
|
denominator = 1843200 / Divisor; \
|
|
DesiredBaud = denominator / (ULONG)16; \
|
|
/* RS syncronize clock TXC prohibit */ \
|
|
/* 98 mode -> V.Fast mode, and Baud rate set */ \
|
|
switch (DesiredBaud) { \
|
|
case 0075: Divisor = VFAST_BAUD_0075; \
|
|
break; \
|
|
case 0150: Divisor = VFAST_BAUD_0150; \
|
|
break; \
|
|
case 0300: Divisor = VFAST_BAUD_0300; \
|
|
break; \
|
|
case 0600: Divisor = VFAST_BAUD_0600; \
|
|
break; \
|
|
case 1200: Divisor = VFAST_BAUD_1200; \
|
|
break; \
|
|
case 2400: Divisor = VFAST_BAUD_2400; \
|
|
break; \
|
|
case 4800: Divisor = VFAST_BAUD_4800; \
|
|
break; \
|
|
case 9600: Divisor = VFAST_BAUD_9600; \
|
|
break; \
|
|
case 14400: Divisor = VFAST_BAUD_14400; \
|
|
break; \
|
|
case 19200: Divisor = VFAST_BAUD_19200; \
|
|
break; \
|
|
case 38400: Divisor = VFAST_BAUD_38400; \
|
|
break; \
|
|
case 57600: Divisor = VFAST_BAUD_57600; \
|
|
break; \
|
|
case 115200: Divisor = VFAST_BAUD_115200; \
|
|
} \
|
|
WriteVFASTBaudclkRegister(BaseAddress, \
|
|
(UCHAR)(Divisor | COMMUNICATION_MODE) \
|
|
); \
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
#define WRITE_LINE_CONTROL(BaseAddress,NewLineControl) \
|
|
do \
|
|
{ \
|
|
UCHAR InterruptControl = 0; \
|
|
UCHAR FifoControl = 0; \
|
|
UCHAR OutLineControl = BAUDRATE_DEFAULT_MODE; \
|
|
ENTER_LEGACY_MODE(BaseAddress, &InterruptControl, &FifoControl); \
|
|
/* character length */ \
|
|
OutLineControl |= (NewLineControl & SERIAL_DATA_MASK) << 2; \
|
|
/* parity enable and parity mode */ \
|
|
OutLineControl |= (NewLineControl & SER71051_PARITY_MASK) << 1; \
|
|
/* stop bit */ \
|
|
if (NewLineControl & SERIAL_STOP_MASK) { \
|
|
if (NewLineControl & SERIAL_DATA_MASK) { \
|
|
OutLineControl |= SER71051_1_5_STOP; \
|
|
} else { \
|
|
OutLineControl |= SER71051_2_STOP; \
|
|
} \
|
|
} else { \
|
|
OutLineControl |= SER71051_1_STOP; \
|
|
} \
|
|
Extension->ModeSet71051 = OutLineControl; \
|
|
RESET_71051(OutLineControl); \
|
|
/* break character */ \
|
|
if (NewLineControl & SERIAL_LCR_BREAK) { \
|
|
if (!(Extension->LineControl16550 & SERIAL_LCR_BREAK)) { \
|
|
Extension->CommandSet71051 |= SER71051_SEND_BREAK; \
|
|
WRITE_PORT_UCHAR( \
|
|
(PUCHAR)Extension->CommandSetRegisterAddress, \
|
|
(UCHAR)Extension->CommandSet71051 \
|
|
); \
|
|
} \
|
|
} else { \
|
|
if (Extension->LineControl16550 & SERIAL_LCR_BREAK) { \
|
|
Extension->CommandSet71051 &= ~SER71051_SEND_BREAK; \
|
|
WRITE_PORT_UCHAR( \
|
|
(PUCHAR)Extension->CommandSetRegisterAddress, \
|
|
(UCHAR)Extension->CommandSet71051 \
|
|
); \
|
|
} \
|
|
} \
|
|
Extension->LineControl16550 = NewLineControl; \
|
|
EXIT_LEGACY_MODE(BaseAddress, &InterruptControl, &FifoControl); \
|
|
} while (0)
|
|
|
|
#define WRITE_FIFO_CONTROL(BaseAddress,ControlValue) \
|
|
do \
|
|
{ \
|
|
UCHAR InterruptControl = 0; \
|
|
UCHAR FifoControl = 0; \
|
|
UCHAR OutValue = ControlValue; \
|
|
ENTER_LEGACY_MODE(BaseAddress, &InterruptControl, &FifoControl); \
|
|
OutValue &= ~(MODEM_STATUS_INTERRUPT | LINE_STATUS_INTERRUPT); \
|
|
WriteFifoControlRegister( \
|
|
(BaseAddress), \
|
|
(OutValue) \
|
|
); \
|
|
FifoControl = ReadFifoControlRegister(BaseAddress); \
|
|
/* always Fifo mode on PC98 */ \
|
|
FifoControl |= SERIAL_FCR_ENABLE; \
|
|
EXIT_LEGACY_MODE(BaseAddress, &InterruptControl, &FifoControl); \
|
|
} while (0)
|
|
|
|
#define WRITE_MODEM_CONTROL(BaseAddress,ModemControl) \
|
|
do \
|
|
{ \
|
|
UCHAR InterruptControl = 0; \
|
|
UCHAR FifoControl = 0; \
|
|
/* command default:ER, RXE, TxE -> ON */ \
|
|
UCHAR CommandSet = COMMAND_DEFAULT_SET; \
|
|
Extension->ModemControl16550 = ModemControl; \
|
|
ENTER_LEGACY_MODE(BaseAddress, &InterruptControl, &FifoControl); \
|
|
CommandSet |= (ModemControl & SERIAL_MCR_DTR) << 1; \
|
|
CommandSet |= (ModemControl & SERIAL_MCR_RTS) << 4; \
|
|
Extension->CommandSet71051 = CommandSet; \
|
|
WRITE_PORT_UCHAR( \
|
|
(PUCHAR)Extension->CommandSetRegisterAddress, \
|
|
(CommandSet) \
|
|
); \
|
|
EXIT_LEGACY_MODE(BaseAddress, &InterruptControl, &FifoControl); \
|
|
} while (0)
|
|
#else
|
|
|
|
|
|
//
|
|
// 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)
|
|
#endif //defined(NEC_98)
|
|
|
|
//
|
|
// We use this to query into the registry as to whether we
|
|
// should break at driver entry.
|
|
//
|
|
|
|
extern SERIAL_FIRMWARE_DATA driverDefaults;
|
|
|
|
|
|
//
|
|
// This is exported from the kernel. It is used to point
|
|
// to the address that the kernel debugger is using.
|
|
//
|
|
|
|
extern PUCHAR *KdComPortInUse;
|
|
|
|
|
|
typedef enum _SERIAL_MEM_COMPARES {
|
|
AddressesAreEqual,
|
|
AddressesOverlap,
|
|
AddressesAreDisjoint
|
|
} SERIAL_MEM_COMPARES,*PSERIAL_MEM_COMPARES;
|
|
|
|
|
|
typedef struct _SERIAL_LIST_DATA {
|
|
PLIST_ENTRY destList;
|
|
PLIST_ENTRY newElement;
|
|
} SERIAL_LIST_DATA, *PSERIAL_LIST_DATA;
|
|
|
|
typedef struct _SERIAL_GLOBALS {
|
|
LIST_ENTRY AllDevObjs;
|
|
PVOID PAGESER_Handle;
|
|
UNICODE_STRING RegistryPath;
|
|
#if DBG
|
|
ULONG PAGESER_Count;
|
|
#endif // DBG
|
|
} SERIAL_GLOBALS, *PSERIAL_GLOBALS;
|
|
|
|
extern SERIAL_GLOBALS SerialGlobals;
|
|
|
|
typedef struct _SERIAL_USER_DATA {
|
|
PHYSICAL_ADDRESS UserPort;
|
|
PHYSICAL_ADDRESS UserInterruptStatus;
|
|
ULONG UserVector;
|
|
UNICODE_STRING UserSymbolicLink;
|
|
ULONG UserPortIndex;
|
|
ULONG UserBusNumber;
|
|
ULONG UserInterfaceType;
|
|
ULONG UserClockRate;
|
|
ULONG UserIndexed;
|
|
ULONG UserInterruptMode;
|
|
ULONG UserAddressSpace;
|
|
ULONG UserLevel;
|
|
ULONG DefaultPermitSystemWideShare;
|
|
ULONG DisablePort;
|
|
ULONG RxFIFO;
|
|
ULONG RxFIFODefault;
|
|
ULONG TxFIFO;
|
|
ULONG TxFIFODefault;
|
|
ULONG ForceFIFOEnable;
|
|
ULONG ForceFIFOEnableDefault;
|
|
ULONG PermitShareDefault;
|
|
ULONG LogFIFODefault;
|
|
ULONG MaskInverted;
|
|
} SERIAL_USER_DATA, *PSERIAL_USER_DATA;
|
|
|
|
typedef struct _SERIAL_PTR_CTX {
|
|
ULONG isPointer;
|
|
PHYSICAL_ADDRESS Port;
|
|
ULONG Vector;
|
|
} SERIAL_PTR_CTX, *PSERIAL_PTR_CTX;
|
|
|
|
#define DEVICE_OBJECT_NAME_LENGTH 128
|
|
#define SYMBOLIC_NAME_LENGTH 128
|
|
#define SERIAL_PNP_ID_STR L"*PNP0501"
|
|
#define SERIAL_PNP_MULTI_ID_STR L"*PNP0502"
|
|
#define SERIAL_DEVICE_MAP L"SERIALCOMM"
|
|
|
|
//
|
|
// Return values for mouse detection callback
|
|
//
|
|
|
|
#define SERIAL_FOUNDPOINTER_PORT 1
|
|
#define SERIAL_FOUNDPOINTER_VECTOR 2
|
|
|
|
#define SerialCompleteRequest(PDevExt, PIrp, PriBoost) \
|
|
{ \
|
|
IoCompleteRequest((PIrp), (PriBoost)); \
|
|
SerialIRPEpilogue((PDevExt)); \
|
|
}
|
|
|
|
#define SERIAL_WMI_GUID_LIST_SIZE 5
|
|
|
|
extern WMIGUIDREGINFO SerialWmiGuidList[SERIAL_WMI_GUID_LIST_SIZE];
|