/* Copyright (C) Magna Carta Software, Inc. 1989-1991. All Rights Reserved INS8250.C -- Routines to support the 8250 family (with the exception of NS16550A routines) that are not concerned with interrupts. */ #define CCT_DEVELOPMENT #include #if (defined(MSC) || defined(__WATCOMC__) || defined(_INTELC32_) || defined(__HIGHC__)) #include #endif #define SPEED_DIVISOR 1843200L /* U8250_DEINIT -- When finished -- close the port */ short u8250_deinit_(COMM_PORT *p) { u8250_deinstall_ipr_(p, LINE_STATUS); u8250_deinstall_ipr_(p, RECEIVE); u8250_deinstall_ipr_(p, TRANSMIT); u8250_deinstall_ipr_(p, MODEM_STATUS); u8250_restore_status_(p); return (0); } /* U8250_GET -- General purpose parameter retriever for the INS8250 family of UARTs. Return values: Success: The value of the UART register that sets stopbits; EOF : Invalid parameter; */ short u8250_get_(COMM_PORT *p, short cmd) { short ret = 0; if (cmd >= 0 && cmd <= 7) ret = u8250_get_reg_(p->ubase_addr + cmd, 0XFF); else switch (cmd) { /* DATA FORMAT */ case DATABITS: ret = u8250_get_reg_(p->uart.u8250.lcr_addr, U8250_DATABITS_MASK) + 5; break; case PARITY: ret = u8250_get_reg_(p->uart.u8250.lcr_addr, U8250_PARITY_MASK) >> 3; break; case STOPBITS: ret = (u8250_get_reg_(p->uart.u8250.lcr_addr, U8250_STOPBITS_MASK) >> 2) + 1; break; /* RS-232 */ case CTS: ret = u8250_get_reg_(p->uart.u8250.msr_addr, U8250_CTS_MASK) != 0; break; case DCD: ret = u8250_get_reg_(p->uart.u8250.msr_addr, U8250_DCD_MASK) != 0; break; case DSR: ret = u8250_get_reg_(p->uart.u8250.msr_addr, U8250_DSR_MASK) != 0; break; case DTR: ret = u8250_get_reg_(p->uart.u8250.mcr_addr, U8250_DTR_MASK); break; case RI: ret = u8250_get_reg_(p->uart.u8250.msr_addr, U8250_RI_MASK) != 0; break; case RTS: ret = u8250_get_reg_(p->uart.u8250.mcr_addr, U8250_RTS_MASK) != 0; break; case OUT1: ret = u8250_get_reg_(p->uart.u8250.mcr_addr, U8250_OUT1_MASK) != 0; break; case OUT2: ret = u8250_get_reg_(p->uart.u8250.mcr_addr, U8250_OUT2_MASK) != 0; break; case LOOPBACK: ret = u8250_get_reg_(p->uart.u8250.mcr_addr, U8250_LOOP_MASK) != 0; break; default: ret = EOF; } return (ret); } /* U8250_GET_REG_ -- General purpose command dispatcher for the INS8250 family of UARTs. Return values: Success: The value of the UART register that sets stopbits; EOF : Invalid parameter; */ short u8250_get_reg_(WORD reg, WORD mask) { return (inp(reg) & mask); } /* U8250_GET_RX_XLAT_ -- Query the setting of each of the input translation variables used in c_getc(). */ short u8250_get_rx_xlat_(COMM_PORT *p, short item) { short ret = 0; switch (item) { case XLAT: ret = isrxxlat(p); break; /* OFF or ON */ case LOCAL_ECHO: ret = isrxecholocal(p); break; /* OFF or ON */ case REMOTE_ECHO: ret = isrxechoremote(p); break;/* OFF or ON */ case PRINTER_ECHO: ret = isrxechoprn(p); break; /* OFF or ON */ case CAPTURE_BUFFER_ECHO: ret = isrxechobuf(p); break; /* OFF or ON */ /* EOL is one of: FALSE, LF2CR, CR2LF, EOL2SP, STRIPEOL, LF2CRLF, CR2CRLF, CRLF2LF, CRLF2CR */ case EOL: ret = p->rx_eol; break; case CASE: ret = p->rx_case_convert; break; case ASCII_ONLY: ret = p->rx_ascii_only; break; case INTERBYTE_DELAY: ret = p->rx_ibdelay; break; case FLOWCTL: ret = p->flowctl & RX; break; /* OFF or ON */ case XONCHAR: ret = p->xonchar; break; /* ASCII value */ case XOFFCHAR: ret = p->xoffchar; break; /* ASCII value */ case ABORT_KEY: ret = p->abort_key; break; default: ret = EOF; break; } return (ret); } /* U8250_GET_SPEED_ -- Get the data rate (bps) for the specified port. */ long u8250_get_speed_(COMM_PORT *p) { BYTE old_reg, old_ier; WORD divisor = 0; /* DISABLE INTERRUPTS */ old_ier = (BYTE) inp(p->uart.u8250.ier_addr); outp(p->uart.u8250.ier_addr, 0); /* SET THE DLAB */ old_reg = (BYTE) inp(p->uart.u8250.lcr_addr); outp(p->uart.u8250.lcr_addr, old_reg | 0X80); divisor = inp(p->ubase_addr+1) << 8; /* MSB */ divisor |= (BYTE) inp(p->ubase_addr); /* LSB */ /* RESET THE DLAB */ outp(p->uart.u8250.lcr_addr, old_reg & 0X7F); /* RE-ENABLE INTERRUPTS */ outp(p->uart.u8250.ier_addr, old_ier); return (divisor ? 115200L /divisor : (long) EOF); } /* U8250_GET_TX_XLAT_ -- Return the value of various transmit data translation parameters. */ short u8250_get_tx_xlat_(COMM_PORT *p, short item) { short ret = 0; switch (item) { case LOCAL_ECHO: ret = istxecholocal(p); break; /* OFF or ON */ case REMOTE_ECHO: ret = istxechoremote(p);break; /* OFF or ON */ case PRINTER_ECHO: ret = istxechoprn(p); break; /* OFF or ON */ case CAPTURE_BUFFER_ECHO: ret = istxechobuf(p); break; /* OFF or ON */ case XLAT: ret = istxxlat(p); break; /* OFF or ON */ /* EOL is one of: FALSE, LF2CR, CR2LF, EOL2SP, STRIPEOL, LF2CRLF, CR2CRLF, CRLF2LF, CRLF2CR */ case EOL: ret = p->tx_eol; break; case CASE: ret = p->tx_case_convert; break; case ASCII_ONLY: ret = p->tx_ascii_only; break; /* INTERBYTE AND INTERLINE DELAYS (in ms.). */ case INTERBYTE_DELAY: ret = p->tx_ibdelay; break; case INTERLINE_DELAY: ret = p->tx_ildelay; break; /* CHARACTER TO TRIGGER INTERLINE DELAY */ case INTERLINE_DELAY_CHAR: ret = p->tx_ildelay_ch; break; case FLOWCTL: ret = p->flowctl & TX; break; /* OFF or ON */ case TX_DELAY: ret = p->tx_delay; break; case TX_DELAYCHAR: ret = p->tx_delaych; break; case XONCHAR: ret = p->xonchar; break; /* ASCII value */ case XOFFCHAR: ret = p->xoffchar; break; /* ASCII value */ /* ASCII CODE OF KEY TO ABORT XOFF */ case ABORT_KEY: ret = p->abort_key; break; default: ret = EOF; break; } return (ret); } /* U8250_INIT -- Initialize the National Semiconductor 8250/A/B, 16450. */ short u8250_init(COMM_PORT *p, WORD addr, long speed, short databits, short parity, short stopbits) { short i; if (p->open) return (PORT_OPEN); /* INITIALIZE MEMBERS OF THE UART STRUCTURE */ p->ubase_addr = p->udata_reg_addr = addr; p->uart.u8250.rbr_addr = p->uart.u8250.thr_addr = addr; p->uart.u8250.ier_addr = addr + 1; p->uart.u8250.iir_addr = addr + 2; p->uart.u8250.lcr_addr = addr + 3; p->uart.u8250.mcr_addr = addr + 4; p->uart.u8250.lsr_addr = addr + 5; p->uart.u8250.msr_addr = addr + 6; /* INITIALIZE DATA IN THE COMM_PORT STRUCTURE */ p->utype = INS8250; p->rxdatabits = p->txdatabits = databits; p->parity = parity; p->stopbits = stopbits; p->speed = speed; p->dtr = p->rts = HIGH; p->out1 = p->out2 = LOW; p->xonchar = DC1; p->xoffchar = DC3; p->abort_key = ESCAPE; /* INITIALIZE FUNCTION POINTERS IN THE COMM_PORT STRUCTURE */ p->set = u8250_set_; p->get = u8250_get_; p->get_rx_xlat = u8250_get_rx_xlat_; p->set_rx_xlat = u8250_set_rx_xlat_; p->get_tx_xlat = u8250_get_tx_xlat_; p->set_tx_xlat = u8250_set_tx_xlat_; p->set_speed = u8250_set_speed_; p->get_speed = u8250_get_speed_; p->rxstat = p->rxstat_ = u8250_rxstat_; p->txstat = p->txstat_ = u8250_txstat_; p->c_read = p->c_read_ = u8250_read_; p->c_write = p->c_write_ = (short (*)(COMM_PORT *, WORD)) u8250_write_; p->install_ipr = u8250_install_ipr_; p->deinstall_ipr = u8250_deinstall_ipr_; p->deinit_port = u8250_deinit_; p->enable_comm_int = u8250_enable_comm_int_; #if defined(__NDPC__) printf("\nCalling init."); #endif p->disable_comm_int = u8250_disable_comm_int_; #if defined(__NDPC__) printf("\nCalling save_status"); #endif /* SAVE UART STARTUP REGISTER VALUES */ u8250_save_status_(p); for (i=0; i < 4; i++) p->a_ipr[i] = (void FAR_ *) com_dummy_; /* initialize isr ptrs. */ u8250_set_(p, OUT1, p->out1); u8250_set_(p, OUT2, p->out2); #if defined(__NDPC__) printf("\nCalling int_set"); #endif u8250_int_set_(p, 0XF, FALSE); /* disable all interrupts */ /* OPEN THE PORT */ return (c_open_(p)); } /* U8250_MOD_REG_ -- General purpose command dispatcher for the INS8250 family of UARTs. Return values: Success: The value of the UART register that sets stopbits; EOF : Invalid parmaeter; */ void u8250_mod_reg_(WORD reg, WORD mask, WORD value) { outp(reg, (inp(reg) & ~mask) | value); } /* U8250_READ_ -- read a byte from the data register of the UART of the specified serial port. Return: the byte read. */ short u8250_read_(COMM_PORT *p) { BYTE c; c = (BYTE) inp(p->udata_reg_addr); if (p->rxdatabits == 7) c &= 0X7F; else if (p->rxdatabits == 6) c &= 0X3F; else if (p->rxdatabits == 5) c &= 0X1F; return (c); } /* U8250_RESTORE_STATUS_ -- Restore the register state and speed setting of the 8250. */ void u8250_restore_status_(COMM_PORT *p) { u8250_set_speed_(p, p->old_speed); /* restore the port speed */ /* RESTORE UART STARTUP REGISTER VALUES */ outp(p->ubase_addr+1, p->uart.u8250.old_status[1]); outp(p->ubase_addr+3, p->uart.u8250.old_status[3]); outp(p->ubase_addr+4, p->uart.u8250.old_status[4]); outp(p->ubase_addr+5, p->uart.u8250.old_status[5]); outp(p->ubase_addr+6, p->uart.u8250.old_status[6]); } /* U8250_RXSTAT_ -- report whether a character is ready in the receive buffer Return: FALSE (NULL) if no byte is ready. Else TRUE. */ short u8250_rxstat_(COMM_PORT *p) { return (inp(p->uart.u8250.lsr_addr) & U8250_RX_MASK); } /* U8250_SAVE_STATUS_ -- Save the register state and speed setting of the 8250. */ void u8250_save_status_(COMM_PORT *p) { p->old_speed = u8250_get_speed_(p); /* save the port speed */ /* SAVE UART STARTUP REGISTER VALUES */ p->uart.u8250.old_status[1] = (BYTE) inp(p->ubase_addr+1); p->uart.u8250.old_status[2] = (BYTE) inp(p->ubase_addr+2); p->uart.u8250.old_status[3] = (BYTE) inp(p->ubase_addr+3); p->uart.u8250.old_status[4] = (BYTE) inp(p->ubase_addr+4); p->uart.u8250.old_status[5] = (BYTE) inp(p->ubase_addr+5); p->uart.u8250.old_status[6] = (BYTE) inp(p->ubase_addr+6); } /* U8250_SEND_BREAK_ -- Send a BREAK. */ short u8250_send_break_(COMM_PORT *p) { short reg; reg = inp(p->uart.u8250.lcr_addr); outp(p->uart.u8250.lcr_addr, reg | 64); mspause(300); outp(p->uart.u8250.lcr_addr, reg & U8250_BREAK_MASK); return (0); } /* U8250_SET_ -- General purpose command dispatcher for the INS8250 family of UARTs. Return values: 0 -- Success; EOF -- Invalid parameter; */ short u8250_set_(COMM_PORT *p, short cmd, short value) { short ret = 0; if (cmd >= 0 && cmd <= 7) u8250_mod_reg_(p->ubase_addr + cmd, 0XFF, value); else switch (cmd) { /* DATA FORMAT */ case DATABITS: ret = u8250_set_databits_(p, value); break; case PARITY: ret = u8250_set_parity_(p, value); break; case STOPBITS: ret = u8250_set_stopbits_(p, value); break; case BREAK: ret = u8250_send_break_(p); break; /* RS-232 */ case DTR: u8250_mod_reg_(p->uart.u8250.mcr_addr, U8250_DTR_MASK, value); break; case RTS: u8250_mod_reg_(p->uart.u8250.mcr_addr, U8250_RTS_MASK, value << 1); break; case OUT1: u8250_mod_reg_(p->uart.u8250.mcr_addr, U8250_OUT1_MASK, value << 2); break; case OUT2: u8250_mod_reg_(p->uart.u8250.mcr_addr, U8250_OUT2_MASK, value << 3); break; /* DIAGNOSTIC/OTHER */ case LOOPBACK: u8250_mod_reg_(p->uart.u8250.mcr_addr, U8250_LOOP_MASK, value << 4); break; default: ret = EOF; } return (ret); } /* U8250_SET_DATABITS_ -- Set the number of databits to be used in data transfer. */ short u8250_set_databits_(COMM_PORT *p, short value) { switch (value) { case DATABITS5: value = U8250_DATABITS5; break; case DATABITS6: value = U8250_DATABITS6; break; case DATABITS7: value = U8250_DATABITS7; break; case DATABITS8: value = U8250_DATABITS8; break; default: return (EOF); } outp(p->uart.u8250.lcr_addr, (inp(p->uart.u8250.lcr_addr) & ~U8250_DATABITS_MASK) | value); return (0); } /* U8250_SET_PARITY_ -- Set the parity for the designated serial port to the designated value. The type of UART is obtained from the COMM_PORT structure. Return values: Success: The new value of the UART line control register; EOF : Invalid parity value requested; */ short u8250_set_parity_(COMM_PORT *p, short value) { switch (value) { case PARITY_NONE: value = U8250_PARITY_NONE; break; case PARITY_ODD: value = U8250_PARITY_ODD; break; case PARITY_EVEN: value = U8250_PARITY_EVEN; break; case PARITY_MARK: value = U8250_PARITY_MARK; break; case PARITY_SPACE: value = U8250_PARITY_SPACE; break; default: return (EOF); } outp(p->uart.u8250.lcr_addr, (inp(p->uart.u8250.lcr_addr) & ~U8250_PARITY_MASK) | value); return (0); } /* U8250_SET_RX_XLAT_ -- Specify the type of input translation used by c_getc() and c_gets(). */ short u8250_set_rx_xlat_(COMM_PORT *p, short item, short value) { short ret = 0; switch (item) { case LOCAL_ECHO: case REMOTE_ECHO: case PRINTER_ECHO: case CAPTURE_BUFFER_ECHO: if (value) p->echo |= item; else p->echo &= ~item; break; /* EOL is one of: FALSE, LF2CR, CR2LF, EOL2SP, STRIPEOL, LF2CRLF, CR2CRLF, CRLF2LF, CRLF2CR */ case EOL: p->rx_eol = value; break; case CASE: /* UPPER, LOWER, FALSE */ p->rx_case_convert = value; break; case ASCII_ONLY: p->rx_ascii_only = value; break; case INTERBYTE_DELAY: p->rx_ibdelay = value; break; case TRAILINGBYTE_DELAY: p->rx_tbdelay = value; break; case FLOWCTL: if (value) p->flowctl |= (value >> 4); else p->flowctl &= ~RX; break; case FLOWHIGH: p->flowhigh = value; break; case XONCHAR: p->xonchar = value; break; /* ASCII value */ case XOFFCHAR: p->xoffchar = value; break; /* ASCII value */ /* SCAN CODE/CHARACTER CODE OF KEY TO ABORT */ case ABORT_KEY: p->abort_key = value; break; case RX_BUFFER_HWM: if ((value >= 0 && value <=100) && (p->rx_bufsiz/100 * value > p->lowwater)) p->highwater = p->rx_bufsiz/100 * value; break; case RX_BUFFER_LWM: if ((value >= 0 && value <=100) && (p->rx_bufsiz/100 * value <= p->highwater)) p->lowwater = p->rx_bufsiz/100 * value; break; default: ret = EOF; break; } if (!ret) { if (value) p->rx_xlat = 1; else p->rx_xlat = isrxxlat(p); } return (ret); } /* U8250_SET_SPEED_ -- Set the data rate (bps) for the INS8250 family. */ short u8250_set_speed_(COMM_PORT *p, long value) { BYTE old_reg, old_ier; unsigned divisor; /* DISABLE INTERRUPTS */ old_ier = (BYTE) inp(p->uart.u8250.ier_addr); outp(p->uart.u8250.ier_addr, 0); /* CALCULATE THE BAUD RATE */ divisor = (unsigned) (SPEED_DIVISOR / (value << 4)); old_reg = (BYTE) (inp(p->uart.u8250.lcr_addr)); outp(p->uart.u8250.lcr_addr, old_reg | 0X80); outp(p->ubase_addr, (BYTE) divisor); /* LSB */ outp(p->ubase_addr+1,(BYTE) (divisor >> 8)); /* MSB */ outp(p->uart.u8250.lcr_addr, old_reg & 0X7F); /* RE-ENABLE INTERRUPTS */ outp(p->uart.u8250.ier_addr, old_ier); return (0); } /* U8250_SET_STOPBITS_ -- Set the number of stopbits for the designated serial port to the designated value. Return values: Success: The value of the UART register that sets stopbits; EOF : Invalid number of stopbits specified; */ short u8250_set_stopbits_(COMM_PORT *p, short value) { switch (value) { case STOPBITS1: value = U8250_STOPBITS1; break; case STOPBITS15: value = U8250_STOPBITS15; break; case STOPBITS2: value = U8250_STOPBITS2; break; default: return (EOF); } outp(p->uart.u8250.lcr_addr, (inp(p->uart.u8250.lcr_addr) & ~U8250_STOPBITS_MASK) | value); return (0); } /* U8250_SET_TX_XLAT_ -- Assign values to data translation parameters. */ short u8250_set_tx_xlat_(COMM_PORT *p, short item, short value) { short ret = 0; switch (item) { case LOCAL_ECHO: case REMOTE_ECHO: case PRINTER_ECHO: case CAPTURE_BUFFER_ECHO: if (value) p->echo |= (item << 4); else p->echo &= ~(item << 4); break; /* EOL is one of: FALSE, LF2CR, CR2LF, EOL2SP, STRIPEOL, LF2CRLF, CR2CRLF, CRLF2LF, CRLF2CR */ case EOL: p->tx_eol = value; break; case CASE: /* UPPER, LOWER, NORMAL */ p->tx_case_convert = value; break; case ASCII_ONLY: p->tx_ascii_only = value; break; case INTERBYTE_DELAY: p->tx_ibdelay = value; break; case TX_DELAY: p->tx_delay = value; break; case TX_DELAYCHAR: p->tx_delaych = value; break; case INTERLINE_DELAY: p->tx_ildelay = value; break; case INTERLINE_DELAY_CHAR: p->tx_ildelay_ch = value; break; case FLOWCTL: if (value) p->flowctl |= value; else p->flowctl &= 0X8F; break; case XONCHAR: /* value to be considered an XON */ if (value == XON_ALL) p->xonchar = 0; /* any character */ else p->xonchar = value; /* a specific char. */ break; case XOFFCHAR: p->xoffchar = value; ret = 0; break; /* ASCII value */ /* SCAN CODE/CHARACTER CODE OF KEY TO ABORT XOFF */ case ABORT_KEY: p->abort_key = value; break; default: ret = EOF; break; } if (!ret) { if (value) p->tx_xlat = 1; else p->tx_xlat = istxxlat(p); } return (ret); } /* U8250_TXSTAT_ -- report space in UART transmit buffer; Return Value: 1 room for 1 byte; 0 no space; */ short u8250_txstat_(COMM_PORT *p) { return( inp(p->uart.u8250.lsr_addr) & U8250_TX_MASK); } /* U8250_WRITE_ -- write a byte to the data register of the UART of the specified serial port. */ void u8250_write_(COMM_PORT *p, WORD c) { outp(p->udata_reg_addr, c); }