Hi, The following diff contains a port of the FreeBSD driver for RocketPort PCI cards. The tty interface works with getty(8) and the cua part with cu for outbound connections. Some issues are still in, which I could not figure out yet. I worked on this driver for one and half year now, and think it ready to get in.
I've done a lot of clean up of the original FreeBSD code. Because there is no specification available, I kept most of the comments. I also switched the original polling mode, which is used in the FreeBSD and Linux version of the driver to an interrupt driven mode. This is my first major work in the driver section. I'm sure that I may miss something. So, even nitpicks are welcome :-) OK? Thanks, Jan Index: share/man/man4/rp.4 =================================================================== RCS file: share/man/man4/rp.4 diff -N share/man/man4/rp.4 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ share/man/man4/rp.4 2 Oct 2020 17:50:01 -0000 @@ -0,0 +1,54 @@ +.\" $OpenBSD$ +.\" +.\" Copyright (c) 2020 Jan Klemkow <j.klem...@wemelug.de> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: October 3 2020 $ +.Dt RP 4 +.Os +.Sh NAME +.Nm rp +.Nd Comtrol RocketPort Intelligent Serial Port Cards device driver +.Sh SYNOPSIS +.Cd "rp* at pci?" +.Sh DESCRIPTION +This driver provides an interface to RocketPort multiport serial boards. +.Pp +The device minor numbers for this driver are encoded as follows: +.Bd -literal + d c c u u u u u - bits in the minor device number + + bits meaning + ---- ------- + uuuuu physical serial line (i.e., unit) to use + 0-7 on a 8Y, 0-15 on a 16Y + + cc card number + + d dial-out flag +.Ed +.Sh SEE ALSO +.Xr com 4 , +.Xr cy 4 , +.Xr intro 4 , +.Xr pci 4 , +.Xr termios 4 , +.Xr tty 4 +.Sh AUTHORS +This driver was written under contract for Comtrol Corporation by +.An Theodore Ts'o Aq Mt ty...@mit.edu . +It was ported to OpenBSD by +.An Jan Klemkow Aq Mt j.klem...@wemelug.de . +.Sh BUGS +Modem control does not work properly. Index: sys/arch/amd64/amd64/conf.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/amd64/conf.c,v retrieving revision 1.71 diff -u -p -r1.71 conf.c --- sys/arch/amd64/amd64/conf.c 6 Jul 2020 04:32:25 -0000 1.71 +++ sys/arch/amd64/amd64/conf.c 2 Oct 2020 17:08:36 -0000 @@ -75,6 +75,12 @@ struct bdevsw bdevsw[] = }; int nblkdev = nitems(bdevsw); +/* open, close, read, write, ioctl, tty, mmap */ +#define cdev_pc_init(c,n) { \ + dev_init(c,n,open), dev_init(c,n,close), dev_init(c,n,read), \ + dev_init(c,n,write), dev_init(c,n,ioctl), dev_init(c,n,stop), \ + dev_init(c,n,tty), ttselect, dev_init(c,n,mmap), D_TTY } + /* open, close, read, ioctl */ #define cdev_joy_init(c,n) { \ dev_init(c,n,open), dev_init(c,n,close), dev_init(c,n,read), \ @@ -132,6 +138,8 @@ cdev_decl(lms); #include "opms.h" cdev_decl(pms); #endif +#include "rp.h" +cdev_decl(rp); #include "cy.h" cdev_decl(cy); #include "tun.h" @@ -222,7 +230,7 @@ struct cdevsw cdevsw[] = cdev_notdef(), /* 31 */ cdev_notdef(), /* 32 */ cdev_notdef(), /* 33 */ - cdev_notdef(), /* 34 */ + cdev_tty_init(NRP,rp), /* 34: Comtrol RocketPort serial port */ cdev_notdef(), /* 35: Microsoft mouse */ cdev_notdef(), /* 36: Logitech mouse */ cdev_notdef(), /* 37: Extended PS/2 mouse */ Index: sys/arch/amd64/conf/GENERIC =================================================================== RCS file: /cvs/src/sys/arch/amd64/conf/GENERIC,v retrieving revision 1.493 diff -u -p -r1.493 GENERIC --- sys/arch/amd64/conf/GENERIC 15 Sep 2020 18:31:14 -0000 1.493 +++ sys/arch/amd64/conf/GENERIC 2 Oct 2020 17:08:39 -0000 @@ -399,6 +399,7 @@ com* at puc? # options CY_HW_RTS #cy* at pci? # PCI cyclom serial card #cz* at pci? # Cyclades-Z multi-port serial boards +rp* at pci? # PCI RocketPort serial card lpt0 at isa? port 0x378 irq 7 # standard PC parallel ports #lpt1 at isa? port 0x278 Index: sys/conf/files =================================================================== RCS file: /cvs/src/sys/conf/files,v retrieving revision 1.691 diff -u -p -r1.691 files --- sys/conf/files 20 Jul 2020 00:15:59 -0000 1.691 +++ sys/conf/files 2 Oct 2020 17:08:36 -0000 @@ -334,6 +334,10 @@ file dev/ic/pcf8584.c pcfiic define ac97 file dev/ic/ac97.c ac97 +# Comtrol RocketPort multiport serial cards +device rp: tty +file dev/ic/rp.c rp & (rp_pci) needs-flag + # Cyclades Cyclom multiport serial cards device cy: tty file dev/ic/cy.c cy & (cy_isa | cy_pci) needs-flag Index: sys/dev/ic/rp.c =================================================================== RCS file: sys/dev/ic/rp.c diff -N sys/dev/ic/rp.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/ic/rp.c 2 Oct 2020 17:08:39 -0000 @@ -0,0 +1,1294 @@ +/* $OpenBSD$ */ +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) Comtrol Corporation <supp...@comtrol.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted prodived that the follwoing conditions + * are met. + * 1. Redistributions of source code must retain the above copyright + * notive, this list of conditions and the following disclainer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials prodided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Comtrol Corporation. + * 4. The name of Comtrol Corporation may not be used to endorse or + * promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COMTROL CORPORATION BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> + +#include <sys/types.h> +#include <sys/atomic.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/endian.h> +#include <sys/fcntl.h> +#include <sys/malloc.h> +#include <sys/tty.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <machine/bus.h> + +#include <dev/ic/rpreg.h> + +#define DEVNAME(_s) ((_s)->sc_dev.dv_xname) +#define DEVCUA(x) (minor(x) & 0x80) + +static const char RP_Version[] = "3.02"; + +static uint8_t RData[RDATASIZE] = { + 0x00, 0x09, 0xf6, 0x82, + 0x02, 0x09, 0x86, 0xfb, + 0x04, 0x09, 0x00, 0x0a, + 0x06, 0x09, 0x01, 0x0a, + 0x08, 0x09, 0x8a, 0x13, + 0x0a, 0x09, 0xc5, 0x11, + 0x0c, 0x09, 0x86, 0x85, + 0x0e, 0x09, 0x20, 0x0a, + 0x10, 0x09, 0x21, 0x0a, + 0x12, 0x09, 0x41, 0xff, + 0x14, 0x09, 0x82, 0x00, + 0x16, 0x09, 0x82, 0x7b, + 0x18, 0x09, 0x8a, 0x7d, + 0x1a, 0x09, 0x88, 0x81, + 0x1c, 0x09, 0x86, 0x7a, + 0x1e, 0x09, 0x84, 0x81, + 0x20, 0x09, 0x82, 0x7c, + 0x22, 0x09, 0x0a, 0x0a +}; + +static uint8_t RRegData[RREGDATASIZE]= { + 0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */ + 0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */ + 0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */ + 0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */ + 0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */ + 0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */ + 0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */ + 0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */ + 0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */ + 0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */ + 0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */ + 0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */ + 0x22, 0x09, 0x0a, 0x0a /* 30: Rx FIFO Enable */ +}; + +#if 0 +/* IRQ number to MUDBAC register 2 mapping */ +uint8_t sIRQMap[16] = +{ + 0,0,0,0x10,0x20,0x30,0,0,0,0x40,0x50,0x60,0x70,0,0,0x80 +}; +#endif + +uint8_t rp_sBitMapClrTbl[8] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f}; +uint8_t rp_sBitMapSetTbl[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; + +struct cfdriver rp_cd = { + NULL, "rp", DV_TTY +}; + +/* + * Read the AIOP idenfication number directly from an AIOP. + * + * Return: + * Flag AIOPID_XXXX if a valid AIOP is found, where X is replace by an + * identifying number. Returns -1 if no valid AIOP is found. + * + * Warnings: No context switches are allowed while executing this function. + */ +int +rp_read_aiopid(struct rp_softc *sc, int aiop) +{ + uint8_t AiopID; /* ID byte from AIOP */ + + rp_writeaiop1(sc, aiop, _CMD_REG, RESET_ALL); /* reset AIOP */ + rp_writeaiop1(sc, aiop, _CMD_REG, 0x0); + AiopID = rp_readaiop1(sc, aiop, _CHN_STAT0) & 0x07; + + if (AiopID == 0x06) + return (1); + + return (-1); /* AIOP does not exist */ +} + +/* + * Read the number of channels available in an AIOP directly from an AIOP. + * + * Return: The number of channels available + * + * The number of channels is determined by write/reads from identical offsets + * within the SRAM address spaces for channels 0 and 4. If the channel 4 space + * is mirrored to channel 0 it is a 4 channel AIOP, otherwise it is an 8 + * channel. + * + * Warnings: No context switches are allowed while executing this function. + */ +int +rp_read_aiop_numchan(struct rp_softc *sc, int aiop) +{ + uint16_t x, y; + + /* write to chan 0 SRAM */ + rp_writeaiop4(sc, aiop, _INDX_ADDR, 0x12340000L); + + /* read from SRAM, chan 0 */ + rp_writeaiop2(sc, aiop, _INDX_ADDR, 0); + x = rp_readaiop2(sc, aiop, _INDX_DATA); + + /* read from SRAM, chan 4 */ + rp_writeaiop2(sc, aiop, _INDX_ADDR, 0x4000); + y = rp_readaiop2(sc, aiop, _INDX_DATA); + + if (x != y) /* if different must be 8 chan */ + return (8); + + return (4); +} + +/* + * Initialization of a channel and channel structure + * + * Return: True if initialization succeeded, false if it fails because channel + * number exceeds number of channels available in AIOP. + * + * This function must be called before a channel can be used. + * + * Warnings: + * No range checking on any of the parameters is done. + * No context switches are allowed while executing this function. + */ +int +rp_init_chan(struct rp_softc *sc, struct rp_chan *ch, int AiopNum, int ChanNum) +{ + int i, ChOff; + static uint8_t R[4]; + + if (ChanNum >= sc->AiopNumChan[AiopNum]) + return (false); /* exceeds num chans in AIOP */ + + /* Channel, AIOP, and controller identifiers */ + ch->sc = sc; + ch->ChanID = sc->AiopID[AiopNum]; + ch->AiopNum = AiopNum; + ch->ChanNum = ChanNum; + + /* Initialize the channel from the RData array */ + for (i=0; i < RDATASIZE; i+=4) { + R[0] = RData[i]; + R[1] = RData[i+1] + 0x10 * ChanNum; + R[2] = RData[i+2]; + R[3] = RData[i+3]; + rp_writech4(ch, _INDX_ADDR, lemtoh32(R)); + } + + for (i = 0; i < RREGDATASIZE; i += 4) { + ch->R[i] = RRegData[i]; + ch->R[i+1] = RRegData[i+1] + 0x10 * ChanNum; + ch->R[i+2] = RRegData[i+2]; + ch->R[i+3] = RRegData[i+3]; + } + + /* Indexed registers */ + ChOff = (uint16_t)ChanNum * 0x1000; + + ch->BaudDiv[0] = (uint8_t)(ChOff + _BAUD); + ch->BaudDiv[1] = (uint8_t)((ChOff + _BAUD) >> 8); + ch->BaudDiv[2] = (uint8_t)RP_BRD9600; + ch->BaudDiv[3] = (uint8_t)(RP_BRD9600 >> 8); + rp_writech4(ch, _INDX_ADDR, lemtoh32(ch->BaudDiv)); + + ch->TxControl[0] = (uint8_t)(ChOff + _TX_CTRL); + ch->TxControl[1] = (uint8_t)((ChOff + _TX_CTRL) >> 8); + ch->TxControl[2] = 0; + ch->TxControl[3] = 0; + rp_writech4(ch, _INDX_ADDR, lemtoh32(ch->TxControl)); + + ch->RxControl[0] = (uint8_t)(ChOff + _RX_CTRL); + ch->RxControl[1] = (uint8_t)((ChOff + _RX_CTRL) >> 8); + ch->RxControl[2] = 0; + ch->RxControl[3] = 0; + rp_writech4(ch, _INDX_ADDR, lemtoh32(ch->RxControl)); + + ch->TxEnables[0] = (uint8_t)(ChOff + _TX_ENBLS); + ch->TxEnables[1] = (uint8_t)((ChOff + _TX_ENBLS) >> 8); + ch->TxEnables[2] = 0; + ch->TxEnables[3] = 0; + rp_writech4(ch, _INDX_ADDR, lemtoh32(ch->TxEnables)); + + ch->TxCompare[0] = (uint8_t)(ChOff + _TXCMP1); + ch->TxCompare[1] = (uint8_t)((ChOff + _TXCMP1) >> 8); + ch->TxCompare[2] = 0; + ch->TxCompare[3] = 0; + rp_writech4(ch, _INDX_ADDR, lemtoh32(ch->TxCompare)); + + ch->TxReplace1[0] = (uint8_t)(ChOff + _TXREP1B1); + ch->TxReplace1[1] = (uint8_t)((ChOff + _TXREP1B1) >> 8); + ch->TxReplace1[2] = 0; + ch->TxReplace1[3] = 0; + rp_writech4(ch, _INDX_ADDR, lemtoh32(ch->TxReplace1)); + + ch->TxReplace2[0] = (uint8_t)(ChOff + _TXREP2); + ch->TxReplace2[1] = (uint8_t)((ChOff + _TXREP2) >> 8); + ch->TxReplace2[2] = 0; + ch->TxReplace2[3] = 0; + rp_writech4(ch, _INDX_ADDR, lemtoh32(ch->TxReplace2)); + + ch->TxFIFOPtrs = ChOff + _TXF_OUTP; + ch->TxFIFO = ChOff + _TX_FIFO; + + /* apply reset Tx FIFO count */ + rp_writech1(ch, _CMD_REG, (uint8_t)ChanNum | RESTXFCNT); + + rp_writech1(ch, _CMD_REG, (uint8_t)ChanNum); /* remove reset Tx FIFO count */ + rp_writech2(ch, _INDX_ADDR, ch->TxFIFOPtrs); /* clear Tx in/out ptrs */ + rp_writech2(ch, _INDX_DATA, 0); + ch->RxFIFOPtrs = ChOff + _RXF_OUTP; + ch->RxFIFO = ChOff + _RX_FIFO; + + /* apply reset Rx FIFO count */ + rp_writech1(ch, _CMD_REG, (uint8_t)ChanNum | RESRXFCNT); + + rp_writech1(ch, _CMD_REG, (uint8_t)ChanNum); /* remove reset Rx FIFO count */ + rp_writech2(ch, _INDX_ADDR, ch->RxFIFOPtrs); /* clear Rx out ptr */ + rp_writech2(ch, _INDX_DATA, 0); + rp_writech2(ch, _INDX_ADDR, ch->RxFIFOPtrs + 2); /* clear Rx in ptr */ + rp_writech2(ch, _INDX_DATA, 0); + ch->TxPrioCnt = ChOff + _TXP_CNT; + rp_writech2(ch, _INDX_ADDR, ch->TxPrioCnt); + rp_writech1(ch, _INDX_DATA, 0); + ch->TxPrioPtr = ChOff + _TXP_PNTR; + rp_writech2(ch, _INDX_ADDR, ch->TxPrioPtr); + rp_writech1(ch, _INDX_DATA, 0); + ch->TxPrioBuf = ChOff + _TXP_BUF; + rp_enable_rx_processor(ch); /* start the rx processor */ + + return (true); +} + +/* + * Stop the receive processor from processing a channel. + * + * The receive processor can be started again with rp_start_rx_processor(). + * This function causes the receive processor to skip over the stopped channel. + * It does not stop it from processing other channels. + * + * Warnings: + * No context switches are allowed while executing this function. + * Do not leave the receive processor stopped for more than one character time. + * After calling this function a delay of 4 uS is required to ensure that the + * receive processor is no longer processing this channel. + */ +void +rp_stop_rx_processor(struct rp_chan *ch) +{ + uint8_t R[4]; + + R[0] = ch->R[0]; + R[1] = ch->R[1]; + R[2] = 0x0a; + R[3] = ch->R[3]; + + rp_writech4(ch, _INDX_ADDR, lemtoh32(R)); +} + +/* + * To prevent data from being enqueued or dequeued in the Tx FIFO while it is + * being flushed the receive processor is stopped and the transmitter is + * disabled. After these operations a 4 uS delay is done before clearing the + * pointers to allow the receive processor to stop. These items are handled + * inside this function. + * + * Warnings: No context switches are allowed while executing this function. + */ +void +rp_flush_rx_fifo(struct rp_chan *ch) +{ + int RxFIFOEnabled = false; /* true if Rx FIFO enabled */ + uint8_t Ch; /* channel number within AIOP */ + + if (rp_get_rx_cnt(ch) == 0) /* Rx FIFO empty */ + return; /* don't need to flush */ + + if (ch->R[0x32] == 0x08) { /* Rx FIFO is enabled */ + RxFIFOEnabled = true; + rp_disable_rx_fifo(ch); /* disable it */ + delay(2); /* delay 2 uS to allow proc to disable FIFO */ + } + rp_chan_status(ch); /* clear any pending Rx errors in chan stat */ + Ch = (uint8_t)ch->ChanNum; + rp_writech1(ch, _CMD_REG, Ch | RESRXFCNT); /* apply reset Rx FIFO count */ + rp_writech1(ch, _CMD_REG, Ch); /* remove reset Rx FIFO count */ + rp_writech2(ch, _INDX_ADDR, ch->RxFIFOPtrs); /* clear Rx out ptr */ + rp_writech2(ch, _INDX_DATA, 0); + rp_writech2(ch, _INDX_ADDR, ch->RxFIFOPtrs + 2);/* clear Rx in ptr */ + rp_writech2(ch, _INDX_DATA, 0); + + if (RxFIFOEnabled) + rp_enable_rx_fifo(ch); +} + +/* + * To prevent data from being enqueued or dequeued in the Tx FIFO while it is + * being flushed the receive processor is stopped and the transmitter is + * disabled. After these operations a 4 uS delay is done before clearing the + * pointers to allow the receive processor to stop. These items are handled + * inside this function. + * + * Warnings: No context switches are allowed while executing this function. + */ +void +rp_flush_tx_fifo(struct rp_chan *ch) +{ + int TxEnabled = false; /* true if transmitter enabled */ + uint8_t Ch; /* channel number within AIOP */ + + if (rp_get_tx_cnt(ch) == 0) /* Tx FIFO empty */ + return; /* don't need to flush */ + + if (ch->TxControl[3] & TX_ENABLE) { + TxEnabled = true; + rp_disable_transmit(ch); /* disable transmitter */ + } + + rp_stop_rx_processor(ch); /* stop Rx processor */ + delay(4); /* delay 4 uS to allow proc to stop */ + Ch = (uint8_t)ch->ChanNum; + rp_writech1(ch, _CMD_REG, Ch | RESTXFCNT); /* apply reset Tx FIFO count */ + rp_writech1(ch, _CMD_REG, Ch); /* remove reset Tx FIFO count */ + rp_writech2(ch, _INDX_ADDR, ch->TxFIFOPtrs); /* clear Tx in/out ptrs */ + rp_writech2(ch, _INDX_DATA, 0); + + if (TxEnabled) + rp_enable_transmit(ch); /* enable transmitter */ + + rp_start_rx_processor(ch); /* restart Rx processor */ +} + +/* + * Write a byte of priority transmit data to a channel. + * + * Returns 1 if the bytes is successfully written, otherwise 0. The priority + * byte is transmitted before any data in the Tx FIFO. + * + * Warnings: No context switches are allowed while executing this function. + */ +int +rp_write_tx_prio_byte(struct rp_chan *ch, uint8_t data) +{ + uint8_t DWBuf[4]; /* buffer for double word writes */ + + if (rp_get_tx_cnt(ch) > 1) { /* write it to Tx priority buffer */ + /* get priority buffer status */ + rp_writech2(ch, _INDX_ADDR, ch->TxPrioCnt); + + /* priority buffer busy */ + if (rp_readch1(ch, _INDX_DATA) & PRI_PEND) + return (0); /* nothing sent */ + + htolem16(DWBuf, ch->TxPrioBuf);/* data byte address */ + + DWBuf[2] = data; /* data byte value */ + DWBuf[3] = 0; /* priority buffer pointer */ + rp_writech4(ch, _INDX_ADDR, lemtoh32(DWBuf)); /* write it out */ + + htolem16(DWBuf, ch->TxPrioCnt); /* Tx priority count address */ + + DWBuf[2] = PRI_PEND + 1; /* indicate 1 byte pending */ + DWBuf[3] = 0; /* priority buffer pointer */ + rp_writech4(ch, _INDX_ADDR, lemtoh32(DWBuf)); /* write it out */ + } else { + /* write it to Tx FIFO */ + rp_write_tx_byte(ch, rp_txrx_data_io(ch), data); + } + + return (1); /* 1 byte sent */ +} + +/* + * Enable one or more interrupts for a channel + * + * rp_enable_interrupts(ch, flags) + * struct rp_chan *ch; Ptr to channel structure + * uint16_t flags: Interrupt enable flags, can be any combination + * of the following flags: + * TXINT_EN: Interrupt on Tx FIFO empty + * RXINT_EN: Interrupt on Rx FIFO at trigger level (see + * rp_rx_trigger()) + * SRCINT_EN: Interrupt on SRC (Special Rx Condition) + * MCINT_EN: Interrupt on modem input change + * CHANINT_EN: Allow channel interrupt signal to the AIOP's + * Interrupt Channel Register. + * + * If an interrupt enable flag is set in flags, that interrupt will be enabled. + * If an interrupt enable flag is not set in flags, that interrupt will not be + * changed. Interrupts can be disabled with function rp_disable_interrupts(). + * + * This function sets the appropriate bit for the channel in the AIOP's + * Interrupt Mask Register if the CHANINT_EN flag is set. This allows this + * channel's bit to be set in the AIOP's Interrupt Channel Register. + * + * Interrupts must also be globally enabled before channel interrupts will be + * passed on to the host. This is done with function sEnGlobalInt(). + * + * In some cases it may be desirable to disable interrupts globally but enable + * channel interrupts. This would allow the global interrupt status register + * to be used to determine which AIOPs need service. + */ +void +rp_enable_interrupts(struct rp_chan *ch, uint16_t flags) +{ + ch->RxControl[2] |= ((uint8_t)flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); + rp_writech4(ch, _INDX_ADDR, lemtoh32(ch->RxControl)); + + ch->TxControl[2] |= ((uint8_t)flags & TXINT_EN); + rp_writech4(ch, _INDX_ADDR, lemtoh32(ch->TxControl)); + + if (flags & CHANINT_EN) { + uint8_t Mask; /* Interrupt Mask Register */ + + Mask = rp_readch1(ch,_INT_MASK) | rp_sBitMapSetTbl[ch->ChanNum]; + rp_writech1(ch, _INT_MASK, Mask); + } +} + +/* + * Disable one or more interrupts for a channel + * + * rp_disable_interrupts(ch, flags) + * struct rp_chan *ch; Ptr to channel structure + * uint16_t flags: Interrupt flags, can be any combination + * of the following flags: + * TXINT_EN: Interrupt on Tx FIFO empty + * RXINT_EN: Interrupt on Rx FIFO at trigger level (see + * rp_rx_trigger()) + * SRCINT_EN: Interrupt on SRC (Special Rx Condition) + * MCINT_EN: Interrupt on modem input change + * CHANINT_EN: Disable channel interrupt signal to the + * AIOP's Interrupt Channel Register. + * + * If an interrupt flag is set in flags, that interrupt will be disabled. If + * an interrupt flag is not set in flags, that interrupt will not be changed. + * Interrupts can be enabled with function rp_enable_interrupts(). + * + * This function clears the appropriate bit for the channel in the AIOP's + * Interrupt Mask Register if the CHANINT_EN flag is set. This blocks this + * channel's bit from being set in the AIOP's Interrupt Channel Register. + */ +void +rp_disable_interrupts(struct rp_chan *ch, uint16_t flags) +{ + ch->RxControl[2] &= + ~((uint8_t)flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); + rp_writech4(ch, _INDX_ADDR, lemtoh32(ch->RxControl)); + + ch->TxControl[2] &= ~((uint8_t)flags & TXINT_EN); + rp_writech4(ch, _INDX_ADDR, lemtoh32(ch->TxControl)); + + if (flags & CHANINT_EN) { /* Interrupt Mask Register */ + uint8_t mask = rp_readch1(ch,_INT_MASK) & rp_sBitMapClrTbl[ch->ChanNum]; + rp_writech1(ch, _INT_MASK, mask); + } +} + +/* + * Begin OS-specific driver code + */ + +#define RP_ISMULTIPORT(dev) ((dev)->id_flags & 0x1) +#define RP_MPMASTER(dev) (((dev)->id_flags >> 8) & 0xff) +#define RP_NOTAST4(dev) ((dev)->id_flags & 0x04) + +/* + * The top-level routines begin here + */ +int rpclose(dev_t dev, int, int, struct proc *); +void rphardclose(struct tty *, struct rp_port *); +/* TODO: int rpmodem(struct tty *, int, int); */ +int rpparam(struct tty *, struct termios *); +void rpstart(struct tty *); +struct tty *rptty(dev_t); +int rpioctl(dev_t dev, u_long , caddr_t, int, struct proc *); +int rpopen(dev_t dev, int, int, struct proc *); + +static void +rp_do_receive(struct rp_port *rp, struct tty *tp, struct rp_chan *cp, + unsigned int ChanStatus) +{ + unsigned int CharNStat; + int ToRecv, ch, s; + + ToRecv = rp_get_rx_cnt(cp); +#ifdef RP_DEBUG2 + printf("%s port %d receive: %d bytes\n", DEVNAME(rp->rp_sc), + RP_PORT(tp->t_dev), ToRecv); +#endif + if (ToRecv == 0) + return; + + /* + * If status indicates there are errored characters in the FIFO, then + * enter status mode (a word in FIFO holds characters and status) + */ + if (ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) { + if (!(ChanStatus & STATMODE)) { + ChanStatus |= STATMODE; + rp_enable_rx_status_mode(cp); + } + } + + /* + * if we previously entered status mode then read down the FIFO one + * word at a time, pulling apart the character and the status. Update + * error counters depending on status. + */ + s = spltty(); + if (ChanStatus & STATMODE) { + while (ToRecv) { + CharNStat = rp_readch2(cp, rp_txrx_data_io(cp)); + ch = CharNStat & 0xff; + + if ((CharNStat & STMBREAK) || (CharNStat & STMFRAMEH)) + ch |= TTY_FE; + else if (CharNStat & STMPARITYH) + ch |= TTY_PE; + else if (CharNStat & STMRCVROVRH) { + rp->rp_overflows++; + + printf("%s port %d tty overrun\n", + DEVNAME(rp->rp_sc), RP_PORT(tp->t_dev)); + } + + (*linesw[tp->t_line].l_rint)(ch, tp); + ToRecv--; + } + + /* After emtying FIFO in status mode, turn off status mode */ + if (rp_get_rx_cnt(cp) == 0) + rp_dis_rx_status_mode(cp); + } else { + ToRecv = rp_get_rx_cnt(cp); + while (ToRecv) { + ch = rp_readch1(cp, rp_txrx_data_io(cp)); + (*linesw[tp->t_line].l_rint)(ch & 0xff, tp); + ToRecv--; + } + } + splx(s); +} + +static void +rp_handle_port(struct rp_port *rp) +{ + struct rp_chan *cp; + struct tty *tp; + unsigned int IntMask; + unsigned int ChanStatus; + unsigned int oldcts; + + if (rp == NULL) + return; + + cp = &rp->rp_channel; + tp = rp->rp_tty; + IntMask = rp_chan_intr_id(cp); + IntMask = IntMask & rp->rp_intmask; + ChanStatus = rp_chan_status(cp); + if (IntMask & RXF_TRIG) + rp_do_receive(rp, tp, cp, ChanStatus); + + if (IntMask & DELTA_CD) { + if (ChanStatus & CD_ACT) { + (void)(*linesw[tp->t_line].l_modem)(tp, 1); + } else { + (void)(*linesw[tp->t_line].l_modem)(tp, 0); + } + } + + oldcts = rp->rp_cts; + rp->rp_cts = ((ChanStatus & CTS_ACT) != 0); + CLR(tp->t_state, TS_BUSY); // XXX: not sure with this + if (oldcts != rp->rp_cts) { + CLR(tp->t_state, TS_BUSY | TS_FLUSH); +#ifdef RP_DEBUG + printf("CTS change (now %s)... on port %d\n", rp->rp_cts ? "on" : "off", rp->rp_port); +#endif + (*linesw[tp->t_line].l_start)(tp); + } +} + +void +rp_poll(struct rp_port *rp) +{ + struct rp_softc *sc = rp->rp_sc; + struct tty *tp = rp->rp_tty; + int count; + unsigned char AiopMask; + + if (sc->ctlmask(sc) & (1 << rp->rp_aiop)) { + AiopMask = rp_aiop_intr_status(sc, rp->rp_aiop); + if (AiopMask & (1 << rp->rp_chan)) { + rp_handle_port(rp); + } + } + + count = rp_get_tx_cnt(&rp->rp_channel); + if (count > 0) + rpstart(tp); +} + +int +rp_attach(struct rp_softc *sc, int num_aiops, int num_ports) +{ + struct rp_port *rp; + struct tty *tp; + int unit; + int num_chan; + int aiop, chan, port; + int ChanStatus; + int retval; + + unit = sc->sc_dev.dv_unit; + + printf(" rp%d (Version %s) %d ports\n", unit, RP_Version, num_ports); + + sc->num_ports = num_ports; + sc->sc_rp = rp = mallocarray(num_ports, sizeof(*rp), M_DEVBUF, M_NOWAIT|M_ZERO); + + if (rp == NULL) { + printf("%s port %d rp_attach: could not malloc\n", DEVNAME(sc), + port); + retval = ENOMEM; + goto nogo; + } + + port = 0; + for (aiop = 0; aiop < num_aiops; aiop++) { + num_chan = sc->AiopNumChan[aiop]; + for (chan = 0; chan < num_chan; chan++, port++, rp++) { + rp->rp_tty = tp = ttymalloc(0); + tp->t_oproc = rpstart; + tp->t_param = rpparam; + + rp->rp_port = port; + rp->rp_sc = sc; + rp->rp_unit = unit; + rp->rp_chan = chan; + rp->rp_aiop = aiop; + + rp->rp_intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | + DELTA_CD | DELTA_CTS | DELTA_DSR; +#ifdef notdef + ChanStatus = rp_chan_status(&rp->rp_channel); +#endif /* notdef */ + if (rp_init_chan(sc, &rp->rp_channel, aiop, chan) == 0){ + printf("%s port %d init channel (%d, %d, %d) failed.\n", + DEVNAME(sc), port, unit, aiop, chan); + retval = ENXIO; + goto nogo; + } + ChanStatus = rp_chan_status(&rp->rp_channel); + rp->rp_cts = (ChanStatus & CTS_ACT) != 0; + } + } + + mtx_init(&sc->hwmtx, IPL_TTY); + sc->hwmtx_init = 1; + return (0); + nogo: + rp_releaseresource(sc); + return (retval); +} + +void +rp_releaseresource(struct rp_softc *sc) +{ + if (sc->sc_rp != NULL) { + int i; + + for (i = 0; i < sc->num_ports; i++) { + struct rp_port *rp = sc->sc_rp + i; + ttyfree(rp->rp_tty); + } + free(sc->sc_rp, M_DEVBUF, sizeof(*(sc->sc_rp)) * sc->num_ports); + sc->sc_rp = NULL; + } +} + +int +rp_intr(void *arg) +{ + struct rp_softc *sc = arg; + int i; + + if (sc->sc_rp == NULL) + return 0; + + for (i = 0; i < sc->num_ports; i++) + rp_poll(sc->sc_rp + i); + +#define _PCI_INT_FUNC 0x3A +#define PCI_STROB 0x2000 +#define INTR_EN_PCI 0x0010 + + rp_writeio2(sc, 0, _PCI_INT_FUNC, PCI_STROB | INTR_EN_PCI); + + return 1; +} + +int +rpopen(dev_t dev, int flag, int mode, struct proc *p) +{ + int card = RP_CARD(dev); + int port = RP_PORT(dev); + struct rp_softc *sc; + struct rp_port *rp; + struct tty *tp; + int flags = 0; + int error = 0; + int s; + + if (card >= rp_cd.cd_ndevs || (sc = rp_cd.cd_devs[card]) == NULL) + return (ENXIO); + +#ifdef RP_DEBUG + printf("%s open port %d flag 0x%x mode 0x%x\n", DEVNAME(sc), port, flag, + mode); +#endif + + rp = &sc->sc_rp[port]; + + s = spltty(); + if (rp->rp_tty == NULL) + rp->rp_tty = ttymalloc(0); + splx(s); + + tp = rp->rp_tty; + tp->t_oproc = rpstart; + tp->t_param = rpparam; + tp->t_dev = dev; + if (!ISSET(tp->t_state, TS_ISOPEN)) { + SET(tp->t_state, TS_WOPEN); + ttychars(tp); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_cflag = TTYDEF_CFLAG; + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; + + if (ISSET(rp->rp_swflags, TIOCFLAG_CLOCAL)) + SET(tp->t_termios.c_cflag, CLOCAL); + + s = spltty(); + + rpparam(tp, &tp->t_termios); + ttsetwater(tp); + + /* No carrier detect support. */ + SET(tp->t_state, TS_CARR_ON); + + /* XXX: this block may not be here?! */ + flags |= SET_RTS; + flags |= SET_DTR; + rp->rp_channel.TxControl[3] = + ((rp->rp_channel.TxControl[3] & ~(SET_RTS | SET_DTR)) | flags); + rp_writech4(&rp->rp_channel,_INDX_ADDR, lemtoh32(rp->rp_channel.TxControl)); + rp_rx_trigger(&rp->rp_channel, TRIG_1); + rp_dis_rx_status_mode(&rp->rp_channel); + rp_flush_rx_fifo(&rp->rp_channel); + rp_flush_tx_fifo(&rp->rp_channel); + + rp_enable_interrupts(&rp->rp_channel, + (TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN)); + rp_rx_trigger(&rp->rp_channel, TRIG_1); + + rp_dis_rx_status_mode(&rp->rp_channel); + rp_clr_tx_xoff(&rp->rp_channel); + +// rp_disable_RTS_flowctl(&rp->rp_channel); +// rp_disable_CTS_flowctl(&rp->rp_channel); + + rp_disable_tx_soft_flowctl(&rp->rp_channel); + rp_start_rx_processor(&rp->rp_channel); + + rp_enable_rx_fifo(&rp->rp_channel); + rp_enable_transmit(&rp->rp_channel); + +// rp_set_DTR(&rp->rp_channel); +// rp_set_RTS(&rp->rp_channel); + } else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0) { + return (EBUSY); + } else { + s = spltty(); + } + + if (DEVCUA(dev)) { + if (ISSET(tp->t_state, TS_ISOPEN)) { + /* Ah, but someone already is dialed in... */ + splx(s); + return (EBUSY); + } + rp->rp_cua = 1; + } else { + /* tty (not cua) device; wait for carrier if necessary. */ + if (ISSET(flag, O_NONBLOCK)) { + if (rp->rp_cua) { + /* Opening TTY non-blocking... but the CUA is busy. */ + splx(s); + return (EBUSY); + } + } else { + while (rp->rp_cua || (!ISSET(tp->t_cflag, CLOCAL) && + !ISSET(tp->t_state, TS_CARR_ON))) { + + SET(tp->t_state, TS_WOPEN); + error = ttysleep(tp, &tp->t_rawq, + TTIPRI | PCATCH, ttopen); + + /* + * If TS_WOPEN has been reset, that means the + * cua device has been closed. + * We don't want to fail in that case, + * so just go around again. + */ + if (error && ISSET(tp->t_state, TS_WOPEN)) { + CLR(tp->t_state, TS_WOPEN); + splx(s); + return (error); + } + } + } + } + splx(s); + + return ((*linesw[tp->t_line].l_open)(dev, tp, p)); +} + +int +rpclose(dev_t dev, int flag, int mode, struct proc *p) +{ + int card = RP_CARD(dev); + int port = RP_PORT(dev); + struct rp_softc *sc = rp_cd.cd_devs[card]; + struct rp_port *rp = &sc->sc_rp[port]; + struct tty *tp = rp->rp_tty; + int s; + +#ifdef RP_DEBUG + printf("%s close port %d flag 0x%x mode 0x%x\n", DEVNAME(sc), port, + flag, mode); +#endif + + if (!ISSET(tp->t_state, TS_ISOPEN)) + return (0); + + (*linesw[tp->t_line].l_close)(tp, flag, p); + + s = spltty(); + + if (!ISSET(tp->t_state, TS_WOPEN)) + rphardclose(tp, rp); + + CLR(tp->t_state, TS_BUSY | TS_FLUSH); + rp->rp_cua = 0; + splx(s); + ttyclose(tp); + + return (0); +} + +void +rphardclose(struct tty *tp, struct rp_port *rp) +{ + struct rp_chan *cp = &rp->rp_channel; + + rp_flush_rx_fifo(cp); + rp_flush_tx_fifo(cp); + rp_disable_transmit(cp); + rp_disable_interrupts(cp, TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN); + rp_disable_RTS_flowctl(cp); + rp_disable_CTS_flowctl(cp); + rp_disable_tx_soft_flowctl(cp); + rp_clr_tx_xoff(cp); + +#ifdef DJA + if (tp->t_cflag&HUPCL || !(tp->t_state & TS_ISOPEN) || !tp->t_actout) + rp_clr_DTR(cp); + + if (ISCALLOUT(tp->t_dev)) + rp_clr_DTR(cp); + + tp->t_actout = false; + wakeup(&tp->t_actout); + wakeup(TSA_CARR_ON(tp)); +#endif /* DJA */ +} + +int +rpread(dev_t dev, struct uio *uio, int flag) +{ + int card = RP_CARD(dev); + int port = RP_PORT(dev); + struct rp_softc *sc = rp_cd.cd_devs[card]; + struct rp_port *rp = &sc->sc_rp[port]; + struct tty *tp = rp->rp_tty; + +#ifdef RP_DEBUG1 + printf("%s read port %d uio %p flag 0x%x\n", DEVNAME(sc), port, uio, + flag); +#endif + + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} + +int +rpwrite(dev_t dev, struct uio *uio, int flag) +{ + int card = RP_CARD(dev); + int port = RP_PORT(dev); + struct rp_softc *sc = rp_cd.cd_devs[card]; + struct rp_port *rp = &sc->sc_rp[port]; + struct tty *tp = rp->rp_tty; + +#ifdef RP_DEBUG1 + printf("%s write port %d uio %p flag 0x%x\n", DEVNAME(sc), port, uio, + flag); +#endif + + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); +} + +struct tty * +rptty(dev_t dev) +{ + int card = RP_CARD(dev); + int port = RP_PORT(dev); + struct rp_softc *sc = rp_cd.cd_devs[card]; + struct rp_port *rp = &sc->sc_rp[port]; + struct tty *tp = rp->rp_tty; + + return (tp); +} + +int +rpioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + int card = RP_CARD(dev); + int port = RP_PORT(dev); + struct rp_softc *sc = rp_cd.cd_devs[card]; + struct rp_port *rp = &sc->sc_rp[port]; + struct rp_chan *cp = &rp->rp_channel; + struct tty *tp = rp->rp_tty; + int error; + +#ifdef RP_DEBUG1 + printf("%s port %d ioctl cmd 0x%lx data %p flag 0x%x\n", DEVNAME(sc), + port, cmd, data, flag); +#endif + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + error = ttioctl(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + switch (cmd) { + case TIOCSBRK: + cp->TxControl[3] |= ~SETBREAK; + rp_writech4(cp, _INDX_ADDR, lemtoh32(cp->TxControl)); + break; + case TIOCCBRK: + cp->TxControl[3] &= ~SETBREAK; + rp_writech4(cp, _INDX_ADDR, lemtoh32(cp->TxControl)); + break; + case TIOCSDTR: /* DIR on */ + cp->TxControl[3] |= SET_DTR; + rp_writech4(cp, _INDX_ADDR, lemtoh32(cp->TxControl)); + break; + case TIOCCDTR: /* DIR off */ + cp->TxControl[3] &= ~SET_DTR; + rp_writech4(cp, _INDX_ADDR, lemtoh32(cp->TxControl)); + break; + case TIOCMSET: /* set new modem control line values */ + cp->TxControl[3] &= ~SET_DTR; + cp->TxControl[3] &= ~SET_RTS; + case TIOCMBIS: /* turn modem control bits on */ + if (*(int*)data & TIOCM_DTR) + cp->TxControl[3] |= SET_DTR; + if (*(int*)data & TIOCM_RTS) + cp->TxControl[3] |= SET_RTS; + + rp_writech4(cp, _INDX_ADDR, lemtoh32(cp->TxControl)); + break; + case TIOCMBIC: /* turn modem control bits off */ + if (*(int*)data & TIOCM_DTR) + cp->TxControl[3] &= ~SET_DTR; + if (*(int*)data & TIOCM_RTS) + cp->TxControl[3] &= ~SET_RTS; + + rp_writech4(cp, _INDX_ADDR, lemtoh32(cp->TxControl)); + break; + case TIOCMGET: /* get modem control/status line state */ + return (ENOTTY); + case TIOCGFLAGS:/* get flags */ + *(int *)data = rp->rp_swflags; + break; + case TIOCSFLAGS:/* set flags */ + error = suser(p); + if (error) + return (EPERM); + rp->rp_swflags = *(int *)data; + break; + default: + return (ENOTTY); + } + + return (0); +} +#if 0 +int +rpmodem(struct tty *tp, int sigon, int sigoff) +{ + struct rp_port *rp; + int i, j, k; + + rp = tty_softc(tp); + if (sigon != 0 || sigoff != 0) { + i = j = 0; + if (sigon & SER_DTR) + i = SET_DTR; + if (sigoff & SER_DTR) + j = SET_DTR; + if (sigon & SER_RTS) + i = SET_RTS; + if (sigoff & SER_RTS) + j = SET_RTS; + rp->rp_channel.TxControl[3] &= ~i; + rp->rp_channel.TxControl[3] |= j; + rp_writech4(&rp->rp_channel,_INDX_ADDR, + lemtoh32(rp->rp_channel.TxControl)); + } else { + i = rp_chan_status_lo(&rp->rp_channel); + j = rp->rp_channel.TxControl[3]; + k = 0; + if (j & SET_DTR) + k |= SER_DTR; + if (j & SET_RTS) + k |= SER_RTS; + if (i & CD_ACT) + k |= SER_DCD; + if (i & DSR_ACT) + k |= SER_DSR; + if (i & CTS_ACT) + k |= SER_CTS; + return(k); + } + return (0); +} +#endif + +static struct { + int baud; + int conversion; +} baud_table[] = { + {B0, 0}, {B50, RP_BRD50}, {B75, RP_BRD75}, + {B110, RP_BRD110}, {B134, RP_BRD134}, {B150, RP_BRD150}, + {B200, RP_BRD200}, {B300, RP_BRD300}, {B600, RP_BRD600}, + {B1200, RP_BRD1200}, {B1800, RP_BRD1800}, {B2400, RP_BRD2400}, + {B4800, RP_BRD4800}, {B9600, RP_BRD9600}, {B19200, RP_BRD19200}, + {B38400, RP_BRD38400}, {B7200, RP_BRD7200}, {B14400, RP_BRD14400}, + {B57600, RP_BRD57600}, {B76800, RP_BRD76800}, {B115200, RP_BRD115200}, + {B230400, RP_BRD230400}, {-1, -1} +}; + +static int +rp_convert_baud(int baud) +{ + int i; + + for (i = 0; baud_table[i].baud >= 0; i++) + if (baud_table[i].baud == baud) + break; + + return (baud_table[i].conversion); +} + +int +rpstop(struct tty *tp, int flag) +{ + int card = RP_CARD(tp->t_dev); + int port = RP_PORT(tp->t_dev); + struct rp_softc *sc = rp_cd.cd_devs[card]; + struct rp_port *rp = &sc->sc_rp[port]; + int s; + +#ifdef RP_DEBUG + printf("%s port %d stop tty %p flag 0x%x\n", DEVNAME(sc), port, tp, + flag); +#endif + + s = spltty(); + if (ISSET(tp->t_state, TS_BUSY)) { + if (!ISSET(tp->t_state, TS_TTSTOP)) + SET(tp->t_state, TS_FLUSH); + + SET(rp->rp_flags, RPF_STOP); + } + splx(s); + + return (0); +} + +int +rpparam(struct tty *tp, struct termios *t) +{ + int card = RP_CARD(tp->t_dev); + int port = RP_PORT(tp->t_dev); + struct rp_softc *sc = rp_cd.cd_devs[card]; + struct rp_port *rp = &sc->sc_rp[port]; + struct rp_chan *cp = &rp->rp_channel; + int cflag = t->c_cflag; + int iflag = t->c_iflag; + int ospeed = rp_convert_baud(t->c_ispeed); + +#ifdef RP_DEBUG1 + printf("%s port %d param tty %p\n", DEVNAME(sc), port, tp); +#endif + +#ifdef RPCLOCAL + int devshift; + devshift = umynor / 32; + devshift = 1 << devshift; + if (devshift & RPCLOCAL) + cflag |= CLOCAL; +#endif + + if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) + return (EINVAL); + + if (t->c_ospeed == 0) { + rp_clr_DTR(cp); + return (0); + } + rp->rp_fifo_lw = ((t->c_ospeed*2) / 1000) +1; + + /* Set baud rate ----- we only pay attention to ispeed */ + rp_set_DTR(cp); + rp_set_RTS(cp); + rp_set_baud(cp, ospeed); + + if (cflag & CSTOPB) + rp_set_stop2(cp); + else + rp_set_stop1(cp); + + if (cflag & PARENB) { + rp_enable_parity(cp); + + if (cflag & PARODD) + rp_set_odd_parity(cp); + else + rp_set_even_parity(cp); + } else { + rp_disable_parity(cp); + } + + if ((cflag & CSIZE) == CS8) { + rp_set_data8(cp); + rp->rp_imask = 0xFF; + } else { + rp_set_data7(cp); + rp->rp_imask = 0x7F; + } + + if (iflag & ISTRIP) + rp->rp_imask &= 0x7F; + + if (cflag & CLOCAL) + rp->rp_intmask &= ~DELTA_CD; + else + rp->rp_intmask |= DELTA_CD; + + /* Put flow control stuff here */ + + if (cflag & CCTS_OFLOW) + rp_enable_CTS_flowctl(cp); + else + rp_disable_CTS_flowctl(cp); + + /* XXX: dead code: rp_rts_iflow is never used */ + if (cflag & CRTS_IFLOW) + rp->rp_rts_iflow = 1; + else + rp->rp_rts_iflow = 0; + + if (cflag & CRTS_IFLOW) + rp_enable_RTS_flowctl(cp); + else + rp_disable_RTS_flowctl(cp); + + /* just to be sure */ + rpstart(tp); + return (0); +} + +void +rpstart(struct tty *tp) +{ + int card = RP_CARD(tp->t_dev); + int port = RP_PORT(tp->t_dev); + struct rp_softc *sc = rp_cd.cd_devs[card]; + struct rp_port *rp = &sc->sc_rp[port]; + struct rp_chan *cp = &rp->rp_channel; + int xmit_fifo_room; + int s, i, count, wcount; + +#ifdef RP_DEBUG1 + printf("%s port %d start, tty %p\n", DEVNAME(sc), port, tp); +#endif + + s = spltty(); + + if (ISSET(tp->t_state, TS_TTSTOP | TS_TIMEOUT | TS_BUSY)) + goto out; + + ttwakeupwr(tp); + + if (tp->t_outq.c_cc == 0) + goto out; + SET(tp->t_state, TS_BUSY); + + xmit_fifo_room = TXFIFO_SIZE - rp_get_tx_cnt(cp); + count = q_to_b(&tp->t_outq, rp->TxBuf, xmit_fifo_room); + if (xmit_fifo_room > 0) { + for (i = 0, wcount = count >> 1; wcount > 0; i += 2, wcount--) + rp_writech2(cp, rp_txrx_data_io(cp), lemtoh16(&rp->TxBuf[i])); + + if (count & 1) + rp_writech1(cp, rp_txrx_data_io(cp), rp->TxBuf[(count-1)]); + } + + out: + splx(s); +} Index: sys/dev/ic/rpreg.h =================================================================== RCS file: sys/dev/ic/rpreg.h diff -N sys/dev/ic/rpreg.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/ic/rpreg.h 2 Oct 2020 17:08:39 -0000 @@ -0,0 +1,805 @@ +/* $OpenBSD$ */ +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) Comtrol Corporation <supp...@comtrol.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted prodived that the follwoing conditions + * are met. + * 1. Redistributions of source code must retain the above copyright + * notive, this list of conditions and the following disclainer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials prodided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Comtrol Corporation. + * 4. The name of Comtrol Corporation may not be used to endorse or + * promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COMTROL CORPORATION BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * Begin OS-specific defines for RocketPort + */ + +#define rp_readio(size, sc, rid, offset) \ + (bus_space_read_##size((sc)->sc_iot, (sc)->sc_ioh, (offset))) +#define rp_readmultiio(size, sc, rid, offset, addr, count) \ + (bus_space_read_multi_##size((sc)->sc_iot, (sc)->sc_ioh, (offset), (addr), (count))) +#define rp_writeio(size, sc, rid, offset, data) \ + (bus_space_write_##size((sc)->sc_iot, (sc)->sc_ioh, (offset), (data))) +#define rp_writemultiio(size, sc, rid, offset, addr, count) \ + (bus_space_write_multi_##size((sc)->sc_iot, (sc)->sc_ioh, (offset), (addr), (count))) + +#define rp_readio1(sc, rid, offset) rp_readio(1, (sc), (rid), (offset)) +#define rp_readio2(sc, rid, offset) rp_readio(2, (sc), (rid), (offset)) +#define rp_readio4(sc, rid, offset) rp_readio(4, (sc), (rid), (offset)) + +#define rp_writeio1(sc, rid, offset, data) \ + rp_writeio(1, (sc), (rid), (offset), (data)) +#define rp_writeio2(sc, rid, offset, data) \ + rp_writeio(2, (sc), (rid), (offset), (data)) +#define rp_writeio4(sc, rid, offset, data) \ + rp_writeio(4, (sc), (rid), (offset), (data)) + +#define rp_readmultiio1(sc, rid, offset, addr, count) \ + rp_readmultiio(1, (sc), (rid), (offset), (addr), (count)) +#define rp_readmultiio2(sc, rid, offset, addr, count) \ + rp_readmultiio(2, (sc), (rid), (offset), (addr), (count)) +#define rp_readmultiio4(sc, rid, offset, addr, count) \ + rp_readmultiio(4, (sc), (rid), (offset), (addr), (count)) + +#define rp_writemultiio1(sc, rid, offset, addr, count) \ + rp_writemultiio(1, (sc), (rid), (offset), (addr), (count)) +#define rp_writemultiio2(sc, rid, offset, addr, count) \ + rp_writemultiio(2, (sc), (rid), (offset), (addr), (count)) +#define rp_writemultiio4(sc, rid, offset, addr, count) \ + rp_writemultiio(4, (sc), (rid), (offset), (addr), (count)) + +#define rp_readaiop1(sc, aiop, offset) \ + (rp_readio1((sc), (sc)->aiop2rid(aiop, offset), (sc)->aiop2off(aiop, offset))) +#define rp_readaiop2(sc, aiop, offset) \ + (rp_readio2((sc), (sc)->aiop2rid(aiop, offset), (sc)->aiop2off(aiop, offset))) +#define rp_readaiop4(sc, aiop, offset) \ + (rp_readio4((sc), (sc)->aiop2rid(aiop, offset), (sc)->aiop2off(aiop, offset))) +#define rp_readmultiaiop1(sc, aiop, offset, addr, count) \ + (rp_readmultiio1((sc), (sc)->aiop2rid(aiop, offset), (sc)->aiop2off(aiop, offset), addr, count)) +#define rp_readmultiaiop2(sc, aiop, offset, addr, count) \ + (rp_readmultiio2((sc), (sc)->aiop2rid(aiop, offset), (sc)->aiop2off(aiop, offset), addr, count)) +#define rp_readmultiaiop4(sc, aiop, offset, addr, count) \ + (rp_readmultiio4((sc), (sc)->aiop2rid(aiop, offset), (sc)->aiop2off(aiop, offset), addr, count)) +#define rp_writeaiop1(sc, aiop, offset, data) \ + (rp_writeio1((sc), (sc)->aiop2rid(aiop, offset), (sc)->aiop2off(aiop, offset), data)) +#define rp_writeaiop2(sc, aiop, offset, data) \ + (rp_writeio2((sc), (sc)->aiop2rid(aiop, offset), (sc)->aiop2off(aiop, offset), data)) +#define rp_writeaiop4(sc, aiop, offset, data) \ + (rp_writeio4((sc), (sc)->aiop2rid(aiop, offset), (sc)->aiop2off(aiop, offset), data)) +#define rp_writemultiaiop1(sc, aiop, offset, addr, count) \ + (rp_writemultiio1((sc), (sc)->aiop2rid(aiop, offset), (sc)->aiop2off(aiop, offset), addr, count)) +#define rp_writemultiaiop2(sc, aiop, offset, addr, count) \ + (rp_writemultiio2((sc), (sc)->aiop2rid(aiop, offset), (sc)->aiop2off(aiop, offset), addr, count)) +#define rp_writemultiaiop4(sc, aiop, offset, addr, count) \ + (rp_writemultiio4((sc), (sc)->aiop2rid(aiop, offset), (sc)->aiop2off(aiop, offset), addr, count)) + +#define rp_readch1(ch, offset) \ + (rp_readaiop1((ch)->sc, (ch)->AiopNum, offset)) +#define rp_readch2(ch, offset) \ + (rp_readaiop2((ch)->sc, (ch)->AiopNum, offset)) +#define rp_readch4(ch, offset) \ + (rp_readaiop4((ch)->sc, (ch)->AiopNum, offset)) +#define rp_readmultich1(ch, offset, addr, count) \ + (rp_readmultiaiop1((ch)->sc, (ch)->AiopNum, offset, addr, count)) +#define rp_readmultich2(ch, offset, addr, count) \ + (rp_readmultiaiop2((ch)->sc, (ch)->AiopNum, offset, addr, count)) +#define rp_readmultich4(ch, offset, addr, count) \ + (rp_readmultiaiop4((ch)->sc, (ch)->AiopNum, offset, addr, count)) +#define rp_writech1(ch, offset, data) \ + (rp_writeaiop1((ch)->sc, (ch)->AiopNum, offset, data)) +#define rp_writech2(ch, offset, data) \ + (rp_writeaiop2((ch)->sc, (ch)->AiopNum, offset, data)) +#define rp_writech4(ch, offset, data) \ + (rp_writeaiop4((ch)->sc, (ch)->AiopNum, offset, data)) +#define rp_writemultich1(ch, offset, addr, count) \ + (rp_writemultiaiop1((ch)->sc, (ch)->AiopNum, offset, addr, count)) +#define rp_writemultich2(ch, offset, addr, count) \ + (rp_writemultiaiop2((ch)->sc, (ch)->AiopNum, offset, addr, count)) +#define rp_writemultich4(ch, offset, addr, count) \ + (rp_writemultiaiop4((ch)->sc, (ch)->AiopNum, offset, addr, count)) + +/* + * Port number on card encoded in low 5 bits + * card number in next 2 bits (only space for 4 cards) + * high bit reserved for dialout flag + */ +#define RP_PORT(x) (minor(x) & 0xf) +#define RP_CARD(x) ((minor(x) >> 5) & 3) + +/* + * End of OS-specific defines + */ + +#define RP_CTL_SIZE 4 +#define RP_AIOP_CTL_SIZE 4 +#define RP_CHAN_AIOP_SIZE 8 +#define RP_MAX_PORTS_PER_AIOP 8 +#define RP_MAX_AIOPS_PER_BOARD 4 +#define RP_MAX_PORTS_PER_BOARD 32 + +/* AIOP ID numbers, identifies AIOP type implementing channel */ +#define RP_AIOPID_NULL -1 /* no AIOP or channel exists */ +#define RP_AIOPID_0001 0x0001 /* AIOP release 1 */ + +/* + * Global Register Offsets - Direct Access - Fixed values + */ +#define _CMD_REG 0x38 /* Command Register 8 Write */ +#define _INT_CHAN 0x39 /* Interrupt Channel Register 8 Read */ +#define _INT_MASK 0x3A /* Interrupt Mask Register 8 Read / Write */ +#define _UNUSED 0x3B /* Unused 8 */ +#define _INDX_ADDR 0x3C /* Index Register Address 16 Write */ +#define _INDX_DATA 0x3E /* Index Register Data 8/16 Read / Write */ + +/* + * Channel Register Offsets for 1st channel in AIOP - Direct Access + */ +#define _TD0 0x00 /* Transmit Data 16 Write */ +#define _RD0 0x00 /* Receive Data 16 Read */ +#define _CHN_STAT0 0x20 /* Channel Status 8/16 Read / Write */ +#define _FIFO_CNT0 0x10 /* Transmit/Receive FIFO Count 16 Read */ +#define _INT_ID0 0x30 /* Interrupt Identification 8 Read */ + +/* + * Tx Control Register Offsets - Indexed - External - Fixed + */ +#define _TX_ENBLS 0x980 /* Tx Processor Enables Register 8 Read / Write */ +#define _TXCMP1 0x988 /* Transmit Compare Value #1 8 Read / Write */ +#define _TXCMP2 0x989 /* Transmit Compare Value #2 8 Read / Write */ +#define _TXREP1B1 0x98A /* Tx Replace Value #1 - Byte 1 8 Read / Write */ +#define _TXREP1B2 0x98B /* Tx Replace Value #1 - Byte 2 8 Read / Write */ +#define _TXREP2 0x98C /* Transmit Replace Value #2 8 Read / Write */ + +/* + * Receive FIFO + */ +#define RXFIFO_DATA 0x5f +#define RXFIFO_OUT 0x5c +#define RXFIFO_EN 0x08 +#define RXFIFO_DIS 0xa7 + +/* + * Memory Controller Register Offsets - Indexed - External - Fixed + */ +#define _RX_FIFO 0x000 /* Rx FIFO */ +#define _TX_FIFO 0x800 /* Tx FIFO */ +#define _RXF_OUTP 0x990 /* Rx FIFO OUT pointer 16 Read / Write */ +#define _RXF_INP 0x992 /* Rx FIFO IN pointer 16 Read / Write */ +#define _TXF_OUTP 0x994 /* Tx FIFO OUT pointer 8 Read / Write */ +#define _TXF_INP 0x995 /* Tx FIFO IN pointer 8 Read / Write */ +#define _TXP_CNT 0x996 /* Tx Priority Count 8 Read / Write */ +#define _TXP_PNTR 0x997 /* Tx Priority Pointer 8 Read / Write */ + +#define PRI_PEND 0x80 /* Priority data pending (bit7, Tx pri cnt) */ +#define TXFIFO_SIZE 255 /* size of Tx FIFO */ +#define RXFIFO_SIZE 1023 /* size of Rx FIFO */ + +/* + * Tx Priority Buffer - Indexed - External - Fixed + */ +#define _TXP_BUF 0x9C0 /* Tx Priority Buffer 32 Bytes Read / Write */ +#define TXP_SIZE 0x20 /* 32 bytes */ + +/* + * Channel Register Offsets - Indexed - Internal - Fixed + */ +#define _TX_CTRL 0xFF0 /* Transmit Control 16 Write */ +#define _RX_CTRL 0xFF2 /* Receive Control 8 Write */ +#define _BAUD 0xFF4 /* Baud Rate 16 Write */ +#define _CLK_PRE 0xFF6 /* Clock Prescaler 8 Write */ + +#define CLOCK_PRESC 0x19 /* mod 9 (divide by 10) prescale */ + +#define RP_BRD50 4607 +#define RP_BRD75 3071 +#define RP_BRD110 2094 +#define RP_BRD134 1712 +#define RP_BRD150 1535 +#define RP_BRD200 1151 +#define RP_BRD300 767 +#define RP_BRD600 383 +#define RP_BRD1200 191 +#define RP_BRD1800 127 +#define RP_BRD2000 114 +#define RP_BRD2400 95 +#define RP_BRD3600 64 +#define RP_BRD4800 47 +#define RP_BRD7200 31 +#define RP_BRD9600 23 +#define RP_BRD14400 15 +#define RP_BRD19200 11 +#define RP_BRD38400 5 +#define RP_BRD57600 3 +#define RP_BRD76800 2 +#define RP_BRD115200 1 +#define RP_BRD230400 0 + +#define STMPARITY 0x01 /* parity error */ +#define STMRCVROVR 0x02 /* receiver over run error */ +#define STMFRAME 0x04 /* framing error */ +#define STMBREAK 0x08 /* BREAK */ +#define STMERROR (STMBREAK | STMFRAME | STMPARITY) +#define STMPARITYH 0x100 /* parity error */ +#define STMRCVROVRH 0x200 /* receiver over run error */ +#define STMFRAMEH 0x400 /* framing error */ +#define STMBREAKH 0x800 /* BREAK */ +#define STMERRORH (STMBREAKH | STMFRAMEH | STMPARITYH) + +#define RDA 0x01 /* Rx data available */ +#define TXSHRMT 0x02 /* Tx shift register is empty */ +#define TXFIFOMT 0x04 /* Tx FIFO is empty */ +#define CD_ACT 0x08 /* CD input asserted */ +#define DSR_ACT 0x10 /* DSR input asserted */ +#define CTS_ACT 0x20 /* CTS input asserted */ +#define DRAINED (TXFIFOMT | TXSHRMT) /* indicates Tx is drained */ + +#define RXPARITY 0x0100 /* received parity error */ +#define RXFRAME 0x0200 /* received framing error */ +#define RXBREAK 0x0400 /* received BREAK */ +#define RX1MATCH 0x0800 /* receive compare byte 1 match */ +#define RX2MATCH 0x1000 /* receive compare byte 2 match */ +#define RXFOVERFL 0x2000 /* receive FIFO overflow */ +#define STATMODE 0x8000 /* status mode enable bit */ +#define STATERROR (RXBREAK | RXFRAME | RXPARITY) + +#define DATA8BIT 0x01 /* 8 bit data (0 = 7 bit data) */ +#define EVEN_PAR 0x02 /* even parity (0 = odd parity) */ +#define PARITY_EN 0x04 /* enable parity (0 = no parity) */ +#define STOP2 0x08 /* enable 2 stop bits (0 = 1 stop) */ +#define TXINT_EN 0x10 /* transmit interrupt enable */ +#define RTSTOG_EN 0x40 /* RTS toggle enable bit */ +#define CTSFC_EN 0x80 /* CTS flow control enable bit */ + +#define TX_ENABLE 0x01 /* enable transmitter */ +#define SET_RTS 0x02 /* assert RTS */ +#define SET_DTR 0x04 /* assert DTR */ +#define LOCALLOOP 0x08 /* local loopback set for test */ +#define SETBREAK 0x10 /* send break condition (must clear) */ + +#define TRIG_NO 0x00 /* Rx FIFO trigger level 0 (no trigger) */ +#define MCINT_EN 0x01 /* modem change interrupt enable */ +#define RXINT_EN 0x02 /* Rx interrupt enable */ +#define SRCINT_EN 0x04 /* special Rx condition interrupt enable */ +#define TRIG_1 0x08 /* trigger level 1 char */ +#define TRIG_1_2 0x10 /* trigger level 1/2 */ +#define TRIG_7_8 0x18 /* trigger level 7/8 */ +#define TRIG_MASK 0x18 /* trigger level mask */ +#define RXPROC_EN 0x20 /* receive processor enable */ +#define RTSFC_EN 0x40 /* RTS flow control enable */ + +#define DELTA_DSR 0x01 /* DSR change interrupt */ +#define DELTA_CTS 0x02 /* CTS change interrupt */ +#define DELTA_CD 0x04 /* CD change interrupt */ +#define SRC_INT 0x08 /* special receive condition interrupt */ +#define TXFIFO_MT 0x10 /* Tx FIFO empty interrupt */ +#define RXF_TRIG 0x20 /* Rx FIFO trigger level interrupt */ + +#define COMP1_EN 0x01 /* compare byte 1 enable */ +#define COMP2_EN 0x02 /* compare byte 2 enable */ +#define IGN1_EN 0x04 /* ignore byte 1 enable */ +#define IGN2_EN 0x08 /* ignore byte 2 enable */ +#define REP1W2_EN 0x10 /* replace byte 1 with 2 bytes enable */ + +#define RESET_ALL 0x80 /* reset AIOP (all channels) */ +#define TXOVERIDE 0x40 /* Transmit software off override */ +#define RESETUART 0x20 /* reset channel's UART */ +#define RESTXFCNT 0x10 /* reset channel's Tx FIFO count register */ +#define RESRXFCNT 0x08 /* reset channel's Rx FIFO count register */ + +#define INTSTAT0 0x01 /* AIOP 0 interrupt status */ +#define INTSTAT1 0x02 /* AIOP 1 interrupt status */ +#define INTSTAT2 0x04 /* AIOP 2 interrupt status */ +#define INTSTAT3 0x08 /* AIOP 3 interrupt status */ + +#define INTR_EN 0x08 /* allow interrupts to host */ +#define INT_STROB 0x04 /* strobe and clear interrupt line (EOI) */ + +#define CHAN0_EN 0x01 /* enable AIOP 0 */ +#define CHAN1_EN 0x02 /* enable AIOP 1 */ +#define CHAN2_EN 0x04 /* enable AIOP 2 */ +#define CHAN3_EN 0x08 /* enable AIOP 3 */ + +#define FREQ_DIS 0x00 +#define FREQ_9HZ 0x10 +#define FREQ_17HZ 0x20 +#define FREQ_34HZ 0x30 +#define FREQ_69HZ 0x40 +#define FREQ_137HZ 0x50 +#define FREQ_274HZ 0x60 +#define PERIODIC_ONLY 0x80 /* only PERIODIC interrupt */ + +#define CHANINT_EN 0x0100 /* flags to enable/disable channel ints */ + +#define RDATASIZE 72 +#define RREGDATASIZE 52 + +#define RP_PCI_BAR_1 PCI_MAPREG_START +#define RP_PCI_BAR_2 PCI_MAPREG_START + 8 + +struct rp_softc; +struct rp_chan; + +/* The types of bus-specific methods */ +typedef int rp_aiop2rid_t(int, int); +typedef int rp_aiop2off_t(int, int); +typedef unsigned char rp_ctlmask_t(struct rp_softc *); + +/* Controller level information structure */ +struct rp_softc { + /* Device and resource management */ + struct device sc_dev; /* device */ + + int NumAiop; + int AiopID[RP_AIOP_CTL_SIZE]; + int AiopNumChan[RP_AIOP_CTL_SIZE]; + + struct mutex hwmtx; /* Spinlock protecting hardware. */ + int hwmtx_init; + int num_ports; + + void *sc_ih; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_size_t sc_ios; + + struct rp_port *sc_rp; /* port */ + struct cdev **dev_nodes; /* Device nodes */ + void *bus_ctlp; /* Bus-specific properties */ + + /* Bus-specific methods */ + rp_aiop2rid_t *aiop2rid; /* (aiop, offset) -> rid */ + rp_aiop2off_t *aiop2off; /* (aiop, offset) -> off */ + rp_ctlmask_t *ctlmask; /* Int status */ +}; + +/* Channel level information structure */ +struct rp_chan +{ + struct rp_softc *sc; + int AiopNum; + int ChanID; + int ChanNum; + + uint32_t TxFIFO; + uint32_t TxFIFOPtrs; + uint32_t RxFIFO; + uint32_t RxFIFOPtrs; + uint32_t TxPrioCnt; + uint32_t TxPrioPtr; + uint32_t TxPrioBuf; + + uint8_t R[RREGDATASIZE]; + + uint8_t BaudDiv[4]; + uint8_t TxControl[4]; + uint8_t RxControl[4]; + uint8_t TxEnables[4]; + uint8_t TxCompare[4]; + uint8_t TxReplace1[4]; + uint8_t TxReplace2[4]; +}; + +#define CHNOFF_TXRXDATA(ch) ((ch)->ChanNum * 2 + _TD0) +#define CHNOFF_CHANSTAT(ch) ((ch)->ChanNum * 2 + _CHN_STAT0) +#define CHNOFF_TXRXCOUNT(ch) ((ch)->ChanNum * 2 + _FIFO_CNT0) +#define CHNOFF_INTID(ch) ((ch)->ChanNum + _INT_ID0) + +/* Clear the DTR output */ +#define rp_clr_DTR(ch) do { \ + (ch)->TxControl[3] &= ~SET_DTR; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +/* Clear the RTS output */ +#define rp_clr_RTS(ch) do { \ + (ch)->TxControl[3] &= ~SET_RTS; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +/* Clear any existing transmit software flow control off condition */ +#define rp_clr_tx_xoff(ch) do { \ + rp_writech1(ch, _CMD_REG, TXOVERIDE | (uint8_t)(ch)->ChanNum); \ + rp_writech1(ch, _CMD_REG, (uint8_t)(ch)->ChanNum); \ +} while (0) + +/* Disable output flow control using CTS */ +#define rp_disable_CTS_flowctl(ch) do { \ + (ch)->TxControl[2] &= ~CTSFC_EN; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +/* + * Function sSetParity() can be used in place of functions sEnParity(), + * sDisParity(), rp_set_odd_parity(), and sSetEvenParity(). + */ +#define rp_disable_parity(ch) do { \ + (ch)->TxControl[2] &= ~PARITY_EN; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +#define rp_disable_rx_fifo(ch) do { \ + (ch)->R[0x32] = 0x0a; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->R + 0x30)); \ +} while (0) + +/* + * This takes the channel out of the receive status mode. All subsequent reads + * of receive data using sReadRxWord() will return two data bytes. + */ +#define rp_dis_rx_status_mode(ch) rp_writech2((ch), CHNOFF_CHANSTAT(ch), 0) + +/* + * This disables movement of Tx data from the Tx FIFO into the 1 byte Tx + * buffer. Therefore there could be up to a 2 byte latency between the time + * rp_disable_transmit() is called and the transmit buffer and transmit shift + * register going completely empty. + */ +#define rp_disable_transmit(cp) do { \ + (cp)->TxControl[3] &= ~TX_ENABLE; \ + rp_writech4(cp, _INDX_ADDR, lemtoh32((cp)->TxControl)); \ +} while (0) + +#define rp_disable_tx_soft_flowctl(ch) do { \ + (ch)->R[0x06] = 0x8a; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->R + 0x04)); \ +} while (0) + +/* enable output flow control using CTS */ +#define rp_enable_CTS_flowctl(ch) do { \ + (ch)->TxControl[2] |= CTSFC_EN; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +/* + * Function sSetParity() can be used in place of functions sEnParity(), + * sDisParity(), rp_set_odd_parity(), and sSetEvenParity(). + * + * Warnings: + * Before enabling parity odd or even parity should be chosen using functions + * rp_set_odd_parity() or sSetEvenParity(). + */ +#define rp_enable_parity(ch) do { \ + (ch)->TxControl[2] |= PARITY_EN; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +#define rp_enable_RTS_flowctl(ch) do { \ + (ch)->TxControl[2] &= ~RTSTOG_EN; \ + (ch)->TxControl[3] &= ~SET_RTS; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->TxControl)); \ + (ch)->RxControl[2] |= RTSFC_EN; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->RxControl)); \ +} while (0) + +#define rp_disable_RTS_flowctl(ch) do { \ + (ch)->RxControl[2] &= ~RTSFC_EN; \ + rp_writech4((ch), _INDX_ADDR, lemtoh32((ch)->RxControl));\ +} while (0) + +#define rp_enable_rx_fifo(ch) do { \ + (ch)->R[0x32] = 0x08; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->R + 0x30)); \ +} while (0) + +/* + * This function is used to start the receive processor. When the channel is + * in the reset state the receive processor is not running. This is done to + * prevent the receive processor from executing invalid microcode instructions + * prior to the downloading of the microcode. + * + * Warnings: + * This function must be called after valid microcode has been downloaded to + * the AIOP, and it must not be called before the microcode has been + * downloaded. + */ +#define rp_enable_rx_processor(ch) do { \ + (ch)->RxControl[2] |= RXPROC_EN; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->RxControl)); \ +} while (0) + +/* + * This places the channel in the receive status mode. All subsequent reads of + * receive data using sReadRxWord() will return a data byte in the low word and + * a status byte in the high word. + */ +#define rp_enable_rx_status_mode(ch) \ + rp_writech2(ch, CHNOFF_CHANSTAT(ch), STATMODE) + +#define rp_enable_transmit(ch) do { \ + (ch)->TxControl[3] |= TX_ENABLE; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +/* + * Purpose: Get the AIOP interrupt status + * + * Returns the AIOP interrupt status. Bits 0 through 7 represent channels 0 + * through 7 respectively. If a bit is set that channel is interrupting. + */ +#define rp_aiop_intr_status(sc, AIOPNUM) \ + rp_readaiop1((sc), (AIOPNUM), _INT_CHAN) + +/* + * Get a channel's interrupt identification byte and returns the channel + * interrupt ID. Can be any combination of the following flags: + * RXF_TRIG: Rx FIFO trigger level interrupt + * TXFIFO_MT: Tx FIFO empty interrupt + * SRC_INT: Special receive condition interrupt + * DELTA_CD: CD change interrupt + * DELTA_CTS: CTS change interrupt + * DELTA_DSR: DSR change interrupt + */ +#define rp_chan_intr_id(ch) \ + (rp_readch1(ch, (ch)->ChanNum+_INT_ID0) & \ + (RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR)) + +/* + * Returns the channel status. Can be any combination of the following flags: + * + * LOW BYTE FLAGS HIGH BYTE FLAGS + * + * CTS_ACT: CTS input asserted STATMODE: status mode enable bit + * DSR_ACT: DSR input asserted RXFOVERFL: receive FIFO overflow + * D_ACT: CD input asserted RX2MATCH: receive compare byte 2 match + * TXFIFOMT: Tx FIFO is empty RX1MATCH: receive compare byte 1 match + * TXSHRMT: Tx shift reg. is empty RXBREAK: received BREAK + * RDA: Rx data available RXFRAME: received framing error + * RXPARITY: received parity error + * + * Warnings: + * This function will clear the high byte flags in the Channel Status Register. + */ +#define rp_chan_status(ch) rp_readch2((ch), CHNOFF_CHANSTAT(ch)) + +/* + * Get the low byte only of the channel status + * + * Returns the channel status low byte. Can be any combination of the + * following flags: + * + * CTS_ACT: CTS input asserted + * DSR_ACT: DSR input asserted + * CD_ACT: CD input asserted + * TXFIFOMT: Tx FIFO is empty + * TXSHRMT: Tx shift register is empty + * RDA: Rx data available + */ +#define rp_chan_status_lo(ch) rp_readch1((ch), CHNOFF_CHANSTAT(ch)) + +/* + * Purpose: Get the number of data bytes in the Rx FIFO + * Returns the number of data bytes in the Rx FIFO. + * Comments: Byte read of count register is required to obtain Rx count. +*/ +#define rp_get_rx_cnt(ChP) rp_readch2((ChP), CHNOFF_TXRXCOUNT(ChP)) + +/* + * Returns the number of data bytes in the Tx FIFO. + * Comments: Byte read of count register is required to obtain Tx count. + */ +#define rp_get_tx_cnt(ch) rp_readch1((ch), CHNOFF_TXRXCOUNT(ch)) + +/* Return the offset of a channel's TxRx Data register */ +#define rp_txrx_data_io(ch) CHNOFF_TXRXDATA(ch) + +/* + * Initialize a channel structure to its default state. + * + * This function must be called once for every channel structure that exists + * before any other SSCI calls can be made. + */ +#define rp_init_chan_defaults(ch) do { \ + (ch)->sc = NULL; \ + (ch)->AiopNum = -1; \ + (ch)->ChanID = -1; \ + (ch)->ChanNum = -1; \ +} while (0) + +#define rp_reset_aiop_by_num(sc, AIOPNUM) do { \ + rp_writeaiop1((sc), (AIOPNUM), _CMD_REG, RESET_ALL); \ + rp_writeaiop1((sc), (AIOPNUM), _CMD_REG, 0x0); \ +} while (0) + +/* Set baud rate */ +#define rp_set_baud(ch, DIVISOR) do { \ + (ch)->BaudDiv[2] = (uint8_t)(DIVISOR); \ + (ch)->BaudDiv[3] = (uint8_t)((DIVISOR) >> 8); \ + rp_writech4((ch), _INDX_ADDR, lemtoh32((ch)->BaudDiv)); \ +} while (0) + +/* Set data bits to 7 */ +#define rp_set_data7(ch) do { \ + (ch)->TxControl[2] &= ~DATA8BIT; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +/* Set data bits to 8 */ +#define rp_set_data8(ch) do { \ + (ch)->TxControl[2] |= DATA8BIT; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +/* Set the DTR output */ +#define rp_set_DTR(ch) do { \ + (ch)->TxControl[3] |= SET_DTR; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +/* + * Function sSetParity() can be used in place of functions sEnParity(), + * sDisParity(), rp_set_odd_parity(), and sSetEvenParity(). + * + * Warnings: + * This function has no effect unless parity is enabled with function + * sEnParity(). + */ +#define rp_set_even_parity(ch) do { \ + (ch)->TxControl[2] |= EVEN_PAR; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +/* + * Function sSetParity() can be used in place of functions sEnParity(), + * sDisParity(), rp_set_odd_parity(), and sSetEvenParity(). + * + * Warnings: + * This function has no effect unless parity is enabled with function + * sEnParity(). + */ +#define rp_set_odd_parity(ch) do { \ + (ch)->TxControl[2] &= ~EVEN_PAR; \ + rp_writech4(ch, _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +/* Set the RTS output */ +#define rp_set_RTS(ch) do { \ + (ch)->TxControl[3] |= SET_RTS; \ + rp_writech4((ch), _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +/* + * Set the Rx FIFO trigger level + * + * Number of characters in Rx FIFO at which the interrupt will be generated. + * Can be any of the following flags: + * + * TRIG_NO: no trigger + * TRIG_1: 1 character in FIFO + * TRIG_1_2: FIFO 1/2 full + * TRIG_7_8: FIFO 7/8 full + * + * An interrupt will be generated when the trigger level is reached only if + * function sEnInterrupt() has been called with flag RXINT_EN set. The + * RXF_TRIG flag in the Interrupt Idenfification register will be set whenever + * the trigger level is reached regardless of the setting of RXINT_EN. + */ +#define rp_rx_trigger(ch, level) do { \ + (ch)->RxControl[2] &= ~TRIG_MASK; \ + (ch)->RxControl[2] |= (level); \ + rp_writech4((ch), _INDX_ADDR, lemtoh32((ch)->RxControl)); \ +} while (0) + +/* Set stop bits to 1 */ +#define rp_set_stop1(ch) do { \ + (ch)->TxControl[2] &= ~STOP2; \ + rp_writech4((ch), _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +/* Set stop bits to 2 */ +#define rp_set_stop2(ch) do { \ + (ch)->TxControl[2] |= STOP2; \ + rp_writech4((ch), _INDX_ADDR, lemtoh32((ch)->TxControl)); \ +} while (0) + +/* + * Start a channel's receive processor + * + * This function is used to start a Rx processor after it was stopped with + * rp_stop_rx_processor(). It will restart both the Rx processor and software + * input flow control. + */ +#define rp_start_rx_processor(ch) \ + rp_writech4((ch), _INDX_ADDR, lemtoh32((ch)->R)) + +/* + * Write a transmit data byte to a channel. + * + * Warnings: + * This function writes the data byte without checking to see if sMaxTxSize is + * exceeded in the Tx FIFO. + */ +#define rp_write_tx_byte(ch, io, data) rp_writech1((ch), (io), (data)) + +int rp_read_aiopid(struct rp_softc *, int); +int rp_read_aiop_numchan(struct rp_softc *sc, int); +int rp_init_chan(struct rp_softc *, struct rp_chan *, int, int); +void rp_stop_rx_processor(struct rp_chan *); +void rp_flush_rx_fifo(struct rp_chan *); +void rp_flush_tx_fifo(struct rp_chan *); +int rp_write_tx_prio_byte(struct rp_chan *, uint8_t); +int rp_intr(void *); +void rp_enable_interrupts(struct rp_chan *, uint16_t); +void rp_disable_interrupts(struct rp_chan *, uint16_t); +int rp_attach(struct rp_softc* sc, int, int); +void rp_releaseresource(struct rp_softc *sc); +static __inline void +rp_lock(struct rp_softc *sc) +{ + if (sc->hwmtx_init != 0) + mtx_enter(&sc->hwmtx); +} +static __inline void +rp_unlock(struct rp_softc *sc) +{ + if (sc->hwmtx_init != 0) + mtx_leave(&sc->hwmtx); +} + +extern uint8_t rp_sBitMapClrTbl[8]; +extern uint8_t rp_sBitMapSetTbl[8]; + +#define RP_UNIT(x) dv_unit(x) +#define MAX_RP_PORTS 128 + +/* + * Port number on card encoded in low 5 bits + * card number in next 2 bits (only space for 4 cards) + * high bit reserved for dialout flag + */ +#define RP_PORT(x) (minor(x) & 0xf) +#define RP_CARD(x) ((minor(x) >> 5) & 3) + +#define RPF_STOP 0x01 + +struct rp_port { + struct tty *rp_tty; /* cross reference */ + struct timeout rp_timer; + + unsigned char state; /* state of dtr */ + + int rp_swflags; + int rp_cua; + int rp_port; + int rp_flags; + int rp_unit:2; + int rp_aiop:2; + int rp_chan:3; + int rp_intmask; + int rp_imask; /* input mask */ + int rp_fifo_lw; + int rp_restart; + int rp_overflows; + int rp_rts_iflow:1; + int rp_disable_writes:1; + int rp_cts:1; + int rp_waiting:1; + int rp_xmit_stopped:1; + struct rp_softc *rp_sc; + struct rp_chan rp_channel; + unsigned char TxBuf[TXFIFO_SIZE]; + unsigned char RxBuf[RXFIFO_SIZE]; +}; Index: sys/dev/pci/files.pci =================================================================== RCS file: /cvs/src/sys/dev/pci/files.pci,v retrieving revision 1.352 diff -u -p -r1.352 files.pci --- sys/dev/pci/files.pci 17 Jul 2020 06:33:07 -0000 1.352 +++ sys/dev/pci/files.pci 2 Oct 2020 17:08:36 -0000 @@ -270,6 +270,10 @@ device ppb: pcibus attach ppb at pci file dev/pci/ppb.c ppb +# Comtrol RocketPort +attach rp at pci with rp_pci +file dev/pci/rp_pci.c rp_pci + # Cyclades Cyclom-8/16/32 attach cy at pci with cy_pci file dev/pci/cy_pci.c cy_pci Index: sys/dev/pci/pcidevs =================================================================== RCS file: /cvs/src/sys/dev/pci/pcidevs,v retrieving revision 1.1933 diff -u -p -r1.1933 pcidevs --- sys/dev/pci/pcidevs 1 Oct 2020 15:42:15 -0000 1.1933 +++ sys/dev/pci/pcidevs 2 Oct 2020 17:08:37 -0000 @@ -195,6 +195,7 @@ vendor AD 0x11d4 Analog Devices vendor ZORAN 0x11de Zoran vendor PIJNENBURG 0x11e3 Pijnenburg vendor COMPEX 0x11f6 Compex +vendor COMCORP 0x11fe Comtrol Corporation vendor CYCLADES 0x120e Cyclades vendor ESSENTIAL 0x120f Essential Communications vendor O2MICRO 0x1217 O2 Micro @@ -2605,6 +2606,9 @@ product COMPAQ NF3P_BNC 0xf150 NetFlex product COMPEX COMPEXE 0x1401 Compexe product COMPEX RL100ATX 0x2011 RL100-ATX product COMPEX 98713 0x9881 PMAC 98713 + +/* Comtrol Corporation */ +product COMCORP ROCKETPORT_16 0x0009 RocketPort PCI 16-port Serial /* Conexant products */ product CONEXANT 56K_WINMODEM 0x1033 56k Winmodem Index: sys/dev/pci/pcidevs.h =================================================================== RCS file: /cvs/src/sys/dev/pci/pcidevs.h,v retrieving revision 1.1926 diff -u -p -r1.1926 pcidevs.h --- sys/dev/pci/pcidevs.h 1 Oct 2020 15:42:53 -0000 1.1926 +++ sys/dev/pci/pcidevs.h 2 Oct 2020 17:08:37 -0000 @@ -200,6 +200,7 @@ #define PCI_VENDOR_ZORAN 0x11de /* Zoran */ #define PCI_VENDOR_PIJNENBURG 0x11e3 /* Pijnenburg */ #define PCI_VENDOR_COMPEX 0x11f6 /* Compex */ +#define PCI_VENDOR_COMCORP 0x11fe /* Comtrol Corporation */ #define PCI_VENDOR_CYCLADES 0x120e /* Cyclades */ #define PCI_VENDOR_ESSENTIAL 0x120f /* Essential Communications */ #define PCI_VENDOR_O2MICRO 0x1217 /* O2 Micro */ @@ -2610,6 +2611,9 @@ #define PCI_PRODUCT_COMPEX_COMPEXE 0x1401 /* Compexe */ #define PCI_PRODUCT_COMPEX_RL100ATX 0x2011 /* RL100-ATX */ #define PCI_PRODUCT_COMPEX_98713 0x9881 /* PMAC 98713 */ + +/* Comtrol Corporation */ +#define PCI_PRODUCT_COMCORP_ROCKETPORT_16 0x0009 /* RocketPort PCI 16-port Serial */ /* Conexant products */ #define PCI_PRODUCT_CONEXANT_56K_WINMODEM 0x1033 /* 56k Winmodem */ Index: sys/dev/pci/pcidevs_data.h =================================================================== RCS file: /cvs/src/sys/dev/pci/pcidevs_data.h,v retrieving revision 1.1921 diff -u -p -r1.1921 pcidevs_data.h --- sys/dev/pci/pcidevs_data.h 1 Oct 2020 15:42:53 -0000 1.1921 +++ sys/dev/pci/pcidevs_data.h 2 Oct 2020 17:08:37 -0000 @@ -8364,6 +8364,10 @@ static const struct pci_known_product pc "PMAC 98713", }, { + PCI_VENDOR_COMCORP, PCI_PRODUCT_COMCORP_ROCKETPORT_16, + "RocketPort PCI 16-port Serial", + }, + { PCI_VENDOR_CONEXANT, PCI_PRODUCT_CONEXANT_56K_WINMODEM, "56k Winmodem", }, @@ -30302,6 +30306,10 @@ static const struct pci_known_vendor pci { PCI_VENDOR_COMPEX, "Compex", + }, + { + PCI_VENDOR_COMCORP, + "Comtrol Corporation", }, { PCI_VENDOR_CYCLADES,