From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/nthals/halx86/i386/ixkdcom.c | 684 ++++++++++++++++++++++++++++++ 1 file changed, 684 insertions(+) create mode 100644 private/ntos/nthals/halx86/i386/ixkdcom.c (limited to 'private/ntos/nthals/halx86/i386/ixkdcom.c') diff --git a/private/ntos/nthals/halx86/i386/ixkdcom.c b/private/ntos/nthals/halx86/i386/ixkdcom.c new file mode 100644 index 000000000..644d3590b --- /dev/null +++ b/private/ntos/nthals/halx86/i386/ixkdcom.c @@ -0,0 +1,684 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + ixkdcom.c + +Abstract: + + This module contains a very simply package to do com I/O on machines + with standard AT com-ports. It is C code derived from the debugger's + com code. Likely does not work on a PS/2. (Only rewrote the thing + into C so we wouldn't have to deal with random debugger macros.) + + Procedures to init a com object, set and query baud rate, output + character, input character. + +Author: + + Bryan M. Willman (bryanwi) 24-Sep-1990 + +Revision History: + + John Vert (jvert) 12-Jun-1991 + Added ability to check for com-port's existence and hook onto the + highest com-port available. + + John Vert (jvert) 19-Jul-1991 + Moved into HAL + +--*/ + +#include "halp.h" +#include "ixkdcom.h" +#define TIMEOUT_COUNT 1024 * 200 + +UCHAR CpReadLsr (PCPPORT, UCHAR); + +extern BOOLEAN HalpOwnsDisplay; +static UCHAR LastLsr, LastMsr; + + +VOID +CpInitialize ( + PCPPORT Port, + PUCHAR Address, + ULONG Rate + ) + +/*++ + + Routine Description: + + Fill in the com port port object, set the initial baud rate, + turn on the hardware. + + Arguments: + + Port - address of port object + + Address - port address of the com port + (CP_COM1_PORT, CP_COM2_PORT) + + Rate - baud rate (CP_BD_150 ... CP_BD_19200) + +--*/ + +{ + PUCHAR hwport; + UCHAR mcr, ier; + + Port->Address = Address; + Port->Baud = 0; + + CpSetBaud(Port, Rate); + + // + // Assert DTR, RTS. + // + + hwport = Port->Address; + hwport += COM_MCR; + + mcr = MC_DTRRTS; + WRITE_PORT_UCHAR(hwport, mcr); + + hwport = Port->Address; + hwport += COM_IEN; + + ier = 0; + WRITE_PORT_UCHAR(hwport, ier); + +} + + + + +VOID +CpSetBaud ( + PCPPORT Port, + ULONG Rate + ) + +/*++ + + Routine Description: + + Set the baud rate for the port and record it in the port object. + + Arguments: + + Port - address of port object + + Rate - baud rate (CP_BD_150 ... CP_BD_56000) + +--*/ + +{ + ULONG divisorlatch; + PUCHAR hwport; + UCHAR lcr; + + // + // compute the divsor + // + + divisorlatch = CLOCK_RATE / Rate; + + // + // set the divisor latch access bit (DLAB) in the line control reg + // + + hwport = Port->Address; + hwport += COM_LCR; // hwport = LCR register + + lcr = READ_PORT_UCHAR(hwport); + + lcr |= LC_DLAB; + WRITE_PORT_UCHAR(hwport, lcr); + + // + // set the divisor latch value. + // + + hwport = Port->Address; + hwport += COM_DLM; // divisor latch msb + WRITE_PORT_UCHAR(hwport, (UCHAR)((divisorlatch >> 8) & 0xff)); + + hwport--; // divisor latch lsb + WRITE_PORT_UCHAR(hwport, (UCHAR)(divisorlatch & 0xff)); + + + // + // Set LCR to 3. (3 is a magic number in the original assembler) + // + + hwport = Port->Address; + hwport += COM_LCR; + WRITE_PORT_UCHAR(hwport, 3); + + + // + // Remember the baud rate + // + + Port->Baud = Rate; +} + + + +USHORT +CpQueryBaud ( + PCPPORT Port + ) + +/*++ + + Routine Description: + + Return the last value baud rate was set to. + + Arguments: + + Port - address of cpport object which describes the hw port of interest. + + Return Value: + + Baud rate. 0 = none has been set. + +--*/ + +{ + return (USHORT) Port->Baud; +} + +VOID +CpSendModemString ( + PCPPORT Port, + IN PUCHAR String + ) +/*++ + + Routine Description: + + Sends a command string to the modem. + This is down in order to aid the modem in determining th + baud rate the local connect is at. + + Arguments: + + Port - Address of CPPORT + String - String to send to modem + +--*/ +{ + static ULONG Delay; + TIME_FIELDS CurrentTime; + UCHAR i; + ULONG l; + + if (Port->Flags & PORT_SENDINGSTRING) + return ; + + Port->Flags |= PORT_SENDINGSTRING; + if (!Delay) { + // see how long 1 second is + HalQueryRealTimeClock (&CurrentTime); + l = CurrentTime.Second; + while (l == (ULONG) CurrentTime.Second) { + CpReadLsr(Port, 0); + HalQueryRealTimeClock (&CurrentTime); + Delay++; + } + Delay = Delay / 3; + } + + l = Delay; + while (*String) { + HalQueryRealTimeClock (&CurrentTime); + i = CpReadLsr (Port, 0); + if (i & COM_OUTRDY) { + if ((--l) == 0) { + WRITE_PORT_UCHAR(Port->Address+COM_DAT, *String); + String++; + l = Delay; + } + } + if (i & COM_DATRDY) + READ_PORT_UCHAR(Port->Address + COM_DAT); + } + Port->Flags &= ~PORT_SENDINGSTRING; +} + +UCHAR +CpReadLsr ( + PCPPORT Port, + UCHAR waiting + ) + +/*++ + + Routine Description: + + Read LSR byte from specified port. If HAL owns port & display + it will also cause a debug status to be kept up to date. + + Handles entering & exiting modem control mode for debugger. + + Arguments: + + Port - Address of CPPORT + + Returns: + + Byte read from port + +--*/ +{ + static UCHAR ringflag = 0; + static UCHAR diagout[3]; + static ULONG diagmsg[3] = { 'TRP ', 'LVO ', 'MRF ' }; + static UCHAR ModemString[] = "\n\rAT\n\r"; + TIME_FIELDS CurrentTime; + UCHAR lsr, msr, i; + ULONG diagstr[12]; + + lsr = READ_PORT_UCHAR(Port->Address + COM_LSR); + + if (lsr & COM_PE) + diagout[0] = 8; // Parity error + + if (lsr & COM_OE) + diagout[1] = 8; // Overflow error + + if (lsr & COM_FE) + diagout[2] = 8; // Framing error + + if (lsr & waiting) { + LastLsr = ~COM_DATRDY | (lsr & COM_DATRDY); + return lsr; + } + + msr = READ_PORT_UCHAR (Port->Address + COM_MSR); + + if (Port->Flags & PORT_MODEMCONTROL) { + if (msr & SERIAL_MSR_DCD) { + + // + // In modem control mode with carrier detect + // Reset carrier lost time + // + + Port->Flags |= PORT_NOCDLTIME | PORT_MDM_CD; + + } else { + + // + // In modem control mode, but no carrier detect. After + // 60 seconds drop out of modem control mode + // + + if (Port->Flags & PORT_NOCDLTIME) { + HalQueryRealTimeClock (&Port->CarrierLostTime); + Port->Flags &= ~PORT_NOCDLTIME; + ringflag = 0; + } + + HalQueryRealTimeClock (&CurrentTime); + if (CurrentTime.Minute != Port->CarrierLostTime.Minute && + CurrentTime.Second >= Port->CarrierLostTime.Second) { + + // + // It's been at least 60 seconds - drop out of + // modem control mode until next RI + // + + Port->Flags &= ~PORT_MODEMCONTROL; + CpSendModemString (Port, ModemString); + } + + if (Port->Flags & PORT_MDM_CD) { + + // + // We had a connection - if it's the connection has been + // down for a few seconds, then send a string to the modem + // + + if (CurrentTime.Second < Port->CarrierLostTime.Second) + CurrentTime.Second += 60; + + if (CurrentTime.Second > Port->CarrierLostTime.Second + 10) { + Port->Flags &= ~PORT_MDM_CD; + CpSendModemString (Port, ModemString); + } + } + } + } + + if ((lsr == LastLsr && msr == LastMsr) || !(Port->Flags & PORT_SAVED)) + return lsr; + + ringflag |= (msr & SERIAL_MSR_RI) ? 1 : 2; + if (ringflag == 3) { + + // + // The ring indicate line has toggled + // Use modem control from now on + // + + ringflag = 0; + Port->Flags |= PORT_MODEMCONTROL | PORT_NOCDLTIME; + Port->Flags &= ~PORT_MDM_CD; + + if (Port->Flags & PORT_DEFAULTRATE && Port->Baud != BD_9600) { + + // + // Baud rate was never specified switch + // to 9600 baud as default (for modem usage). + // + + HalDisplayString (MSG_DEBUG_9600); + CpSetBaud (Port, BD_9600); + //Port->Flags |= PORT_DISBAUD; + } + } + + for (i=0; i < 3; i++) { + if (diagout[i]) { + diagout[i]--; + diagstr[10-i] = diagmsg[i]; + } else { + diagstr[10-i] = ' '; + } + } + + diagstr[7] = (LastLsr & COM_DATRDY) ? 'VCR ' : ' '; + diagstr[6] = (lsr & COM_OUTRDY) ? ' ' : 'DNS '; + diagstr[5] = (msr & 0x10) ? 'STC ' : ' '; + diagstr[4] = (msr & 0x20) ? 'RSD ' : ' '; + diagstr[3] = (msr & 0x40) ? ' IR ' : ' '; + diagstr[2] = (msr & 0x80) ? ' DC ' : ' '; + diagstr[1] = (Port->Flags & PORT_MODEMCONTROL) ? 'MDM ' : ' '; + diagstr[0] = ' '; +#if 0 + if (Port->Flags & PORT_DISBAUD) { + switch (Port->Baud) { + case BD_9600: diagstr[0] = ' 69 '; break; + case BD_14400: diagstr[0] = 'K41 '; break; + case BD_19200: diagstr[0] = 'K91 '; break; + case BD_56000: diagstr[0] = 'K65 '; break; + } + } +#endif + + HalpDisplayDebugStatus ((PUCHAR) diagstr, 11*4); + LastLsr = lsr; + LastMsr = msr; + return lsr; +} + + + + +VOID +CpPutByte ( + PCPPORT Port, + UCHAR Byte + ) + +/*++ + + Routine Description: + + Write a byte out to the specified com port. + + Arguments: + + Port - Address of CPPORT object + + Byte - data to emit + +--*/ + +{ + UCHAR msr, lsr; + + // + // If modem control, make sure DSR, CTS and CD are all set before + // sending any data. + // + + while ((Port->Flags & PORT_MODEMCONTROL) && + (msr = READ_PORT_UCHAR(Port->Address + COM_MSR) & MS_DSRCTSCD) != MS_DSRCTSCD) { + + // + // If no CD, and there's a charactor ready, eat it + // + + lsr = CpReadLsr (Port, 0); + if ((msr & MS_CD) == 0 && (lsr & COM_DATRDY) == COM_DATRDY) { + READ_PORT_UCHAR(Port->Address + COM_DAT); + } + } + + // + // Wait for port to not be busy + // + + while (!(CpReadLsr(Port, COM_OUTRDY) & COM_OUTRDY)) ; + + // + // Send the byte + // + + WRITE_PORT_UCHAR(Port->Address + COM_DAT, Byte); +} + +USHORT +CpGetByte ( + PCPPORT Port, + PUCHAR Byte, + BOOLEAN WaitForByte + ) + +/*++ + + Routine Description: + + Fetch a byte and return it. + + Arguments: + + Port - address of port object that describes hw port + + Byte - address of variable to hold the result + + WaitForByte - flag indicates wait for byte or not. + + Return Value: + + CP_GET_SUCCESS if data returned. + + CP_GET_NODATA if no data available, but no error. + + CP_GET_ERROR if error (overrun, parity, etc.) + +--*/ + +{ + UCHAR lsr; + UCHAR value; + ULONG limitcount; + + // + // Make sure DTR and CTS are set + // + // (What does CTS have to do with reading from a full duplex line???) + + + // + // Check to make sure the CPPORT we were passed has been initialized. + // (The only time it won't be initialized is when the kernel debugger + // is disabled, in which case we just return.) + // + if (Port->Address == NULL) { + return(CP_GET_NODATA); + } + + limitcount = WaitForByte ? TIMEOUT_COUNT : 1; + while (limitcount != 0) { + limitcount--; + + lsr = CpReadLsr(Port, COM_DATRDY); + if ((lsr & COM_DATRDY) == COM_DATRDY) { + + // + // Check for errors + // + if (lsr & (COM_FE | COM_PE | COM_OE)) { + *Byte = 0; + return(CP_GET_ERROR); + } + + // + // fetch the byte + // + + value = READ_PORT_UCHAR(Port->Address + COM_DAT); + + if (Port->Flags & PORT_MODEMCONTROL) { + + // + // Using modem control. If no CD, then skip this byte. + // + + if ((READ_PORT_UCHAR(Port->Address + COM_MSR) & MS_CD) == 0) { + continue; + } + } + + *Byte = value & (UCHAR)0xff; + return CP_GET_SUCCESS; + } + } + + LastLsr = 0; + CpReadLsr (Port, 0); + return CP_GET_NODATA; +} + + + +BOOLEAN +CpDoesPortExist( + IN PUCHAR Address + ) + +/*++ + +Routine Description: + + This routine will attempt to place the port into its + diagnostic mode. If it does it will twiddle a bit in + the modem control register. If the port exists this + twiddling should show up in the modem status register. + + NOTE: This routine must be called before the device is + enabled for interrupts, this includes setting the + output2 bit in the modem control register. + + This is blatantly stolen from TonyE's code in ntos\dd\serial\serial.c. + +Arguments: + + Address - address of hw port. + +Return Value: + + TRUE - Port exists. Party on. + + FALSE - Port doesn't exist. Don't use it. + +--*/ + +{ + UCHAR OldModemStatus; + UCHAR ModemStatus; + BOOLEAN ReturnValue = TRUE; + + // + // Save the old value of the modem control register. + // + + OldModemStatus = READ_PORT_UCHAR(Address+COM_MCR); + + // + // Set the port into diagnostic mode. + // + + WRITE_PORT_UCHAR( + Address+COM_MCR, + SERIAL_MCR_LOOP + ); + + // + // Bang on it again to make sure that all the lower bits + // are clear. + // + + WRITE_PORT_UCHAR( + Address+COM_MCR, + SERIAL_MCR_LOOP + ); + + // + // Read the modem status register. The high for bits should + // be clear. + // + + ModemStatus = READ_PORT_UCHAR(Address+COM_MSR); + + if (ModemStatus & (SERIAL_MSR_CTS | SERIAL_MSR_DSR | + SERIAL_MSR_RI | SERIAL_MSR_DCD)) { + + ReturnValue = FALSE; + goto AllDone; + + } + + // + // So far so good. Now turn on OUT1 in the modem control register + // and this should turn on ring indicator in the modem status register. + // + + WRITE_PORT_UCHAR( + Address+COM_MCR, + (SERIAL_MCR_OUT1 | SERIAL_MCR_LOOP) + ); + + ModemStatus = READ_PORT_UCHAR(Address+COM_MSR); + + if (!(ModemStatus & SERIAL_MSR_RI)) { + + ReturnValue = FALSE; + goto AllDone; + + } + +AllDone: ; + + // + // Put the modem control back into a clean state. + // + + WRITE_PORT_UCHAR( + Address+COM_MCR, + OldModemStatus + ); + + return ReturnValue; + +} + -- cgit v1.2.3