diff options
author | wollman <wollman@FreeBSD.org> | 1994-12-02 23:23:01 +0000 |
---|---|---|
committer | wollman <wollman@FreeBSD.org> | 1994-12-02 23:23:01 +0000 |
commit | 1e6f21ed41e83670488677de0b159748eb02a365 (patch) | |
tree | 60003a2ecb3238a23ed3424937623de0225e5255 | |
download | FreeBSD-src-1e6f21ed41e83670488677de0b159748eb02a365.zip FreeBSD-src-1e6f21ed41e83670488677de0b159748eb02a365.tar.gz |
Cronyx/Sigma sync/async serial driver with PPP support
from Serge Vakulenko
-rw-r--r-- | sbin/i386/Makefile | 5 | ||||
-rw-r--r-- | sbin/i386/cxconfig/Makefile | 4 | ||||
-rw-r--r-- | sbin/i386/cxconfig/cxconfig.8 | 297 | ||||
-rw-r--r-- | sbin/i386/cxconfig/cxconfig.c | 704 | ||||
-rw-r--r-- | share/man/man4/man4.i386/cx.4 | 239 | ||||
-rw-r--r-- | sys/i386/include/cronyx.h | 475 | ||||
-rw-r--r-- | sys/i386/isa/cronyx.c | 1041 | ||||
-rw-r--r-- | sys/i386/isa/cx.c | 898 | ||||
-rw-r--r-- | sys/i386/isa/cxreg.h | 429 | ||||
-rw-r--r-- | sys/i386/isa/if_cx.c | 818 | ||||
-rw-r--r-- | sys/net/if_sppp.h | 70 | ||||
-rw-r--r-- | sys/net/if_spppsubr.c | 1219 |
12 files changed, 6199 insertions, 0 deletions
diff --git a/sbin/i386/Makefile b/sbin/i386/Makefile new file mode 100644 index 0000000..c480c16 --- /dev/null +++ b/sbin/i386/Makefile @@ -0,0 +1,5 @@ +# $Id$ + +SUBDIR= cxconfig + +.include <bsd.subdir.mk> diff --git a/sbin/i386/cxconfig/Makefile b/sbin/i386/cxconfig/Makefile new file mode 100644 index 0000000..59c828d --- /dev/null +++ b/sbin/i386/cxconfig/Makefile @@ -0,0 +1,4 @@ +PROG = cxconfig +MAN8 = cxconfig.8 + +.include <bsd.prog.mk> diff --git a/sbin/i386/cxconfig/cxconfig.8 b/sbin/i386/cxconfig/cxconfig.8 new file mode 100644 index 0000000..9907435 --- /dev/null +++ b/sbin/i386/cxconfig/cxconfig.8 @@ -0,0 +1,297 @@ +.TH Cronyx-Sigma 1 +.rm ES +.rm EE +.de ES +.PP +.nf +.in +0.5i +.. +.de EE +.in -0.5i +.fi +.. +.na +.SH NAME +.B cxconfig +\- channel options management utility for Cronyx-Sigma adapter +.SH DESCRIPTION +.PP +The \fBcxconfig\fP utility is used for configuring the channel options of +the Cronyx-Sigma adapter. +.PP +To change cha options the channel should be free: the corresponding +network interface in ``down'' state, the asynchronous terminal device /dev/tty* +closed. +Generally, the channel options are set up during the operating +system startup, for example from the \fI/etc/rc\fP file. +.PP +Note, that not all options have a sense for every particular +case, and an attempt to set some of them can hang up the channel or +the whole adapter. +.SH "Usage" +.IP "cxconfig" +The brief information about all channels. +.IP "cxconfig -a" +The full information about all channels. +.IP "cxconfig <channel>" +The brief information about the channel. +.IP "cxconfig -a <channel>" +The full information about the channel. +.IP "cxconfig <channel> <option>..." +Setting the channel options. +.SH "Channel options" +.IP ispeed=# +Set the receiver baud rate to the number given. +The maximal value is 256000 bits/sec. +In the synchronous mode the receiver baud rate is significant +only when DPLL mode is used. +.IP ospeed=# +Set the transmitter baud rate to the number given. +The maximal value is 256000 bits/sec. +In the synchronous mode the transmitter baud rate is significant +only in the case of the internal clock source. +If receiver and transmitter have equal data rate, then it could +be set by specifying only the numerical argument. +.IP async +Set the asynchronous channel mode. +.IP "hdlc, bisync, bsc, x.21, x21 +Set the synchronous channel mode: HDLC, Bisync (BSC) or X.21. +.IP ppp +Set the link-level protocol: PPP/HDLC. The built-in simplified synchronous PPP +implementation is used (see RFC-1548, RFC-1549). +.IP cisco +Set the link-level protocol: Cisco/HDLC (see RFC-1547). +This protocol is intended for compatibility with old models of Cisco routers, +and with early versions of BSD/386 drivers. +The extensive usage of this protocol is not recommended. +.IP ext +Use the external link-level protocol suite (for BSD/386 only). +.IP "+keepalive, -keepalive" +Enable the automatic line state control sub-protocol. +This setting is not significant when the external link-level protocol is used. +.IP "+autorts, -autorts" +Enable the automatic RTS signal control. +When enabled, the RTS signal goes up only when both halves of +the receiver ring buffer are free and ready for receive, +and goes down when one or both buffers are busy. +.IP "port=rs232, port=rs449, port=v35 +Set the zero channel hardware interface type. +.SH "Common options" +.IP "nrz, nrzi, manchester +Set the data line signal encoding. +In the case of \fINRZ\fP encoding the zero bit is transmitted by the zero signal +level, the one bit - by the positive signal level. +In the case of \fINRZI\fP encoding the zero bit is transmitted by the change of +the signal level, the one bit - by the constant signal level. +In the case of \fIManchester\fP encoding the zero bit is encoded as 01 value, +the one bit - as 10 value. +.IP "+dpll, -dpll" +Enable the digital phase locked loop mode (DPLL). +When enabled, the receiver timing clock signal +is derived from the received data. +.IP "+lloop, -lloop" +Set the local loopback mode. +.IP "+extclock, -extclock" +Set the timing clock source of synchronous channels. There are +two possible variants: \fIexternal clock\fP source or \fIinternal clock\fP +generation. +.br +\fIExternal clock\fP mode is the most common method for connecting +external modem hardware. In this mode the external timing +signal is received on TXCIN pin of the connector, and it is +used as a synchronization clock for transmitting data (TXD). +.br +In the case of \fIinternal clock\fP mode the transmitted data (TXD) +are synchronized using the internal on-board timing generator, +the internally generated timing signal is driven on the TXCOUT +pin, and the signal on the TXCIN pin is ignored. This mode +is used for direct terminal-to-terminal communication, +e.g. for connecting two computers together in a synchronous mode +via relatively short cable. This method should also be used +for testing channels with an external loopback connector. +.IP fifo=# +FIFO threshold level setup for receiver and transmitter. +.IP rfifo=# +Hardware RTS/CTS flow control FIFO threshold setup. +.IP "+ctsup, -ctsup" +Enable/disable interrupts on CTS (Clear To Send) signal setup (0 to 1 transition). +.IP "+ctsdown, -ctsdown" +Enable/disable interrupts on CTS (Clear To Send) signal clear (1 to 0 transition). +.IP "+cdup, -cdup" +Enable/disable interrupts on CD (Carrier Detect) signal setup (0 to 1 transition). +.IP "+cddown, -cddown" +Enable/disable interrupts on CD (Carrier Detect) signal clear (1 to 0 transition). +.IP "+dsrup, -dsrup" +Enable/disable interrupts on DSR (Data Set Ready) signal setup (0 to 1 transition). +.IP "+dsrdown, -dsrdown" +Enable/disable interrupts on DSR (Data Set Ready) signal clear (1 to 0 transition). +.SH "Asynchronous mode options" +.IP cs# +Select character size: 5, 6, 7 or 8 bits. +.IP "parodd, pareven +Parity mode: odd or even. +.IP "+ignpar, -ignpar +Disable/enable parity detection. +.IP nopar +Disable parity bit generation. +.IP forcepar +Force parity: even - 0, odd - 1. +.IP "stopb1, stopb1.5, stopb2 +Use 1 or 1.5 or 2 stop bits per character. +.IP "+dsr, -dsr" +Use the DSR input signal as receiver enable/disable. +.IP "+cts, -cts" +Use the CTS input signal as transmitter enable/disable. +.IP "+rts, -rts" +Drive the RTS output signal as transmitter ready. +.IP "+rloop, -rloop" +Set the remote loopback mode. +.IP "+etc, -etc" +Enable the embedded transmit commands mode. +.IP "+ixon, -ixon" +Enable the hardware XON/XOFF flow control support. +.IP "+ixany, -ixany" +Use the hardware IXANY mode support. +.IP "+sdt, -sdt" +Detect the spec. characters SCHR1 and SCHR2 in the receive data. +.IP "+flowct, -flowct" +Receive the flow control spec. characters as data. +.IP "+rdt, -rdt" +Detect the spec. characters in range SCRL..SCRH in the receive data. +.IP "+exdt, -exdt" +Detect the spec. characters SCHR3 and SCHR4 in the receive data. +.IP "parintr, parnull, parign, pardisc, parffnull +Action on parity errors: +.ES + Mode Action + ----------------------------------------------------- + parintr Generate the receiver error interrupt + parnull Input the NULL character + parign Ignore the error, receive as good data + pardisc Ignore the character + parffnull Input the sequence <0xFF, NULL, character> +.EE +.IP "brkintr, brknull, brkdisc +Line break state action: +.ES + Mode Action + --------------------------------------------------- + brkintr Generate the receiver error interrupt + brknull Input the NULL character + brkdisc Ignore the line break state +.EE +.IP "+inlcr, -inlcr" +Translate received NL characters to CR. +.IP "+icrnl, -icrnl" +Translate received CR characters to NL. +.IP "+igncr, -igncr" +Ignore received CR characters. +.IP "+ocrnl, -ocrnl" +Translate transmitted CR characters to NL. +.IP "+onlcr, -onlcr" +Translate transmitted NL characters to CR. +.IP "+fcerr, -fcerr" +Process (don't process) the characters, received with errors, +for special character/flow control matching. +.IP "+lnext, -lnext" +Enable the LNEXT character option: the character following +the LNEXT character is not processed for special character/flow +control matching. +.IP "+istrip, -istrip" +Strip input characters to seven bits. +.IP schr1=# +The XON flow control character value. +.IP schr2=# +The XOFF flow control character value. +.IP schr3=# +The SCHR3 spec. character value. +.IP schr4=# +The SCHR4 spec. character value. +.IP "scrl=#, scrh=# +The spec. character range (inclusive). +.IP lnext=# +The LNEXT spec. character value. +.SH "HDLC mode options" +.IP if# +The minimum number of flags transmitted before a frame is started. +.IP noaddr +No frame address recognition. +.IP "addrlen1, addrlen2" +Address field length: 1 or 2 bytes. +.IP "addr1, addr2" +Addressing mode: 4x1 bytes or 2x2 bytes. +Registers RFAR1..RFAR4 should contain the address to be matched. +.IP "+clrdet, -clrdet" +Enable/disable clear detect for X.21 protocol support. +.IP "+dsr, -dsr" +Use the DSR input signal as receiver enable/disable. +.IP "+cts, -cts" +Use the CTS input signal as transmitter enable/disable. +.IP "+rts, -rts" +Drive the RTS output signal as transmitter ready. +.IP "+fcs, -fcs" +Enable/disable the frame checksum generation and checking. +.IP "crc-16, crc-v.41 +Select the CRC polynomial: CRC-16 (x^16+x^15+x^2+1) +or CRC V.41 (x^16+x^12+x^5+1). +.IP "fcs-crc-16, fcs-v.41 +Frame checksum preset: all zeros (CRC-16) or all ones (CRC V.41). +.IP "+crcinv, -crcinv" +Invert (ie. CRC V.41) or don't invert (ie. CRC-16) the transmitted frame checksum. +.IP "+fcsapd, -fcsapd" +Pass the received CRC to the host at the end of receiver data buffer. +.IP "idlemark, idleflag +Idle mode: idle in mark (transmit all ones) or idle in flag (transmit flag). +.IP "+syn, -syn" +Enable/disable sending pad characters before sending flag when coming out +of the idle mode. +.IP pad# +The number of synchronous characters sent (0..4). +.IP "syn=0xaa, syn=0x00 +Send sync pattern. +.IP "rfar1=#, rfar2=#, rfar3=#, rfar4=# +Frame address registers for address recognition. +.SH EXAMPLES +.PP +Set up the channel 7 of the adapter Sigma-400 under FreeBSD. +Physical 4-wire leased line with Zelax+ M115 short-range modems. +Synchronous mode, 128000 bits/sec, interface RS-232, +protocol PPP/HDLC without keepalive support, NRZI encoding, +DPLL mode, no flow control: +.ES +cxconfig cx7 128000 hdlc ppp -keepalive nrzi -cts +dpll -extclock +ifconfig cx7 158.250.244.2 158.250.244.1 up +.EE +.PP +Set up the channel 0 of the adapter Sigma-100 under FreeBSD. +Attachment to the near computer by short cable, internal clock source. +Synchronous mode, 256000 bits/sec, interface RS-232, +protocol Cisco/HDLC with keepalive support: +.ES +cxconfig cx0 hdlc 256000 cisco +keepalive -extclock +ifconfig cx0 200.1.1.1 200.1.1.2 up +.EE +.PP +Set up the channel 1 of the adapter Sigma-840 under BSD/386. +Synchronous 64 kbit/sec leased line, external clock source. +Synchronous mode, interface V.35, external protocol suite: +.ES +cxconfig cx1 hdlc ext +ifconfig cx1 193.124.254.50 193.124.254.49 multicast up +.EE +.PP +Set up the channel 0 of the adapter Sigma-840 under FreeBSD. +Attachment to the Cisco-4000 router by null-modem cable, internal clock source. +Synchronous mode, 64000 bits/sec, interface RS-232, +protocol PPP/HDLC with keepalive support and flow control, +LCP and IPCP protocols (see RFC-1548 and RFC-1332) debug tracing enabled: +.ES +cxconfig cx0 hdlc 64000 port=rs232 ppp +keepalive -extclock +cts +ifconfig cx0 100.0.0.2 100.0.0.1 debug up +.EE +.SH FILES +.IP /dev/cronyx +The special device file for adapter options management. +.SH SEE ALSO +.PP +cx(4) diff --git a/sbin/i386/cxconfig/cxconfig.c b/sbin/i386/cxconfig/cxconfig.c new file mode 100644 index 0000000..0a2391f --- /dev/null +++ b/sbin/i386/cxconfig/cxconfig.c @@ -0,0 +1,704 @@ +/* + * Cronyx-Sigma adapter configuration utility for Unix. + * + * Copyright (C) 1994 Cronyx Ltd. + * Author: Serge Vakulenko, <vak@zebub.msk.su> + * + * This software is distributed with NO WARRANTIES, not even the implied + * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Authors grant any other persons or organisations permission to use + * or modify this software as long as this message is kept with the software, + * all derivative works or modified versions. + * + * Version 1.1, Wed Oct 26 16:08:09 MSK 1994 + * + * Usage: + * cxconfig [-a] + * -- print status of all channels + * cxconfig [-a] <channel> + * -- print status of the channel + * cxconfig <channel> <option>... + * -- set channel options + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/cronyx.h> +#include <net/if.h> +#include <stdio.h> + +#define CXDEV "/dev/cronyx" +#define atoi(a) strtol((a), (char**)0, 0) + +cx_options_t o; +int aflag; + +char *symbol (unsigned char sym) +{ + static char buf[40]; + + if (sym < ' ') + sprintf (buf, "^%c", sym+0100); + else if (sym == '\\') + strcat (buf, "\\\\"); + else if (sym < 127) + sprintf (buf, "%c", sym); + else + sprintf (buf, "\\%03o", sym); + return (buf); +} + +unsigned char atosym (char *s) +{ + unsigned char c; + + if (*s == '^') + return (*++s & 037); + if (*s == '\\') + return (strtol (++s, 0, 8)); + return (*s); +} + +void usage () +{ + printf ("Cronyx-Sigma Adapter Configuration Utility, Version 1.0\n"); + printf ("Copyright (C) 1994 Cronyx Ltd.\n"); + printf ("Usage:\n"); + printf ("\tcxconfig [-a]\n"); + printf ("\t\t-- print status of all channels\n"); + printf ("\tcxconfig [-a] <channel>\n"); + printf ("\t\t-- print status of the channel\n"); + printf ("\tcxconfig <channel> [async | hdlc | bisync | x.21] [ispeed #] [ospeed #]\n"); + printf ("\t\t[+cts | -cts]\n"); + printf ("\t\t-- set channel options\n"); + exit (1); +} + +char *chantype (int type) +{ + switch (type) { + case T_NONE: return ("none"); + case T_ASYNC: return ("RS-232"); + case T_UNIV_RS232: return ("RS-232"); + case T_UNIV_RS449: return ("RS-232/RS-449"); + case T_UNIV_V35: return ("RS-232/V.35"); + case T_SYNC_RS232: return ("RS-232"); + case T_SYNC_V35: return ("V.35"); + case T_SYNC_RS449: return ("RS-449"); + } +} + +char *chanmode (int mode) +{ + switch (mode) { + case M_ASYNC: return ("Async"); + case M_HDLC: return ("HDLC"); + case M_BISYNC: return ("Bisync"); + case M_X21: return ("X.21"); + default: return ("???"); + } +} + +void getchan (int channel) +{ + int s = open (CXDEV, 0); + if (s < 0) { + perror (CXDEV); + exit (1); + } + o.board = channel/NCHAN; + o.channel = channel%NCHAN; + if (ioctl (s, CXIOCGETMODE, (caddr_t)&o) < 0) { + perror ("cxconfig: CXIOCGETMODE"); + exit (1); + } + close (s); + if (o.type == T_NONE) { + fprintf (stderr, "cx%d: channel %d not configured\n", o.board, + o.channel); + exit (1); + } +} + +void setchan (int channel) +{ + int s = open (CXDEV, 0); + if (s < 0) { + perror (CXDEV); + exit (1); + } + o.board = channel/NCHAN; + o.channel = channel%NCHAN; + if (ioctl (s, CXIOCSETMODE, (caddr_t)&o) < 0) { + perror ("cxconfig: CXIOCSETMODE"); + exit (1); + } + close (s); +} + +void printopt () +{ + /* Common channel options */ + /* channel option register 4 */ + printf ("\t"); + printf ("fifo=%d ", o.opt.cor4.thr); /* FIFO threshold */ + printf ("%cctsdown ", o.opt.cor4.cts_zd ? '+' : '-'); /* detect 1 to 0 transition on the CTS */ + printf ("%ccddown ", o.opt.cor4.cd_zd ? '+' : '-'); /* detect 1 to 0 transition on the CD */ + printf ("%cdsrdown ", o.opt.cor4.dsr_zd ? '+' : '-'); /* detect 1 to 0 transition on the DSR */ + printf ("\n"); + + /* channel option register 5 */ + printf ("\t"); + printf ("rfifo=%d ", o.opt.cor5.rx_thr); /* receive flow control FIFO threshold */ + printf ("%cctsup ", o.opt.cor5.cts_od ? '+' : '-'); /* detect 0 to 1 transition on the CTS */ + printf ("%ccdup ", o.opt.cor5.cd_od ? '+' : '-'); /* detect 0 to 1 transition on the CD */ + printf ("%cdsrup ", o.opt.cor5.dsr_od ? '+' : '-'); /* detect 0 to 1 transition on the DSR */ + printf ("\n"); + + /* receive clock option register */ + printf ("\t"); + printf ("%s ", o.opt.rcor.encod == ENCOD_NRZ ? "nrz" : /* signal encoding */ + o.opt.rcor.encod == ENCOD_NRZI ? "nrzi" : + o.opt.rcor.encod == ENCOD_MANCHESTER ? "manchester" : "???"); + printf ("%cdpll ", o.opt.rcor.dpll ? '+' : '-'); /* DPLL enable */ + + /* transmit clock option register */ + printf ("%clloop ", o.opt.tcor.llm ? '+' : '-'); /* local loopback mode */ + printf ("%cextclock ", o.opt.tcor.ext1x ? '+' : '-'); /* external 1x clock mode */ + printf ("\n"); + + switch (o.mode) { + case M_ASYNC: /* async mode options */ + /* channel option register 1 */ + printf ("\t"); + printf ("cs%d ", o.aopt.cor1.charlen+1); /* character length, 5..8 */ + printf ("par%s ", o.aopt.cor1.parity ? "odd" : "even"); /* parity */ + printf ("%cignpar ", o.aopt.cor1.ignpar ? '+' : '-'); /* ignore parity */ + if (o.aopt.cor1.parmode != PARM_NORMAL) /* parity mode */ + printf ("%s ", o.aopt.cor1.parmode == PARM_NOPAR ? "nopar" : + o.aopt.cor1.parmode == PARM_FORCE ? "forcepar" : "???"); + printf ("\n"); + + /* channel option register 2 */ + printf ("\t"); + printf ("%cdsr ", o.aopt.cor2.dsrae ? '+' : '-'); /* DSR automatic enable */ + printf ("%ccts ", o.aopt.cor2.ctsae ? '+' : '-'); /* CTS automatic enable */ + printf ("%crts ", o.aopt.cor2.rtsao ? '+' : '-'); /* RTS automatic output enable */ + printf ("%crloop ", o.aopt.cor2.rlm ? '+' : '-'); /* remote loopback mode enable */ + printf ("%cetc ", o.aopt.cor2.etc ? '+' : '-'); /* embedded transmitter cmd enable */ + printf ("%cxon ", o.aopt.cor2.ixon ? '+' : '-'); /* in-band XON/XOFF enable */ + printf ("%cxany ", o.aopt.cor2.ixany ? '+' : '-'); /* XON on any character */ + printf ("\n"); + + /* option register 3 */ + printf ("\t"); + printf ("%s ", o.aopt.cor3.stopb == STOPB_1 ? "stopb1" : /* stop bit length */ + o.aopt.cor3.stopb == STOPB_15 ? "stopb1.5" : + o.aopt.cor3.stopb == STOPB_2 ? "stopb2" : "???"); + printf ("%csdt ", o.aopt.cor3.scde ? '+' : '-'); /* special char detection enable */ + printf ("%cflowct ", o.aopt.cor3.flowct ? '+' : '-'); /* flow control transparency mode */ + printf ("%crdt ", o.aopt.cor3.rngde ? '+' : '-'); /* range detect enable */ + printf ("%cexdt ", o.aopt.cor3.escde ? '+' : '-'); /* extended spec. char detect enable */ + printf ("\n"); + + /* channel option register 6 */ + printf ("\t"); + printf ("%s ", o.aopt.cor6.parerr == PERR_INTR ? "parintr" : /* parity/framing error actions */ + o.aopt.cor6.parerr == PERR_NULL ? "parnull" : + o.aopt.cor6.parerr == PERR_IGNORE ? "parign" : + o.aopt.cor6.parerr == PERR_DISCARD ? "pardisc" : + o.aopt.cor6.parerr == PERR_FFNULL ? "parffnull" : "???"); + printf ("%s ", o.aopt.cor6.brk == BRK_INTR ? "brkintr" : /* action on break condition */ + o.aopt.cor6.brk == BRK_NULL ? "brknull" : + o.aopt.cor6.brk == BRK_DISCARD ? "brkdisc" : "???"); + printf ("%cinlcr ", o.aopt.cor6.inlcr ? '+' : '-'); /* translate NL to CR on input */ + printf ("%cicrnl ", o.aopt.cor6.icrnl ? '+' : '-'); /* translate CR to NL on input */ + printf ("%cigncr ", o.aopt.cor6.igncr ? '+' : '-'); /* discard CR on input */ + printf ("\n"); + + /* channel option register 7 */ + printf ("\t"); + printf ("%cocrnl ", o.aopt.cor7.ocrnl ? '+' : '-'); /* translate CR to NL on output */ + printf ("%conlcr ", o.aopt.cor7.onlcr ? '+' : '-'); /* translate NL to CR on output */ + printf ("%cfcerr ", o.aopt.cor7.fcerr ? '+' : '-'); /* process flow ctl err chars enable */ + printf ("%clnext ", o.aopt.cor7.lnext ? '+' : '-'); /* LNext option enable */ + printf ("%cistrip ", o.aopt.cor7.istrip ? '+' : '-'); /* strip 8-bit on input */ + printf ("\n"); + + printf ("\t"); + printf ("schr1=%s ", symbol (o.aopt.schr1)); /* special character register 1 (XON) */ + printf ("schr2=%s ", symbol (o.aopt.schr2)); /* special character register 2 (XOFF) */ + printf ("schr3=%s ", symbol (o.aopt.schr3)); /* special character register 3 */ + printf ("schr4=%s ", symbol (o.aopt.schr4)); /* special character register 4 */ + printf ("scrl=%s ", symbol (o.aopt.scrl)); /* special character range low */ + printf ("scrh=%s ", symbol (o.aopt.scrh)); /* special character range high */ + printf ("lnext=%s ", symbol (o.aopt.lnxt)); /* LNext character */ + printf ("\n"); + break; + + case M_HDLC: /* hdlc mode options */ + /* hdlc channel option register 1 */ + printf ("\t"); + printf ("if%d ", o.hopt.cor1.ifflags); /* number of inter-frame flags sent */ + printf ("%s ", o.hopt.cor1.admode == ADMODE_NOADDR ? "noaddr" : /* addressing mode */ + o.hopt.cor1.admode == ADMODE_4_1 ? "addr1" : + o.hopt.cor1.admode == ADMODE_2_2 ? "addr2" : "???"); + printf ("%cclrdet ", o.hopt.cor1.clrdet ? '+' : '-'); /* clear detect for X.21 data transfer phase */ + printf ("addrlen%d ", o.hopt.cor1.aflo + 1); /* address field length option */ + printf ("\n"); + + /* hdlc channel option register 2 */ + printf ("\t"); + printf ("%cdsr ", o.hopt.cor2.dsrae ? '+' : '-'); /* DSR automatic enable */ + printf ("%ccts ", o.hopt.cor2.ctsae ? '+' : '-'); /* CTS automatic enable */ + printf ("%crts ", o.hopt.cor2.rtsao ? '+' : '-'); /* RTS automatic output enable */ + printf ("%ccrcinv ", o.hopt.cor2.crcninv ? '-' : '+'); /* CRC invertion option */ + printf ("%cfcsapd ", o.hopt.cor2.fcsapd ? '+' : '-'); /* FCS append */ + printf ("\n"); + + /* hdlc channel option register 3 */ + printf ("\t"); + printf ("pad%d ", o.hopt.cor3.padcnt); /* pad character count */ + printf ("idle%s ", o.hopt.cor3.idle ? "mark" : "flag"); /* idle mode */ + printf ("%cfcs ", o.hopt.cor3.nofcs ? '-' : '+'); /* FCS disable */ + printf ("fcs-%s ", o.hopt.cor3.fcspre ? "crc-16" : "v.41"); /* FCS preset */ + printf ("syn=%s ", o.hopt.cor3.syncpat ? "0xAA" : "0x00"); /* send sync pattern */ + printf ("%csyn ", o.hopt.cor3.sndpad ? '+' : '-'); /* send pad characters before flag enable */ + printf ("\n"); + + printf ("\t"); + printf ("rfar1=0x%02x ", o.hopt.rfar1); /* receive frame address register 1 */ + printf ("rfar2=0x%02x ", o.hopt.rfar2); /* receive frame address register 2 */ + printf ("rfar3=0x%02x ", o.hopt.rfar3); /* receive frame address register 3 */ + printf ("rfar4=0x%02x ", o.hopt.rfar4); /* receive frame address register 4 */ + printf ("crc-%s ", o.hopt.cpsr ? "16" : "v.41"); /* CRC polynomial select */ + printf ("\n"); + break; + + case M_BISYNC: /* bisync mode options */ + /* channel option register 1 */ + printf ("\t"); + printf ("cs%d ", o.bopt.cor1.charlen+1); /* character length, 5..8 */ + printf ("par%s ", o.bopt.cor1.parity ? "odd" : "even"); /* parity */ + printf ("%cignpar ", o.bopt.cor1.ignpar ? '+' : '-'); /* ignore parity */ + if (o.bopt.cor1.parmode != PARM_NORMAL) /* parity mode */ + printf ("%s ", o.bopt.cor1.parmode == PARM_NOPAR ? "nopar" : + o.bopt.cor1.parmode == PARM_FORCE ? "forcepar" : "???"); + printf ("\n"); + + /* channel option register 2 */ + printf ("\t"); + printf ("syn%d ", o.bopt.cor2.syns+2); /* number of extra SYN chars before a frame */ + printf ("%ccrcinv ", o.bopt.cor2.crcninv ? '-' : '+'); /* CRC invertion option */ + printf ("%s ", o.bopt.cor2.ebcdic ? "ebcdic" : "ascii"); /* use EBCDIC as char set (instead of ASCII) */ + printf ("%cbccapd ", o.bopt.cor2.bcc ? '+' : '-'); /* BCC append enable */ + printf ("%s ", o.bopt.cor2.lrc ? "lrc" : "crc-16"); /* longitudinal redundancy check */ + printf ("\n"); + + /* channel option register 3 */ + printf ("\t"); + printf ("pad%d ", o.bopt.cor3.padcnt); /* pad character count */ + printf ("idle%s ", o.bopt.cor3.idle ? "mark" : "syn"); /* idle mode */ + printf ("%cfcs ", o.bopt.cor3.nofcs ? '-' : '+'); /* FCS disable */ + printf ("fcs-%s ", o.bopt.cor3.fcspre ? "crc-16" : "v.41"); /* FCS preset */ + printf ("syn=%s ", o.bopt.cor3.padpat ? "0x55" : "0xAA"); /* send sync pattern */ + printf ("%csyn ", o.bopt.cor3.sndpad ? '+' : '-'); /* send pad characters before flag enable */ + printf ("\n"); + + /* channel option register 6 */ + printf ("\t"); + printf ("specterm=%s ", symbol (o.bopt.cor6.specterm)); /* special termination character */ + + printf ("crc-%s ", o.bopt.cpsr ? "16" : "v.41"); /* CRC polynomial select */ + printf ("\n"); + break; + + case M_X21: /* x.21 mode options */ + /* channel option register 1 */ + printf ("\t"); + printf ("cs%d ", o.xopt.cor1.charlen+1); /* character length, 5..8 */ + printf ("par%s ", o.xopt.cor1.parity ? "odd" : "even"); /* parity */ + printf ("%cignpar ", o.xopt.cor1.ignpar ? '+' : '-'); /* ignore parity */ + if (o.xopt.cor1.parmode != PARM_NORMAL) /* parity mode */ + printf ("%s ", o.xopt.cor1.parmode == PARM_NOPAR ? "nopar" : + o.xopt.cor1.parmode == PARM_FORCE ? "forcepar" : "???"); + printf ("\n"); + + /* channel option register 2 */ + printf ("\t"); + printf ("%cetc ", o.xopt.cor2.etc ? '+' : '-'); /* embedded transmitter cmd enable */ + + /* channel option register 3 */ + printf ("%csdt ", o.xopt.cor3.scde ? '+' : '-'); /* special char detection enable */ + printf ("%cstripsyn ", o.xopt.cor3.stripsyn ? '+' : '-'); /* treat SYN chars as special condition */ + printf ("%cssdt ", o.xopt.cor3.ssde ? '+' : '-'); /* steady state detect enable */ + printf ("syn%c ", o.xopt.cor3.syn ? '1' : '2'); /* the number of SYN chars on receive */ + printf ("\n"); + + /* channel option register 6 */ + printf ("\t"); + printf ("syn=%s ", symbol (o.xopt.cor6.synchar)); /* syn character */ + + printf ("schr1=%s ", symbol (o.xopt.schr1)); /* special character register 1 */ + printf ("schr2=%s ", symbol (o.xopt.schr2)); /* special character register 2 */ + printf ("schr3=%s ", symbol (o.xopt.schr3)); /* special character register 3 */ + printf ("\n"); + break; + } +} + +void printchan (int channel) +{ + printf ("cx%d (%s) %s", channel, chantype (o.type), chanmode (o.mode)); + if (o.txbaud == o.rxbaud) + printf (" %d", o.rxbaud); + else + printf (" ospeed=%d ispeed=%d", o.txbaud, o.rxbaud); + if ((o.channel == 0 || o.channel == 8) && + (o.type == T_UNIV_V35 || o.type == T_UNIV_RS449)) + printf (" port=%s", o.iftype ? (o.type == T_UNIV_V35 ? + "v35" : "rs449") : "rs232"); + printf (o.sopt.ext ? " ext" : o.sopt.cisco ? " cisco" : " ppp"); + printf (" %ckeepalive", o.sopt.keepalive ? '+' : '-'); + printf (" %cautorts", o.sopt.norts ? '-' : '+'); + printf ("\n"); + if (aflag) + printopt (); +} + +void printall () +{ + struct ifconf ifc; + struct ifreq *ifr; + char buf[BUFSIZ], *cp; + int s, c; + + s = socket (AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror ("cxconfig: socket"); + exit (1); + } + ifc.ifc_len = sizeof (buf); + ifc.ifc_buf = buf; + if (ioctl (s, SIOCGIFCONF, (caddr_t)&ifc) < 0) { + perror ("cxconfig: SIOCGIFCONF"); + exit (1); + } + close (s); + s = open (CXDEV, 0); + if (s < 0) { + perror (CXDEV); + exit (1); + } + + ifr = ifc.ifc_req; +#define max(a,b) ((a)>(b) ? (a) : (b)) +#define size(p) max((p).sa_len, sizeof(p)) + for (cp=buf; cp<buf+ifc.ifc_len; cp+=sizeof(ifr->ifr_name)+size(ifr->ifr_addr)) { + ifr = (struct ifreq*) cp; + if (ifr->ifr_addr.sa_family != AF_LINK) + continue; + if (strncmp (ifr->ifr_name, "cx", 2) != 0) + continue; + c = atoi (ifr->ifr_name + 2); + o.board = c/NCHAN; + o.channel = c%NCHAN; + if (ioctl (s, CXIOCGETMODE, (caddr_t)&o) < 0) { + perror ("cxconfig: CXIOCGETMODE"); + exit (1); + } + printchan (c); + } + close (s); +} + +void set_interface_type (char *type) +{ + if (o.channel != 0 && o.channel != 8) { + printf ("interface option is applicable only for channels 0 and 8\n"); + exit (1); + } + if (o.type != T_UNIV_V35 && o.type != T_UNIV_RS449) { + printf ("interface option is applicable only for universal channels\n"); + exit (1); + } + if (! strcasecmp (type, "port=rs232")) + o.iftype = 0; + else + o.iftype = 1; +} + +void set_async_opt (char *opt) +{ + /* channel option register 1 */ + if (! strncasecmp (opt, "cs", 2)) o.aopt.cor1.charlen = atoi (opt + 2) - 1; + else if (! strcasecmp (opt, "parodd")) o.aopt.cor1.parity = 1; + else if (! strcasecmp (opt, "pareven")) o.aopt.cor1.parity = 0; + else if (! strcasecmp (opt, "-ignpar")) o.aopt.cor1.ignpar = 0; + else if (! strcasecmp (opt, "+ignpar")) o.aopt.cor1.ignpar = 1; + else if (! strcasecmp (opt, "nopar")) o.aopt.cor1.parmode = PARM_NOPAR; + else if (! strcasecmp (opt, "forcepar")) o.aopt.cor1.parmode = PARM_FORCE; + + /* channel option register 2 */ + else if (! strcasecmp (opt, "-dsr")) o.aopt.cor2.dsrae = 0; + else if (! strcasecmp (opt, "+dsr")) o.aopt.cor2.dsrae = 1; + else if (! strcasecmp (opt, "-cts")) o.aopt.cor2.ctsae = 0; + else if (! strcasecmp (opt, "+cts")) o.aopt.cor2.ctsae = 1; + else if (! strcasecmp (opt, "-rts")) o.aopt.cor2.rtsao = 0; + else if (! strcasecmp (opt, "+rts")) o.aopt.cor2.rtsao = 1; + else if (! strcasecmp (opt, "-rloop")) o.aopt.cor2.rlm = 0; + else if (! strcasecmp (opt, "+rloop")) o.aopt.cor2.rlm = 1; + else if (! strcasecmp (opt, "-etc")) o.aopt.cor2.etc = 0; + else if (! strcasecmp (opt, "+etc")) o.aopt.cor2.etc = 1; + else if (! strcasecmp (opt, "-ixon")) o.aopt.cor2.ixon = 0; + else if (! strcasecmp (opt, "+ixon")) o.aopt.cor2.ixon = 1; + else if (! strcasecmp (opt, "-ixany")) o.aopt.cor2.ixany = 0; + else if (! strcasecmp (opt, "+ixany")) o.aopt.cor2.ixany = 1; + + /* option register 3 */ + else if (! strcasecmp (opt, "stopb1")) o.aopt.cor3.stopb = STOPB_1; + else if (! strcasecmp (opt, "stopb1.5")) o.aopt.cor3.stopb = STOPB_15; + else if (! strcasecmp (opt, "stopb2")) o.aopt.cor3.stopb = STOPB_2; + else if (! strcasecmp (opt, "-sdt")) o.aopt.cor3.scde = 0; + else if (! strcasecmp (opt, "+sdt")) o.aopt.cor3.scde = 1; + else if (! strcasecmp (opt, "-flowct")) o.aopt.cor3.flowct = 0; + else if (! strcasecmp (opt, "+flowct")) o.aopt.cor3.flowct = 1; + else if (! strcasecmp (opt, "-rdt")) o.aopt.cor3.rngde = 0; + else if (! strcasecmp (opt, "+rdt")) o.aopt.cor3.rngde = 1; + else if (! strcasecmp (opt, "-exdt")) o.aopt.cor3.escde = 0; + else if (! strcasecmp (opt, "+exdt")) o.aopt.cor3.escde = 1; + + /* channel option register 6 */ + else if (! strcasecmp (opt, "parintr")) o.aopt.cor6.parerr = PERR_INTR; + else if (! strcasecmp (opt, "parnull")) o.aopt.cor6.parerr = PERR_NULL; + else if (! strcasecmp (opt, "parign")) o.aopt.cor6.parerr = PERR_IGNORE; + else if (! strcasecmp (opt, "pardisc")) o.aopt.cor6.parerr = PERR_DISCARD; + else if (! strcasecmp (opt, "parffnull")) o.aopt.cor6.parerr = PERR_FFNULL; + else if (! strcasecmp (opt, "brkintr")) o.aopt.cor6.brk = BRK_INTR; + else if (! strcasecmp (opt, "brknull")) o.aopt.cor6.brk = BRK_NULL; + else if (! strcasecmp (opt, "brkdisc")) o.aopt.cor6.brk = BRK_DISCARD; + else if (! strcasecmp (opt, "-inlcr")) o.aopt.cor6.inlcr = 0; + else if (! strcasecmp (opt, "+inlcr")) o.aopt.cor6.inlcr = 1; + else if (! strcasecmp (opt, "-icrnl")) o.aopt.cor6.icrnl = 0; + else if (! strcasecmp (opt, "+icrnl")) o.aopt.cor6.icrnl = 1; + else if (! strcasecmp (opt, "-igncr")) o.aopt.cor6.igncr = 0; + else if (! strcasecmp (opt, "+igncr")) o.aopt.cor6.igncr = 1; + + /* channel option register 7 */ + else if (! strcasecmp (opt, "-ocrnl")) o.aopt.cor7.ocrnl = 0; + else if (! strcasecmp (opt, "+ocrnl")) o.aopt.cor7.ocrnl = 1; + else if (! strcasecmp (opt, "-onlcr")) o.aopt.cor7.onlcr = 0; + else if (! strcasecmp (opt, "+onlcr")) o.aopt.cor7.onlcr = 1; + else if (! strcasecmp (opt, "-fcerr")) o.aopt.cor7.fcerr = 0; + else if (! strcasecmp (opt, "+fcerr")) o.aopt.cor7.fcerr = 1; + else if (! strcasecmp (opt, "-lnext")) o.aopt.cor7.lnext = 0; + else if (! strcasecmp (opt, "+lnext")) o.aopt.cor7.lnext = 1; + else if (! strcasecmp (opt, "-istrip")) o.aopt.cor7.istrip = 0; + else if (! strcasecmp (opt, "+istrip")) o.aopt.cor7.istrip = 1; + + else if (! strncasecmp (opt, "schr1=", 6)) o.aopt.schr1 = atosym (opt+6); + else if (! strncasecmp (opt, "schr2=", 6)) o.aopt.schr2 = atosym (opt+6); + else if (! strncasecmp (opt, "schr3=", 6)) o.aopt.schr3 = atosym (opt+6); + else if (! strncasecmp (opt, "schr4=", 6)) o.aopt.schr4 = atosym (opt+6); + else if (! strncasecmp (opt, "scrl=", 5)) o.aopt.scrl = atosym (opt+5); + else if (! strncasecmp (opt, "scrh=", 5)) o.aopt.scrh = atosym (opt+5); + else if (! strncasecmp (opt, "lnext=", 6)) o.aopt.lnxt = atosym (opt+6); + else + usage (); +} + +void set_hdlc_opt (char *opt) +{ + /* hdlc channel option register 1 */ + if (! strncasecmp (opt, "if", 2)) o.hopt.cor1.ifflags = atoi (opt + 2); + else if (! strcasecmp (opt, "noaddr")) o.hopt.cor1.admode = ADMODE_NOADDR; + else if (! strcasecmp (opt, "addr1")) o.hopt.cor1.admode = ADMODE_4_1; + else if (! strcasecmp (opt, "addr2")) o.hopt.cor1.admode = ADMODE_2_2; + else if (! strcasecmp (opt, "-clrdet")) o.hopt.cor1.clrdet = 0; + else if (! strcasecmp (opt, "+clrdet")) o.hopt.cor1.clrdet = 1; + else if (! strcasecmp (opt, "addrlen1")) o.hopt.cor1.aflo = 0; + else if (! strcasecmp (opt, "addrlen2")) o.hopt.cor1.aflo = 1; + + /* hdlc channel option register 2 */ + else if (! strcasecmp (opt, "-dsr")) o.hopt.cor2.dsrae = 0; + else if (! strcasecmp (opt, "+dsr")) o.hopt.cor2.dsrae = 1; + else if (! strcasecmp (opt, "-cts")) o.hopt.cor2.ctsae = 0; + else if (! strcasecmp (opt, "+cts")) o.hopt.cor2.ctsae = 1; + else if (! strcasecmp (opt, "-rts")) o.hopt.cor2.rtsao = 0; + else if (! strcasecmp (opt, "+rts")) o.hopt.cor2.rtsao = 1; + else if (! strcasecmp (opt, "-fcsapd")) o.hopt.cor2.fcsapd = 0; + else if (! strcasecmp (opt, "+fcsapd")) o.hopt.cor2.fcsapd = 1; + else if (! strcasecmp (opt, "-crcinv")) o.hopt.cor2.crcninv = 1; + else if (! strcasecmp (opt, "+crcinv")) o.hopt.cor2.crcninv = 0; + + /* hdlc channel option register 3 */ + else if (! strncasecmp (opt, "pad", 3)) o.hopt.cor3.padcnt = atoi (opt + 3); + else if (! strcasecmp (opt, "idlemark")) o.hopt.cor3.idle = 1; + else if (! strcasecmp (opt, "idleflag")) o.hopt.cor3.idle = 0; + else if (! strcasecmp (opt, "-fcs")) o.hopt.cor3.nofcs = 1; + else if (! strcasecmp (opt, "+fcs")) o.hopt.cor3.nofcs = 0; + else if (! strcasecmp (opt, "fcs-crc-16")) o.hopt.cor3.fcspre = 1; + else if (! strcasecmp (opt, "fcs-v.41")) o.hopt.cor3.fcspre = 0; + else if (! strcasecmp (opt, "syn=0xaa")) o.hopt.cor3.syncpat = 1; + else if (! strcasecmp (opt, "syn=0x00")) o.hopt.cor3.syncpat = 0; + else if (! strcasecmp (opt, "-syn")) o.hopt.cor3.sndpad = 0; + else if (! strcasecmp (opt, "+syn")) o.hopt.cor3.sndpad = 1; + + else if (! strncasecmp (opt, "rfar1=", 6)) o.hopt.rfar1 = atoi (opt + 6); + else if (! strncasecmp (opt, "rfar2=", 6)) o.hopt.rfar2 = atoi (opt + 6); + else if (! strncasecmp (opt, "rfar3=", 6)) o.hopt.rfar3 = atoi (opt + 6); + else if (! strncasecmp (opt, "rfar4=", 6)) o.hopt.rfar4 = atoi (opt + 6); + else if (! strcasecmp (opt, "crc-16")) o.hopt.cpsr = 1; + else if (! strcasecmp (opt, "crc-v.41")) o.hopt.cpsr = 0; + else usage (); +} + +void set_bisync_opt (char *opt) +{ + usage (); +} + +void set_x21_opt (char *opt) +{ + usage (); +} + +int main (int argc, char **argv) +{ + int channel; + + for (--argc, ++argv; argc>0 && **argv=='-'; --argc, ++argv) + if (! strcasecmp (*argv, "-a")) + ++aflag; + else + usage (); + + if (argc <= 0) { + printall (); + return (0); + } + + if (argv[0][0]=='c' && argv[0][1]=='x') + *argv += 2; + if (**argv<'0' || **argv>'9') + usage (); + channel = atoi (*argv); + --argc, ++argv; + getchan (channel); + + if (argc <= 0) { + printchan (channel); + return (0); + } + + for (; argc>0; --argc, ++argv) + if (**argv == '(') + continue; + else if (! strncasecmp (*argv, "ispeed=", 7)) + o.rxbaud = atoi (*argv+7); + else if (! strncasecmp (*argv, "ospeed=", 7)) + o.txbaud = atoi (*argv+7); + else if (! strcasecmp (*argv, "async")) + o.mode = M_ASYNC; + else if (! strcasecmp (*argv, "hdlc")) + o.mode = M_HDLC; + else if (! strcasecmp (*argv, "bisync") || + ! strcasecmp (*argv, "bsc")) + o.mode = M_BISYNC; + else if (! strcasecmp (*argv, "x.21") || + ! strcasecmp (*argv, "x21")) + o.mode = M_X21; + else if (**argv>='0' && **argv<='9') + o.txbaud = o.rxbaud = atoi (*argv); + else if (! strcasecmp (*argv, "cisco")) { + o.sopt.cisco = 1; + o.sopt.ext = 0; + } else if (! strcasecmp (*argv, "ppp")) { + o.sopt.cisco = 0; + o.sopt.ext = 0; + } else if (! strcasecmp (*argv, "ext")) + o.sopt.ext = 1; + else if (! strcasecmp (*argv, "+keepalive")) + o.sopt.keepalive = 1; + else if (! strcasecmp (*argv, "-keepalive")) + o.sopt.keepalive = 0; + else if (! strcasecmp (*argv, "+autorts")) + o.sopt.norts = 0; + else if (! strcasecmp (*argv, "-autorts")) + o.sopt.norts = 1; + else if (! strcasecmp (*argv, "port=rs232") || + ! strcasecmp (*argv, "port=rs449") || + ! strcasecmp (*argv, "port=v35")) + set_interface_type (*argv); + + /* + * Common channel options + */ + /* channel option register 4 */ + else if (! strcasecmp (*argv, "-ctsdown")) + o.opt.cor4.cts_zd = 0; + else if (! strcasecmp (*argv, "+ctsdown")) + o.opt.cor4.cts_zd = 1; + else if (! strcasecmp (*argv, "-cddown")) + o.opt.cor4.cd_zd = 0; + else if (! strcasecmp (*argv, "+cddown")) + o.opt.cor4.cd_zd = 1; + else if (! strcasecmp (*argv, "-dsrdown")) + o.opt.cor4.dsr_zd = 0; + else if (! strcasecmp (*argv, "+dsrdown")) + o.opt.cor4.dsr_zd = 1; + else if (! strncasecmp (*argv, "fifo=", 5)) + o.opt.cor4.thr = atoi (*argv + 5); + + /* channel option register 5 */ + else if (! strcasecmp (*argv, "-ctsup")) + o.opt.cor5.cts_od = 0; + else if (! strcasecmp (*argv, "+ctsup")) + o.opt.cor5.cts_od = 1; + else if (! strcasecmp (*argv, "-cdup")) + o.opt.cor5.cd_od = 0; + else if (! strcasecmp (*argv, "+cdup")) + o.opt.cor5.cd_od = 1; + else if (! strcasecmp (*argv, "-dsrup")) + o.opt.cor5.dsr_od = 0; + else if (! strcasecmp (*argv, "+dsrup")) + o.opt.cor5.dsr_od = 1; + else if (! strncasecmp (*argv, "rfifo=", 6)) + o.opt.cor5.rx_thr = atoi (*argv + 6); + + /* receive clock option register */ + else if (! strcasecmp (*argv, "nrz")) + o.opt.rcor.encod = ENCOD_NRZ; + else if (! strcasecmp (*argv, "nrzi")) + o.opt.rcor.encod = ENCOD_NRZI; + else if (! strcasecmp (*argv, "manchester")) + o.opt.rcor.encod = ENCOD_MANCHESTER; + else if (! strcasecmp (*argv, "-dpll")) + o.opt.rcor.dpll = 0; + else if (! strcasecmp (*argv, "+dpll")) + o.opt.rcor.dpll = 1; + + /* transmit clock option register */ + else if (! strcasecmp (*argv, "-lloop")) + o.opt.tcor.llm = 0; + else if (! strcasecmp (*argv, "+lloop")) + o.opt.tcor.llm = 1; + else if (! strcasecmp (*argv, "-extclock")) + o.opt.tcor.ext1x = 0; + else if (! strcasecmp (*argv, "+extclock")) + o.opt.tcor.ext1x = 1; + + /* + * Mode dependent channel options + */ + else switch (o.mode) { + case M_ASYNC: set_async_opt (*argv); break; + case M_HDLC: set_hdlc_opt (*argv); break; + case M_BISYNC: set_bisync_opt (*argv); break; + case M_X21: set_x21_opt (*argv); break; + } + + setchan (channel); + return (0); +} diff --git a/share/man/man4/man4.i386/cx.4 b/share/man/man4/man4.i386/cx.4 new file mode 100644 index 0000000..969b735 --- /dev/null +++ b/share/man/man4/man4.i386/cx.4 @@ -0,0 +1,239 @@ +.TH Cronyx-Sigma 1 +.rm ES +.rm EE +.de ES +.PP +.nf +.in +0.5i +.. +.de EE +.in -0.5i +.fi +.. +.na +.SH NAME +.B cx +\- asynchronous Cronyx-Sigma adapter driver +.br +.B if_cx +\- synchronous Cronyx-Sigma adapter driver +.SH CONFIGURATION +.B +device cx0 at isa? port 0x240 irq 15 drq 7 +.br +.B +device cx1 at isa? port 0x260 irq 12 drq 6 +.br +.B +pseudo-device sppp +.P +The base i/o port address should be set by jumpers on the board. +The DMA i/o channel and interrupt request numbers are configured +by software at adapter initialization. Legal values are: +.IP Port +0x240, 0x260, 0x280, 0x300, 0x320, 0x380 +.IP IRQ +3, 5, 7, 10, 11, 12, 15 +.IP DMA +5, 6, 7 +.SH DESCRIPTION +.PP +The Cronyx-Sigma driver supports the adapters of models 100, +400, 500, 401, 404, 410, 440, 703, 801, 810, 840. Different models have +different set of channels: +.ES + Model Channels +---------------------------------------------- + Cronyx-Sigma-100 0 + Cronyx-Sigma-400 4, 5, 6, 7 + Cronyx-Sigma-500 0, 4, 5, 6, 7 + Cronyx-Sigma-401 0, 1, 2, 3 + Cronyx-Sigma-404 0, 1, 2, 3 + Cronyx-Sigma-410 0, 1, 2, 3 + Cronyx-Sigma-440 0, 1, 2, 3 + Cronyx-Sigma-703 0, 1, 2, 4, 5, 6, 7 + Cronyx-Sigma-801 0, 1, 2, 3, 4, 5, 6, 7 + Cronyx-Sigma-810 0, 1, 2, 3, 4, 5, 6, 7 + Cronyx-Sigma-840 0, 1, 2, 3, 4, 5, 6, 7 +.EE +.PP +A pair of two adapters can be united together by the special +short inter-board cable. Two united adapters use the same +IRQ and DMA channels and from the point of driver works +as the single 16-channel multiplexer. One of the united +boards is ``master'' and the other is ``slave''. +.PP +The channels of the slave united board are numbered by the driver +beginning with 8, for example, the united adapter of the model 100/500 +has channels 0, 8, 12, 13, 14, 15. +.PP +The channels which have the RS-232 interface can be used +both in synchronous and asynchronous modes (software selectable +by cxconfig utility) and hence are called ``universal'' channels. +.PP +The special device files (/dev/*) for the adapter Cronyx-Sigma +are created by the command file ``/dev/MAKEDEV.cx''. +An example: +.ES +cd /dev +sh MAKEDEV.cx cronyx ttyx0 ttyx1 ttyy0 +.EE +.SH "Asynchronous driver" +.PP +The asynchronous channel device files have the names: +/dev/ttyx# - for adapter cx0, /dev/ttyy# - for adapter cx1, +/dev/ttyz# - for cx2. +Here\ # is the channel number in hexadecimal form, 0-9-a-f. +.PP +The driver fulfills the following standard ioctl requests (see ioctl(2)): +.IP TIOCSBRK +Start sending BREAK. +.IP TIOCCBRK +Stop sending BREAK. +.IP TIOCSDTR +Set DTR signal (DTR := 1). The DTR signal is always set +on the first open(2) and could be changed by +TIOCCDTR, TIOCSDTR, TIOCMSET, TIOCMBIS, TIOCMBIC ioctl calls. +.IP TIOCCDTR +Clear DTR signal (DTR := 0). +.IP TIOCMSET +Set the given values of DTR and RTS signals (<DTR:RTS> := data). +The signals DTR and RTS are controlled by TIOCM_DTR and TIOCM_RTS +bits of the data argument of the ioctl system call. +.IP TIOCMBIS +Set DTR and RTS signals (<DTR:RTS> |= data). +The signals DTR and RTS are controlled by TIOCM_DTR and TIOCM_RTS +bits of the data argument of the ioctl system call. +.IP TIOCMBIC +Clear DTR and RTS signals (<DTR:RTS> &= ~data). +The signals DTR and RTS are controlled by TIOCM_DTR and TIOCM_RTS +bits of the data argument of the ioctl system call. +.IP TIOCMGET +Determine the state of the modem signals of the line. +After the call the data argument contains the following bits: +.ES +TIOCM_LE - always set (Line Enabled) +TIOCM_DSR - Data Set Ready signal received +TIOCM_CTS - Clear To Send signal received +TIOCM_CD - Data Carrier Detect signal received +TIOCM_DTR - Data Terminal Ready signal transmitted +TIOCM_RTS - Request To Send signal transmitted +.EE +.SH "Synchronous driver" +.PP +The synchronous channels and universal channels, turned to the synchronous +mode by the cxconfig(8) utility, are accessible as network +interfaces named ``cx#'' where\ # is the channel number, 0..47. +All standard network interface parameters could be set by ifconfig(8) utility. +The cxconfig(8) command is used to change some extended channel +options, and also for setting the high-level software protocol +(e.g. PPP or Cisco HDLC). +.PP +The universal channels could be used both in asynchronous and synchronous modes. +By default the asynchronous mode is set. +The mode could be changed by cxconfig(8) utility. +The mode is blocked while the channel is busy (an asynchronous channel +in open state or the network interface is up). +.SH "Synchronous Point-to-Point protocol" +.PP +The Cronyx-Sigma driver uses the built-in implementation of the synchronous +Point-to-Point protocol (sppp). It includes the support for such +protocols as PPP/HDLC and Cisco/HDLC, and also the automatic +connection loss test (via keepalive packets). +The sppp protocol set is implemented as an independent module +and could be used by other drivers of synchronous serial channels. +The version of the driver for BSD/386 (BSDI) operating system +also supports the usage of the general set of synchronous +protocols, implemented inside the OS. +The external protocol set could be selected by ``cxconfig ext'' command +(see cxconfig(8)). +.SH "Channel Options Management" +.PP +The cxconfig(8) utility is used for setting the channels options. +The channel options are generally set at the start of the operating +system (for example, from the file /etc/rc). +Note, that not all options have a sense for every particular +case, and an attempt to set some of them can hang up the channel or +the whole adapter. +.PP +The actual channel options control functions are implemented via +the ioctl-s on the special device file /dev/cronyx. +There are the following ioctl-s available: +.IP CXIOCGETMODE +Get the channel option values. +.IP CXIOCSETMODE +Set the channel option values. +.PP +The data argument of the ioctl call has an address of the options structure: +.ES +typedef struct { + unsigned char board; /* adapter number, 0..2 */ + unsigned char channel; /* channel number, 0..15 */ + unsigned char type; /* channel type (read only) */ + unsigned char iftype; /* chan0 interface */ + unsigned long rxbaud; /* receiver speed */ + unsigned long txbaud; /* transmitter speed */ + cx_chan_mode_t mode; /* channel mode */ + cx_chan_opt_t opt; /* common channel options */ + cx_opt_async_t aopt; /* async mode options */ + cx_opt_hdlc_t hopt; /* hdlc mode options */ + cx_opt_bisync_t bopt; /* bisync mode options */ + cx_opt_x21_t xopt; /* x.21 mode options */ + cx_soft_opt_t sopt; /* software options and state flags */ +} cx_options_t; /* user settable options */ +.EE +.IP board +The adapter number, 0..2. +.IP channel +The channel number, 0..15. +.IP type +The type of the channel (read-only argument). +.IP iftype +The interface type of the zero (and also the eight) channel: 0 - RS-232, +1 - RS-449/V.35. +.IP rxbaud +The receiver data baud rate. +.IP txbaud +The transmitter data baud rate. +.IP mode +The channel mode: asynchronous/HDLC/Bisync/X.21. +.IP opt +The general channel options. +.IP aopt +The asynchronous mode options. +.IP hopt +The HDLC mode options. +.IP bopt +The Bisync mode options. +.IP xopt +The X.21 mode options. +.IP sopt +The software protocol options. +.SH FILES +.PP +/dev/cx?? +.IP +Asynchronous channels. +.PP +/dev/cronyx +.IP +The special device file for the channel options management. +.PP +/sys/i386/isa/cronyx.c +.br +/sys/i386/isa/cx.c +.br +/sys/i386/isa/if_cx.c +.br +/sys/i386/isa/cronyx.h +.br +/sys/i386/isa/cxreg.h +.br +/sys/net/if_spppsubr.c +.br +/sys/net/if_sppp.h +.IP +The sources of the driver. +.SH SEE ALSO +.PP +cxconfig(8) diff --git a/sys/i386/include/cronyx.h b/sys/i386/include/cronyx.h new file mode 100644 index 0000000..db56cf9 --- /dev/null +++ b/sys/i386/include/cronyx.h @@ -0,0 +1,475 @@ +/* + * Defines for Cronyx-Sigma adapter driver. + * + * Copyright (C) 1994 Cronyx Ltd. + * Author: Serge Vakulenko, <vak@zebub.msk.su> + * + * This software is distributed with NO WARRANTIES, not even the implied + * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Authors grant any other persons or organisations permission to use + * or modify this software as long as this message is kept with the software, + * all derivative works or modified versions. + * + * Version 1.1, Wed Oct 26 16:09:46 MSK 1994 + */ +/* + * Asynchronous channel mode ------------------------------------------------- + */ + +/* Parity */ +#define PAR_EVEN 0 /* even parity */ +#define PAR_ODD 1 /* odd parity */ + +/* Parity mode */ +#define PARM_NOPAR 0 /* no parity */ +#define PARM_FORCE 1 /* force parity (odd = force 1, even = 0) */ +#define PARM_NORMAL 2 /* normal parity */ + +/* Flow control transparency mode */ +#define FLOWCC_PASS 0 /* pass flow ctl chars as exceptions */ +#define FLOWCC_NOTPASS 1 /* don't pass flow ctl chars to the host */ + +/* Stop bit length */ +#define STOPB_1 2 /* 1 stop bit */ +#define STOPB_15 3 /* 1.5 stop bits */ +#define STOPB_2 4 /* 2 stop bits */ + +/* Action on break condition */ +#define BRK_INTR 0 /* generate an exception interrupt */ +#define BRK_NULL 1 /* translate to a NULL character */ +#define BRK_RESERVED 2 /* reserved */ +#define BRK_DISCARD 3 /* discard character */ + +/* Parity/framing error actions */ +#define PERR_INTR 0 /* generate an exception interrupt */ +#define PERR_NULL 1 /* translate to a NULL character */ +#define PERR_IGNORE 2 /* ignore error; char passed as good data */ +#define PERR_DISCARD 3 /* discard error character */ +#define PERR_FFNULL 5 /* translate to FF NULL char */ + +typedef struct { /* async channel option register 1 */ + unsigned charlen : 4; /* character length, 5..8 */ + unsigned ignpar : 1; /* ignore parity */ + unsigned parmode : 2; /* parity mode */ + unsigned parity : 1; /* parity */ +} cx_cor1_async_t; + +typedef struct { /* async channel option register 2 */ + unsigned dsrae : 1; /* DSR automatic enable */ + unsigned ctsae : 1; /* CTS automatic enable */ + unsigned rtsao : 1; /* RTS automatic output enable */ + unsigned rlm : 1; /* remote loopback mode enable */ + unsigned zero : 1; + unsigned etc : 1; /* embedded transmitter cmd enable */ + unsigned ixon : 1; /* in-band XON/XOFF enable */ + unsigned ixany : 1; /* XON on any character */ +} cx_cor2_async_t; + +typedef struct { /* async channel option register 3 */ + unsigned stopb : 3; /* stop bit length */ + unsigned zero : 1; + unsigned scde : 1; /* special char detection enable */ + unsigned flowct : 1; /* flow control transparency mode */ + unsigned rngde : 1; /* range detect enable */ + unsigned escde : 1; /* extended spec. char detect enable */ +} cx_cor3_async_t; + +typedef struct { /* async channel option register 6 */ + unsigned parerr : 3; /* parity/framing error actions */ + unsigned brk : 2; /* action on break condition */ + unsigned inlcr : 1; /* translate NL to CR on input */ + unsigned icrnl : 1; /* translate CR to NL on input */ + unsigned igncr : 1; /* discard CR on input */ +} cx_cor6_async_t; + +typedef struct { /* async channel option register 7 */ + unsigned ocrnl : 1; /* translate CR to NL on output */ + unsigned onlcr : 1; /* translate NL to CR on output */ + unsigned zero : 3; + unsigned fcerr : 1; /* process flow ctl err chars enable */ + unsigned lnext : 1; /* LNext option enable */ + unsigned istrip : 1; /* strip 8-bit on input */ +} cx_cor7_async_t; + +typedef struct { /* async channel options */ + cx_cor1_async_t cor1; /* channel option register 1 */ + cx_cor2_async_t cor2; /* channel option register 2 */ + cx_cor3_async_t cor3; /* option register 3 */ + cx_cor6_async_t cor6; /* channel option register 6 */ + cx_cor7_async_t cor7; /* channel option register 7 */ + unsigned char schr1; /* special character register 1 (XON) */ + unsigned char schr2; /* special character register 2 (XOFF) */ + unsigned char schr3; /* special character register 3 */ + unsigned char schr4; /* special character register 4 */ + unsigned char scrl; /* special character range low */ + unsigned char scrh; /* special character range high */ + unsigned char lnxt; /* LNext character */ +} cx_opt_async_t; + +/* + * HDLC channel mode --------------------------------------------------------- + */ +/* Address field length option */ +#define AFLO_1OCT 0 /* address field is 1 octet in length */ +#define AFLO_2OCT 1 /* address field is 2 octet in length */ + +/* Clear detect for X.21 data transfer phase */ +#define CLRDET_DISABLE 0 /* clear detect disabled */ +#define CLRDET_ENABLE 1 /* clear detect enabled */ + +/* Addressing mode */ +#define ADMODE_NOADDR 0 /* no address */ +#define ADMODE_4_1 1 /* 4 * 1 byte */ +#define ADMODE_2_2 2 /* 2 * 2 byte */ + +/* FCS append */ +#define FCS_NOTPASS 0 /* receive CRC is not passed to the host */ +#define FCS_PASS 1 /* receive CRC is passed to the host */ + +/* CRC modes */ +#define CRC_INVERT 0 /* CRC is transmitted inverted (CRC V.41) */ +#define CRC_DONT_INVERT 1 /* CRC is not transmitted inverted (CRC-16) */ + +/* Send sync pattern */ +#define SYNC_00 0 /* send 00h as pad char (NRZI encoding) */ +#define SYNC_AA 1 /* send AAh (Manchester/NRZ encoding) */ + +/* FCS preset */ +#define FCSP_ONES 0 /* FCS is preset to all ones (CRC V.41) */ +#define FCSP_ZEROS 1 /* FCS is preset to all zeros (CRC-16) */ + +/* idle mode */ +#define IDLE_FLAG 0 /* idle in flag */ +#define IDLE_MARK 1 /* idle in mark */ + +/* CRC polynomial select */ +#define POLY_V41 0 /* x^16+x^12+x^5+1 (HDLC, preset to 1) */ +#define POLY_16 1 /* x^16+x^15+x^2+1 (bisync, preset to 0) */ + +typedef struct { /* hdlc channel option register 1 */ + unsigned ifflags : 4; /* number of inter-frame flags sent */ + unsigned admode : 2; /* addressing mode */ + unsigned clrdet : 1; /* clear detect for X.21 data transfer phase */ + unsigned aflo : 1; /* address field length option */ +} cx_cor1_hdlc_t; + +typedef struct { /* hdlc channel option register 2 */ + unsigned dsrae : 1; /* DSR automatic enable */ + unsigned ctsae : 1; /* CTS automatic enable */ + unsigned rtsao : 1; /* RTS automatic output enable */ + unsigned zero1 : 1; + unsigned crcninv : 1; /* CRC invertion option */ + unsigned zero2 : 1; + unsigned fcsapd : 1; /* FCS append */ + unsigned zero3 : 1; +} cx_cor2_hdlc_t; + +typedef struct { /* hdlc channel option register 3 */ + unsigned padcnt : 3; /* pad character count */ + unsigned idle : 1; /* idle mode */ + unsigned nofcs : 1; /* FCS disable */ + unsigned fcspre : 1; /* FCS preset */ + unsigned syncpat : 1; /* send sync pattern */ + unsigned sndpad : 1; /* send pad characters before flag enable */ +} cx_cor3_hdlc_t; + +typedef struct { /* hdlc channel options */ + cx_cor1_hdlc_t cor1; /* hdlc channel option register 1 */ + cx_cor2_hdlc_t cor2; /* hdlc channel option register 2 */ + cx_cor3_hdlc_t cor3; /* hdlc channel option register 3 */ + unsigned char rfar1; /* receive frame address register 1 */ + unsigned char rfar2; /* receive frame address register 2 */ + unsigned char rfar3; /* receive frame address register 3 */ + unsigned char rfar4; /* receive frame address register 4 */ + unsigned char cpsr; /* CRC polynomial select */ +} cx_opt_hdlc_t; + +/* + * BISYNC channel mode ------------------------------------------------------- + */ + +/* Longitudinal redundancy check */ +#define BCC_CRC16 0 /* CRC16 is used for BCC */ +#define BCC_LRC 1 /* LRC is used for BCC */ + +/* Send pad pattern */ +#define PAD_AA 0 /* send AAh as pad character */ +#define PAD_55 1 /* send 55h as pad character */ + +typedef struct { /* channel option register 1 */ + unsigned charlen : 4; /* character length, 5..8 */ + unsigned ignpar : 1; /* ignore parity */ + unsigned parmode : 2; /* parity mode */ + unsigned parity : 1; /* parity */ +} cx_cor1_bisync_t; + +typedef struct { /* channel option register 2 */ + unsigned syns : 4; /* number of extra SYN chars before a frame */ + unsigned crcninv : 1; /* CRC invertion option */ + unsigned ebcdic : 1; /* use EBCDIC as char set (instead of ASCII) */ + unsigned bcc : 1; /* BCC append enable */ + unsigned lrc : 1; /* longitudinal redundancy check */ +} cx_cor2_bisync_t; + +typedef struct { /* channel option register 3 */ + unsigned padcnt : 3; /* pad character count */ + unsigned idle : 1; /* idle mode */ + unsigned nofcs : 1; /* FCS disable */ + unsigned fcspre : 1; /* FCS preset */ + unsigned padpat : 1; /* send pad pattern */ + unsigned sndpad : 1; /* send pad characters before SYN enable */ +} cx_cor3_bisync_t; + +typedef struct { /* channel option register 6 */ + unsigned char specterm; /* special termination character */ +} cx_cor6_bisync_t; + +typedef struct { /* bisync channel options */ + cx_cor1_bisync_t cor1; /* channel option register 1 */ + cx_cor2_bisync_t cor2; /* channel option register 2 */ + cx_cor3_bisync_t cor3; /* channel option register 3 */ + cx_cor6_bisync_t cor6; /* channel option register 6 */ + unsigned char cpsr; /* CRC polynomial select */ +} cx_opt_bisync_t; + +/* + * X.21 channel mode --------------------------------------------------------- + */ + +/* The number of SYN chars on receive */ +#define X21SYN_2 0 /* two SYN characters are required */ +#define X21SYN_1 1 /* one SYN character is required */ + +typedef struct { /* channel option register 1 */ + unsigned charlen : 4; /* character length, 5..8 */ + unsigned ignpar : 1; /* ignore parity */ + unsigned parmode : 2; /* parity mode */ + unsigned parity : 1; /* parity */ +} cx_cor1_x21_t; + +typedef struct { /* channel option register 2 */ + unsigned zero1 : 5; + unsigned etc : 1; /* embedded transmitter command enable */ + unsigned zero2 : 2; +} cx_cor2_x21_t; + +typedef struct { /* channel option register 3 */ + unsigned zero : 4; + unsigned scde : 1; /* special character detect enable */ + unsigned stripsyn : 1; /* treat SYN chars as special condition */ + unsigned ssde : 1; /* steady state detect enable */ + unsigned syn : 1; /* the number of SYN chars on receive */ +} cx_cor3_x21_t; + +typedef struct { /* channel option register 6 */ + unsigned char synchar; /* syn character */ +} cx_cor6_x21_t; + +typedef struct { /* x21 channel options */ + cx_cor1_x21_t cor1; /* channel option register 1 */ + cx_cor2_x21_t cor2; /* channel option register 2 */ + cx_cor3_x21_t cor3; /* channel option register 3 */ + cx_cor6_x21_t cor6; /* channel option register 6 */ + unsigned char schr1; /* special character register 1 */ + unsigned char schr2; /* special character register 2 */ + unsigned char schr3; /* special character register 3 */ +} cx_opt_x21_t; + +/* + * CD2400 channel state structure -------------------------------------------- + */ + +/* Signal encoding */ +#define ENCOD_NRZ 0 /* NRZ mode */ +#define ENCOD_NRZI 1 /* NRZI mode */ +#define ENCOD_MANCHESTER 2 /* Manchester mode */ + +/* Clock source */ +#define CLK_0 0 /* clock 0 */ +#define CLK_1 1 /* clock 1 */ +#define CLK_2 2 /* clock 2 */ +#define CLK_3 3 /* clock 3 */ +#define CLK_4 4 /* clock 4 */ +#define CLK_EXT 6 /* external clock */ +#define CLK_RCV 7 /* receive clock */ + +/* Channel type */ +#define T_NONE 0 /* no channel */ +#define T_ASYNC 1 /* pure asynchronous RS-232 channel */ +#define T_SYNC_RS232 2 /* pure synchronous RS-232 channel */ +#define T_SYNC_V35 3 /* pure synchronous V.35 channel */ +#define T_SYNC_RS449 4 /* pure synchronous RS-449 channel */ +#define T_UNIV_RS232 5 /* sync/async RS-232 channel */ +#define T_UNIV_RS449 6 /* sync/async RS-232/RS-449 channel */ +#define T_UNIV_V35 7 /* sync/async RS-232/V.35 channel */ + +typedef enum { /* channel mode */ + M_ASYNC, /* asynchronous mode */ + M_HDLC, /* HDLC mode */ + M_BISYNC, /* BISYNC mode */ + M_X21, /* X.21 mode */ +} cx_chan_mode_t; + +typedef struct { /* channel option register 4 */ + unsigned thr : 4; /* FIFO threshold */ + unsigned zero : 1; + unsigned cts_zd : 1; /* detect 1 to 0 transition on the CTS */ + unsigned cd_zd : 1; /* detect 1 to 0 transition on the CD */ + unsigned dsr_zd : 1; /* detect 1 to 0 transition on the DSR */ +} cx_cor4_t; + +typedef struct { /* channel option register 5 */ + unsigned rx_thr : 4; /* receive flow control FIFO threshold */ + unsigned zero : 1; + unsigned cts_od : 1; /* detect 0 to 1 transition on the CTS */ + unsigned cd_od : 1; /* detect 0 to 1 transition on the CD */ + unsigned dsr_od : 1; /* detect 0 to 1 transition on the DSR */ +} cx_cor5_t; + +typedef struct { /* receive clock option register */ + unsigned clk : 3; /* receive clock source */ + unsigned encod : 2; /* signal encoding NRZ/NRZI/Manchester */ + unsigned dpll : 1; /* DPLL enable */ + unsigned zero : 1; + unsigned tlval : 1; /* transmit line value */ +} cx_rcor_t; + +typedef struct { /* transmit clock option register */ + unsigned zero1 : 1; + unsigned llm : 1; /* local loopback mode */ + unsigned zero2 : 1; + unsigned ext1x : 1; /* external 1x clock mode */ + unsigned zero3 : 1; + unsigned clk : 3; /* transmit clock source */ +} cx_tcor_t; + +typedef struct { + cx_cor4_t cor4; /* channel option register 4 */ + cx_cor5_t cor5; /* channel option register 5 */ + cx_rcor_t rcor; /* receive clock option register */ + cx_tcor_t tcor; /* transmit clock option register */ +} cx_chan_opt_t; + +typedef enum { /* line break mode */ + BRK_IDLE, /* normal line mode */ + BRK_SEND, /* start sending break */ + BRK_STOP, /* stop sending break */ +} cx_break_t; + +typedef struct { + unsigned cisco : 1; /* cisco mode */ + unsigned keepalive : 1; /* keepalive enable */ + unsigned ext : 1; /* use external ppp implementation */ + unsigned lock : 1; /* channel locked for use by driver */ + unsigned norts : 1; /* disable automatic RTS control */ +} cx_soft_opt_t; + +#define NCHIP 4 /* the number of controllers per board */ +#define NCHAN 16 /* the number of channels on the board */ + +typedef struct { + unsigned char board; /* adapter number, 0..2 */ + unsigned char channel; /* channel number, 0..15 */ + unsigned char type; /* channel type (read only) */ + unsigned char iftype; /* chan0 interface RS-232/RS-449/V.35 */ + unsigned long rxbaud; /* receiver speed */ + unsigned long txbaud; /* transmitter speed */ + cx_chan_mode_t mode; /* channel mode */ + cx_chan_opt_t opt; /* common channel options */ + cx_opt_async_t aopt; /* async mode options */ + cx_opt_hdlc_t hopt; /* hdlc mode options */ + cx_opt_bisync_t bopt; /* bisync mode options */ + cx_opt_x21_t xopt; /* x.21 mode options */ + cx_soft_opt_t sopt; /* software options and state flags */ +} cx_options_t; /* user settable options */ + +typedef struct _chan_t { + unsigned char type; /* channel type */ + unsigned char num; /* channel number, 0..15 */ + struct _board_t *board; /* board pointer */ + struct _chip_t *chip; /* controller pointer */ + unsigned long rxbaud; /* receiver speed */ + unsigned long txbaud; /* transmitter speed */ + cx_chan_mode_t mode; /* channel mode */ + cx_chan_opt_t opt; /* common channel options */ + cx_opt_async_t aopt; /* async mode options */ + cx_opt_hdlc_t hopt; /* hdlc mode options */ + cx_opt_bisync_t bopt; /* bisync mode options */ + cx_opt_x21_t xopt; /* x.21 mode options */ + unsigned char *arbuf; /* receiver A dma buffer */ + unsigned char *brbuf; /* receiver B dma buffer */ + unsigned char *atbuf; /* transmitter A dma buffer */ + unsigned char *btbuf; /* transmitter B dma buffer */ + unsigned long arphys; /* receiver A phys address */ + unsigned long brphys; /* receiver B phys address */ + unsigned long atphys; /* transmitter A phys address */ + unsigned long btphys; /* transmitter B phys address */ + unsigned char dtr; /* DTR signal value */ + unsigned char rts; /* RTS signal value */ +#ifdef KERNEL + struct tty *ttyp; /* tty structure pointer */ + struct ifnet *ifp; /* network interface data */ + caddr_t bpf; /* packet filter data */ + cx_soft_opt_t sopt; /* software options and state flags */ + cx_break_t brk; /* line break mode */ +#ifdef __bsdi__ + struct ttydevice_tmp *ttydev; /* tty statistics structure */ +#endif +#endif +} cx_chan_t; + +typedef struct _chip_t { + unsigned short port; /* base port address, or 0 if no chip */ + unsigned char num; /* controller number, 0..3 */ + struct _board_t *board; /* board pointer */ + unsigned long oscfreq; /* oscillator frequency in Hz */ +} cx_chip_t; + +typedef struct _board_t { + unsigned short port; /* base board port, 0..3f0 */ + unsigned short num; /* board number, 0..2 */ + unsigned char irq; /* intterupt request {3 5 7 10 11 12 15} */ + unsigned char dma; /* DMA request {5 6 7} */ + unsigned char if0type; /* chan0 interface RS-232/RS-449/V.35 */ + unsigned char if8type; /* chan8 interface RS-232/RS-449/V.35 */ + unsigned short bcr0; /* BCR0 image */ + unsigned short bcr0b; /* BCR0b image */ + unsigned short bcr1; /* BCR1 image */ + unsigned short bcr1b; /* BCR1b image */ + cx_chip_t chip[NCHIP]; /* controller structures */ + cx_chan_t chan[NCHAN]; /* channel structures */ + char name[16]; /* board version name */ + unsigned char nuniv; /* number of universal channels */ + unsigned char nsync; /* number of sync. channels */ + unsigned char nasync; /* number of async. channels */ +} cx_board_t; + +#define CX_SPEED_DFLT 9600 + +extern long cx_rxbaud, cx_txbaud; +extern int cx_univ_mode, cx_sync_mode, cx_iftype; + +extern cx_chan_opt_t chan_opt_dflt; /* default mode-independent options */ +extern cx_opt_async_t opt_async_dflt; /* default async options */ +extern cx_opt_hdlc_t opt_hdlc_dflt; /* default hdlc options */ +extern cx_opt_bisync_t opt_bisync_dflt; /* default bisync options */ +extern cx_opt_x21_t opt_x21_dflt; /* default x21 options */ + +int cx_probe_board (int port); +void cx_init (cx_board_t *b, int num, int port, int irq, int dma); +void cx_init_board (cx_board_t *b, int num, int port, int irq, int dma, + int chain, int rev, int osc, int rev2, int osc2); +void cx_setup_board (cx_board_t *b); +void cx_setup_chan (cx_chan_t *c); +void cx_chan_dtr (cx_chan_t *c, int on); +void cx_chan_rts (cx_chan_t *c, int on); +void cx_cmd (int base, int cmd); +void cx_disable_dma (cx_board_t *b); +void cx_reinit_board (cx_board_t *b); +int cx_chan_dsr (cx_chan_t *c); +int cx_chan_cd (cx_chan_t *c); +void cx_clock (long hz, long ba, int *clk, int *div); + +#define CXIOCGETMODE _IOWR('x', 1, cx_options_t) /* get channel options */ +#define CXIOCSETMODE _IOW('x', 2, cx_options_t) /* set channel options */ diff --git a/sys/i386/isa/cronyx.c b/sys/i386/isa/cronyx.c new file mode 100644 index 0000000..4cee6b8 --- /dev/null +++ b/sys/i386/isa/cronyx.c @@ -0,0 +1,1041 @@ +/* + * Low-level subroutines for Cronyx-Sigma adapter. + * + * Copyright (C) 1994 Cronyx Ltd. + * Author: Serge Vakulenko, <vak@zebub.msk.su> + * + * This software is distributed with NO WARRANTIES, not even the implied + * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Authors grant any other persons or organisations permission to use + * or modify this software as long as this message is kept with the software, + * all derivative works or modified versions. + * + * Version 1.2, Mon Nov 28 16:12:18 MSK 1994 + */ +#include <string.h> +#if defined (MSDOS) || defined (__MSDOS__) +# include <dos.h> +# define inb(port) inportb(port) +# define inw(port) inport(port) +# define outb(port,b) outportb(port,b) +# define outw(port,w) outport(port,w) +# define vtophys(a) (((unsigned long)(a)>>12 & 0xffff0) +\ + ((unsigned)(a) & 0xffff)) +#else +# include <sys/param.h> +# include <sys/socket.h> +# include <net/if.h> +# include <vm/vm.h> +# ifdef __FreeBSD__ +# if __FreeBSD__ < 2 +# include <i386/include/pio.h> +# else +# include <i386/include/cpufunc.h> +# endif +# else +# include <i386/include/inline.h> +# endif +#endif + +#include "cronyx.h" +#include "cxreg.h" + +#define DMA_MASK 0xd4 /* DMA mask register */ +#define DMA_MASK_CLEAR 0x04 /* DMA clear mask */ +#define DMA_MODE 0xd6 /* DMA mode register */ +#define DMA_MODE_MASTER 0xc0 /* DMA master mode */ + +#define BYTE *(unsigned char*)& + +static unsigned char irqmask [] = { + BCR0_IRQ_DIS, BCR0_IRQ_DIS, BCR0_IRQ_DIS, BCR0_IRQ_3, + BCR0_IRQ_DIS, BCR0_IRQ_5, BCR0_IRQ_DIS, BCR0_IRQ_7, + BCR0_IRQ_DIS, BCR0_IRQ_DIS, BCR0_IRQ_10, BCR0_IRQ_11, + BCR0_IRQ_12, BCR0_IRQ_DIS, BCR0_IRQ_DIS, BCR0_IRQ_15, +}; + +static unsigned char dmamask [] = { + BCR0_DMA_DIS, BCR0_DMA_DIS, BCR0_DMA_DIS, BCR0_DMA_DIS, + BCR0_DMA_DIS, BCR0_DMA_5, BCR0_DMA_6, BCR0_DMA_7, +}; + +long cx_rxbaud = CX_SPEED_DFLT; /* receiver baud rate */ +long cx_txbaud = CX_SPEED_DFLT; /* transmitter baud rate */ + +int cx_univ_mode = M_ASYNC; /* univ. chan. mode: async or sync */ +int cx_sync_mode = M_HDLC; /* sync. chan. mode: HDLC, Bisync or X.21 */ +int cx_iftype = 0; /* univ. chan. interface: upper/lower */ + +static int cx_probe_chip (int base); +static void cx_setup_chip (cx_chip_t *c); + +/* + * Wait for CCR to clear. + */ +void cx_cmd (int base, int cmd) +{ + unsigned short port = CCR(base); + unsigned short count; + + /* Wait 10 msec for the previous command to complete. */ + for (count=0; inb(port) && count<20000; ++count) + continue; + + /* Issue the command. */ + outb (port, cmd); + + /* Wait 10 msec for the command to complete. */ + for (count=0; inb(port) && count<20000; ++count) + continue; +} + +/* + * Reset the chip. + */ +static int cx_reset (unsigned short port) +{ + int count; + + /* Wait up to 10 msec for revision code to appear after reset. */ + for (count=0; count<20000; ++count) + if (inb(GFRCR(port)) != 0) + break; + + cx_cmd (port, CCR_RSTALL); + + /* Firmware revision code should clear imediately. */ + /* Wait up to 10 msec for revision code to appear again. */ + for (count=0; count<20000; ++count) + if (inb(GFRCR(port)) != 0) + return (1); + + /* Reset failed. */ + return (0); +} + +/* + * Check if the CD2400 board is present at the given base port. + */ +static int cx_probe_chained_board (int port, int *c0, int *c1) +{ + int rev, i; + + /* Read and check the board revision code. */ + rev = inb (BSR(port)); + *c0 = *c1 = 0; + switch (rev & BSR_VAR_MASK) { + case CRONYX_100: *c0 = 1; break; + case CRONYX_400: *c1 = 1; break; + case CRONYX_500: *c0 = *c1 = 1; break; + case CRONYX_410: *c0 = 1; break; + case CRONYX_810: *c0 = *c1 = 1; break; + case CRONYX_410s: *c0 = 1; break; + case CRONYX_810s: *c0 = *c1 = 1; break; + case CRONYX_440: *c0 = 1; break; + case CRONYX_840: *c0 = *c1 = 1; break; + case CRONYX_401: *c0 = 1; break; + case CRONYX_801: *c0 = *c1 = 1; break; + case CRONYX_401s: *c0 = 1; break; + case CRONYX_801s: *c0 = *c1 = 1; break; + case CRONYX_404: *c0 = 1; break; + case CRONYX_703: *c0 = *c1 = 1; break; + default: return (0); /* invalid variant code */ + } + + switch (rev & BSR_OSC_MASK) { + case BSR_OSC_20: /* 20 MHz */ + case BSR_OSC_18432: /* 18.432 MHz */ + break; + default: + return (0); /* oscillator frequency does not match */ + } + + for (i=2; i<0x10; i+=2) + if ((inb (BSR(port)+i) & BSR_REV_MASK) != (rev & BSR_REV_MASK)) + return (0); /* status changed? */ + return (1); +} + +/* + * Check if the CD2400 board is present at the given base port. + */ +int cx_probe_board (int port) +{ + int c0, c1, c2=0, c3=0, result; + + if (! cx_probe_chained_board (port, &c0, &c1)) + return (0); /* no board detected */ + + if (! (inb (BSR(port)) & BSR_NOCHAIN)) { /* chained board attached */ + if (! cx_probe_chained_board (port + 0x10, &c2, &c3)) + return (0); /* invalid chained board? */ + + if (! (inb (BSR(port+0x10)) & BSR_NOCHAIN)) + return (0); /* invalid chained board flag? */ + } + + /* Turn off the reset bit. */ + outb (BCR0(port), BCR0_NORESET); + if (c2 || c3) + outb (BCR0(port + 0x10), BCR0_NORESET); + + result = 1; + if (c0 && ! cx_probe_chip (CS0(port))) + result = 0; /* no CD2400 chip here */ + else if (c1 && ! cx_probe_chip (CS1(port))) + result = 0; /* no second CD2400 chip */ + else if (c2 && ! cx_probe_chip (CS0(port + 0x10))) + result = 0; /* no CD2400 chip on the slave board */ + else if (c3 && ! cx_probe_chip (CS1(port + 0x10))) + result = 0; /* no second CD2400 chip on the slave board */ + + /* Reset the controller. */ + outb (BCR0(port), 0); + if (c2 || c3) + outb (BCR0(port + 0x10), 0); + + /* Yes, we really have valid CD2400 board. */ + return (result); +} + +/* + * Check if the CD2400 chip is present at the given base port. + */ +static int cx_probe_chip (int base) +{ + int rev, newrev, count; + + /* Wait up to 10 msec for revision code to appear after reset. */ + for (count=0; inb(GFRCR(base))==0; ++count) + if (count >= 20000) + return (0); /* reset failed */ + + /* Read and check the global firmware revision code. */ + rev = inb (GFRCR(base)); + if (rev<REVCL_MIN || rev>REVCL_MAX) + return (0); /* CD2400 revision does not match */ + + /* Reset the chip. */ + if (! cx_reset (base)) + return (0); + + /* Read and check the new global firmware revision code. */ + newrev = inb (GFRCR(base)); + if (newrev != rev) + return (0); /* revision changed */ + + /* Yes, we really have CD2400 chip here. */ + return (1); +} + +/* + * Probe and initialize the board structure. + */ +void cx_init (cx_board_t *b, int num, int port, int irq, int dma) +{ + int rev, chain, rev2; + + rev = inb (BSR(port)); + chain = !(rev & BSR_NOCHAIN); + rev2 = chain ? inb (BSR(port+0x10)) : 0; + cx_init_board (b, num, port, irq, dma, chain, + (rev & BSR_VAR_MASK), (rev & BSR_OSC_MASK), + (rev2 & BSR_VAR_MASK), (rev2 & BSR_OSC_MASK)); +} + +/* + * Initialize the board structure, given the type of the board. + */ +void cx_init_board (cx_board_t *b, int num, int port, int irq, int dma, + int chain, int rev, int osc, int rev2, int osc2) +{ + cx_chan_t *c; + int i, c0, c1; + + /* Initialize board structure. */ + b->port = port; + b->num = num; + b->irq = irq; + b->dma = dma; + b->if0type = b->if8type = cx_iftype; + + /* Set channels 0 and 8 mode, set DMA and IRQ. */ + b->bcr0 = b->bcr0b = BCR0_NORESET | dmamask[b->dma] | irqmask[b->irq]; + + /* Clear DTR[0..3] and DTR[8..12]. */ + b->bcr1 = b->bcr1b = 0; + + /* Initialize chip structures. */ + for (i=0; i<NCHIP; ++i) { + b->chip[i].num = i; + b->chip[i].board = b; + } + b->chip[0].port = CS0(port); + b->chip[1].port = CS1(port); + b->chip[2].port = CS0(port+0x10); + b->chip[3].port = CS1(port+0x10); + + /*------------------ Master board -------------------*/ + + /* Read and check the board revision code. */ + c0 = c1 = 0; + b->name[0] = 0; + switch (rev) { + case CRONYX_100: strcpy (b->name, "100"); c0 = 1; break; + case CRONYX_400: strcpy (b->name, "400"); c1 = 1; break; + case CRONYX_500: strcpy (b->name, "500"); c0 = c1 = 1; break; + case CRONYX_410: strcpy (b->name, "410"); c0 = 1; break; + case CRONYX_810: strcpy (b->name, "810"); c0 = c1 = 1; break; + case CRONYX_410s: strcpy (b->name, "410s"); c0 = 1; break; + case CRONYX_810s: strcpy (b->name, "810s"); c0 = c1 = 1; break; + case CRONYX_440: strcpy (b->name, "440"); c0 = 1; break; + case CRONYX_840: strcpy (b->name, "840"); c0 = c1 = 1; break; + case CRONYX_401: strcpy (b->name, "401"); c0 = 1; break; + case CRONYX_801: strcpy (b->name, "801"); c0 = c1 = 1; break; + case CRONYX_401s: strcpy (b->name, "401s"); c0 = 1; break; + case CRONYX_801s: strcpy (b->name, "801s"); c0 = c1 = 1; break; + case CRONYX_404: strcpy (b->name, "404"); c0 = 1; break; + case CRONYX_703: strcpy (b->name, "703"); c0 = c1 = 1; break; + } + + switch (osc) { + default: + case BSR_OSC_20: /* 20 MHz */ + b->chip[0].oscfreq = b->chip[1].oscfreq = 20000000L; + strcat (b->name, "a"); + break; + case BSR_OSC_18432: /* 18.432 MHz */ + b->chip[0].oscfreq = b->chip[1].oscfreq = 18432000L; + strcat (b->name, "b"); + break; + } + + if (! c0) + b->chip[0].port = 0; + if (! c1) + b->chip[1].port = 0; + + /*------------------ Slave board -------------------*/ + + if (! chain) { + b->chip[2].oscfreq = b->chip[3].oscfreq = 0L; + b->chip[2].port = b->chip[3].port = 0; + } else { + /* Read and check the board revision code. */ + c0 = c1 = 0; + strcat (b->name, "/"); + switch (rev2) { + case CRONYX_100: strcat(b->name,"100"); c0=1; break; + case CRONYX_400: strcat(b->name,"400"); c1=1; break; + case CRONYX_500: strcat(b->name,"500"); c0=c1=1; break; + case CRONYX_410: strcat(b->name,"410"); c0=1; break; + case CRONYX_810: strcat(b->name,"810"); c0=c1=1; break; + case CRONYX_410s: strcat(b->name,"410s"); c0=1; break; + case CRONYX_810s: strcat(b->name,"810s"); c0=c1=1; break; + case CRONYX_440: strcat(b->name,"440"); c0=1; break; + case CRONYX_840: strcat(b->name,"840"); c0=c1=1; break; + case CRONYX_401: strcat(b->name,"401"); c0=1; break; + case CRONYX_801: strcat(b->name,"801"); c0=c1=1; break; + case CRONYX_401s: strcat(b->name,"401s"); c0=1; break; + case CRONYX_801s: strcat(b->name,"801s"); c0=c1=1; break; + case CRONYX_404: strcat(b->name,"404"); c0=1; break; + case CRONYX_703: strcat(b->name,"703"); c0=c1=1; break; + } + + switch (osc2) { + default: + case BSR_OSC_20: /* 20 MHz */ + b->chip[2].oscfreq = b->chip[3].oscfreq = 20000000L; + strcat (b->name, "a"); + break; + case BSR_OSC_18432: /* 18.432 MHz */ + b->chip[2].oscfreq = b->chip[3].oscfreq = 18432000L; + strcat (b->name, "b"); + break; + } + + if (! c0) + b->chip[2].port = 0; + if (! c1) + b->chip[3].port = 0; + } + + /* Initialize channel structures. */ + for (i=0; i<NCHAN; ++i) { + cx_chan_t *c = b->chan + i; + + c->num = i; + c->board = b; + c->chip = b->chip + i*NCHIP/NCHAN; + c->type = T_NONE; + } + + /*------------------ Master board -------------------*/ + + switch (rev) { + case CRONYX_400: + break; + case CRONYX_100: + case CRONYX_500: + b->chan[0].type = T_UNIV_RS232; + break; + case CRONYX_410: + case CRONYX_810: + b->chan[0].type = T_UNIV_V35; + for (i=1; i<4; ++i) + b->chan[i].type = T_UNIV_RS232; + break; + case CRONYX_410s: + case CRONYX_810s: + b->chan[0].type = T_UNIV_V35; + for (i=1; i<4; ++i) + b->chan[i].type = T_SYNC_RS232; + break; + case CRONYX_440: + case CRONYX_840: + b->chan[0].type = T_UNIV_V35; + for (i=1; i<4; ++i) + b->chan[i].type = T_SYNC_V35; + break; + case CRONYX_401: + case CRONYX_801: + b->chan[0].type = T_UNIV_RS449; + for (i=1; i<4; ++i) + b->chan[i].type = T_UNIV_RS232; + break; + case CRONYX_401s: + case CRONYX_801s: + b->chan[0].type = T_UNIV_RS449; + for (i=1; i<4; ++i) + b->chan[i].type = T_SYNC_RS232; + break; + case CRONYX_404: + b->chan[0].type = T_UNIV_RS449; + for (i=1; i<4; ++i) + b->chan[i].type = T_SYNC_RS449; + break; + case CRONYX_703: + b->chan[0].type = T_UNIV_RS449; + for (i=1; i<3; ++i) + b->chan[i].type = T_SYNC_RS449; + break; + } + + /* If the second controller is present, + * then we have 4..7 channels in async. mode */ + if (b->chip[1].port) + for (i=4; i<8; ++i) + b->chan[i].type = T_UNIV_RS232; + + /*------------------ Slave board -------------------*/ + + if (chain) { + switch (rev2) { + case CRONYX_400: + break; + case CRONYX_100: + case CRONYX_500: + b->chan[8].type = T_UNIV_RS232; + break; + case CRONYX_410: + case CRONYX_810: + b->chan[8].type = T_UNIV_V35; + for (i=9; i<12; ++i) + b->chan[i].type = T_UNIV_RS232; + break; + case CRONYX_410s: + case CRONYX_810s: + b->chan[8].type = T_UNIV_V35; + for (i=9; i<12; ++i) + b->chan[i].type = T_SYNC_RS232; + break; + case CRONYX_440: + case CRONYX_840: + b->chan[8].type = T_UNIV_V35; + for (i=9; i<12; ++i) + b->chan[i].type = T_SYNC_V35; + break; + case CRONYX_401: + case CRONYX_801: + b->chan[8].type = T_UNIV_RS449; + for (i=9; i<12; ++i) + b->chan[i].type = T_UNIV_RS232; + break; + case CRONYX_401s: + case CRONYX_801s: + b->chan[8].type = T_UNIV_RS449; + for (i=9; i<12; ++i) + b->chan[i].type = T_UNIV_RS232; + break; + case CRONYX_404: + b->chan[8].type = T_UNIV_RS449; + for (i=9; i<12; ++i) + b->chan[i].type = T_SYNC_RS449; + break; + case CRONYX_703: + b->chan[8].type = T_UNIV_RS449; + for (i=9; i<11; ++i) + b->chan[i].type = T_SYNC_RS449; + break; + } + + /* If the second controller is present, + * then we have 4..7 channels in async. mode */ + if (b->chip[3].port) + for (i=12; i<16; ++i) + b->chan[i].type = T_UNIV_RS232; + } + + b->nuniv = b->nsync = b->nasync = 0; + for (c=b->chan; c<b->chan+NCHAN; ++c) + switch (c->type) { + case T_ASYNC: ++b->nasync; break; + case T_UNIV_RS232: + case T_UNIV_RS449: + case T_UNIV_V35: ++b->nuniv; break; + case T_SYNC_RS232: + case T_SYNC_V35: + case T_SYNC_RS449: ++b->nsync; break; + } + + cx_reinit_board (b); +} + +/* + * Reinitialize all channels, using new options and baud rate. + */ +void cx_reinit_board (cx_board_t *b) +{ + cx_chan_t *c; + + b->if0type = b->if8type = cx_iftype; + for (c=b->chan; c<b->chan+NCHAN; ++c) { + switch (c->type) { + default: + case T_NONE: + continue; + case T_UNIV_RS232: + case T_UNIV_RS449: + case T_UNIV_V35: + c->mode = (cx_univ_mode == M_ASYNC) ? + M_ASYNC : cx_sync_mode; + break; + case T_SYNC_RS232: + case T_SYNC_V35: + case T_SYNC_RS449: + c->mode = cx_sync_mode; + break; + case T_ASYNC: + c->mode = M_ASYNC; + break; + } + c->rxbaud = cx_rxbaud; + c->txbaud = cx_txbaud; + c->opt = chan_opt_dflt; + c->aopt = opt_async_dflt; + c->hopt = opt_hdlc_dflt; + c->bopt = opt_bisync_dflt; + c->xopt = opt_x21_dflt; + } +} + +/* + * Set up the board. + */ +void cx_setup_board (cx_board_t *b) +{ + int i; + + /* Disable DMA channel. */ + outb (DMA_MASK, (b->dma & 3) | DMA_MASK_CLEAR); + + /* Reset the controller. */ + outb (BCR0(b->port), 0); + if (b->chip[2].port || b->chip[3].port) + outb (BCR0(b->port+0x10), 0); + + /* + * Set channels 0 and 8 to RS232 async. mode. + * Enable DMA and IRQ. + */ + outb (BCR0(b->port), b->bcr0); + if (b->chip[2].port || b->chip[3].port) + outb (BCR0(b->port+0x10), b->bcr0b); + + /* Clear DTR[0..3] and DTR[8..12]. */ + outw (BCR1(b->port), b->bcr1); + if (b->chip[2].port || b->chip[3].port) + outw (BCR1(b->port+0x10), b->bcr1b); + + /* Initialize all controllers. */ + for (i=0; i<NCHIP; ++i) + if (b->chip[i].port) + cx_setup_chip (b->chip + i); + + /* Set up DMA channel to master mode. */ + outb (DMA_MODE, (b->dma & 3) | DMA_MODE_MASTER); + + /* Enable DMA channel. */ + outb (DMA_MASK, b->dma & 3); + + /* Initialize all channels. */ + for (i=0; i<NCHAN; ++i) + if (b->chan[i].type != T_NONE) + cx_setup_chan (b->chan + i); +} + +/* + * Initialize the board. + */ +static void cx_setup_chip (cx_chip_t *c) +{ + /* Reset the chip. */ + cx_reset (c->port); + + /* + * Set all interrupt level registers to the same value. + * This enables the internal CD2400 priority scheme. + */ + outb (RPILR(c->port), BRD_INTR_LEVEL); + outb (TPILR(c->port), BRD_INTR_LEVEL); + outb (MPILR(c->port), BRD_INTR_LEVEL); + + /* Set bus error count to zero. */ + outb (BERCNT(c->port), 0); + + /* Set 16-bit DMA mode. */ + outb (DMR(c->port), 0); + + /* Set timer period register to 1 msec (approximately). */ + outb (TPR(c->port), 10); +} + +/* + * Initialize the CD2400 channel. + */ +void cx_setup_chan (cx_chan_t *c) +{ + unsigned short port = c->chip->port; + int clock, period; + + if (c->num == 0) { + c->board->bcr0 &= ~BCR0_UMASK; + if (c->mode != M_ASYNC) + c->board->bcr0 |= BCR0_UM_SYNC; + if (c->board->if0type && + (c->type==T_UNIV_RS449 || c->type==T_UNIV_V35)) + c->board->bcr0 |= BCR0_UI_RS449; + outb (BCR0(c->board->port), c->board->bcr0); + } else if (c->num == 8) { + c->board->bcr0b &= ~BCR0_UMASK; + if (c->mode != M_ASYNC) + c->board->bcr0b |= BCR0_UM_SYNC; + if (c->board->if8type && + (c->type==T_UNIV_RS449 || c->type==T_UNIV_V35)) + c->board->bcr0b |= BCR0_UI_RS449; + outb (BCR0(c->board->port+0x10), c->board->bcr0b); + } + + /* set current channel number */ + outb (CAR(port), c->num & 3); + + /* reset the channel */ + cx_cmd (port, CCR_CLRCH); + + /* set LIVR to contain the board and channel numbers */ + outb (LIVR(port), c->board->num << 6 | c->num << 2); + + /* clear DTR, RTS, set TXCout/DTR pin */ + outb (MSVR_RTS(port), 0); + outb (MSVR_DTR(port), c->mode==M_ASYNC ? 0 : MSV_TXCOUT); + + switch (c->mode) { /* initialize the channel mode */ + case M_ASYNC: + /* set receiver timeout register */ + outw (RTPR(port), 10); /* 10 msec, see TPR */ + + outb (CMR(port), CMR_RXDMA | CMR_TXDMA | CMR_ASYNC); + outb (COR1(port), BYTE c->aopt.cor1); + outb (COR2(port), BYTE c->aopt.cor2); + outb (COR3(port), BYTE c->aopt.cor3); + outb (COR6(port), BYTE c->aopt.cor6); + outb (COR7(port), BYTE c->aopt.cor7); + outb (SCHR1(port), c->aopt.schr1); + outb (SCHR2(port), c->aopt.schr2); + outb (SCHR3(port), c->aopt.schr3); + outb (SCHR4(port), c->aopt.schr4); + outb (SCRL(port), c->aopt.scrl); + outb (SCRH(port), c->aopt.scrh); + outb (LNXT(port), c->aopt.lnxt); + break; + case M_HDLC: + outb (CMR(port), CMR_RXDMA | CMR_TXDMA | CMR_HDLC); + outb (COR1(port), BYTE c->hopt.cor1); + outb (COR2(port), BYTE c->hopt.cor2); + outb (COR3(port), BYTE c->hopt.cor3); + outb (RFAR1(port), c->hopt.rfar1); + outb (RFAR2(port), c->hopt.rfar2); + outb (RFAR3(port), c->hopt.rfar3); + outb (RFAR4(port), c->hopt.rfar4); + outb (CPSR(port), c->hopt.cpsr); + break; + case M_BISYNC: + outb (CMR(port), CMR_RXDMA | CMR_TXDMA | CMR_BISYNC); + outb (COR1(port), BYTE c->bopt.cor1); + outb (COR2(port), BYTE c->bopt.cor2); + outb (COR3(port), BYTE c->bopt.cor3); + outb (COR6(port), BYTE c->bopt.cor6); + outb (CPSR(port), c->bopt.cpsr); + break; + case M_X21: + outb (CMR(port), CMR_RXDMA | CMR_TXDMA | CMR_X21); + outb (COR1(port), BYTE c->xopt.cor1); + outb (COR2(port), BYTE c->xopt.cor2); + outb (COR3(port), BYTE c->xopt.cor3); + outb (COR6(port), BYTE c->xopt.cor6); + outb (SCHR1(port), c->xopt.schr1); + outb (SCHR2(port), c->xopt.schr2); + outb (SCHR3(port), c->xopt.schr3); + break; + } + + /* set mode-independent options */ + outb (COR4(port), BYTE c->opt.cor4); + outb (COR5(port), BYTE c->opt.cor5); + + /* set up receiver clock values */ + if (c->mode == M_ASYNC || c->opt.rcor.dpll) { + cx_clock (c->chip->oscfreq, c->rxbaud, &clock, &period); + c->opt.rcor.clk = clock; + } else { + c->opt.rcor.clk = CLK_EXT; + period = 1; + } + outb (RCOR(port), BYTE c->opt.rcor); + outb (RBPR(port), period); + + /* set up transmitter clock values */ + if (c->mode == M_ASYNC || !c->opt.tcor.ext1x) { + unsigned ext1x = c->opt.tcor.ext1x; + c->opt.tcor.ext1x = 0; + cx_clock (c->chip->oscfreq, c->txbaud, &clock, &period); + c->opt.tcor.clk = clock; + c->opt.tcor.ext1x = ext1x; + } else { + c->opt.tcor.clk = CLK_EXT; + period = 1; + } + outb (TCOR(port), BYTE c->opt.tcor); + outb (TBPR(port), period); + + /* set receiver A buffer physical address */ + c->arphys = vtophys (c->arbuf); + outw (ARBADRU(port), (unsigned short) (c->arphys>>16)); + outw (ARBADRL(port), (unsigned short) c->arphys); + + /* set receiver B buffer physical address */ + c->brphys = vtophys (c->brbuf); + outw (BRBADRU(port), (unsigned short) (c->brphys>>16)); + outw (BRBADRL(port), (unsigned short) c->brphys); + + /* set transmitter A buffer physical address */ + c->atphys = vtophys (c->atbuf); + outw (ATBADRU(port), (unsigned short) (c->atphys>>16)); + outw (ATBADRL(port), (unsigned short) c->atphys); + + /* set transmitter B buffer physical address */ + c->btphys = vtophys (c->btbuf); + outw (BTBADRU(port), (unsigned short) (c->btphys>>16)); + outw (BTBADRL(port), (unsigned short) c->btphys); + + c->dtr = 0; + c->rts = 0; +} + +/* + * Control DTR signal for the channel. + * Turn it on/off. + */ +void cx_chan_dtr (cx_chan_t *c, int on) +{ + c->dtr = on ? 1 : 0; + + if (c->mode == M_ASYNC) { + outb (CAR(c->chip->port), c->num & 3); + outb (MSVR_DTR(c->chip->port), on ? MSV_DTR : 0); + return; + } + + switch (c->num) { + default: + /* Channels 4..7 and 12..15 in syncronous mode + * have no DTR signal. */ + break; + case 0: case 1: case 2: case 3: + if (on) + c->board->bcr1 |= 0x100 << c->num; + else + c->board->bcr1 &= ~(0x100 << c->num); + outb (CAR(c->chip->port), c->num); + outw (BCR1(c->board->port), c->board->bcr1); + break; + case 8: case 9: case 10: case 11: + if (on) + c->board->bcr1b |= 0x100 << (c->num & 3); + else + c->board->bcr1b &= ~(0x100 << (c->num & 3)); + outb (CAR(c->chip->port), c->num & 3); + outw (BCR1(c->board->port+0x10), c->board->bcr1b); + break; + } +} + +/* + * Control RTS signal for the channel. + * Turn it on/off. + */ +void cx_chan_rts (cx_chan_t *c, int on) +{ + c->rts = on ? 1 : 0; + outb (CAR(c->chip->port), c->num & 3); + outb (MSVR_RTS(c->chip->port), on ? MSV_RTS : 0); +} + +/* + * Get the state of DSR signal of the channel. + */ +int cx_chan_dsr (cx_chan_t *c) +{ + unsigned char sigval; + + if (c->mode == M_ASYNC) { + outb (CAR(c->chip->port), c->num & 3); + return (inb (MSVR(c->chip->port)) & MSV_DSR ? 1 : 0); + } + + /* + * Channels 4..7 and 12..15 don't have DSR signal available. + */ + switch (c->num) { + default: + return (1); + case 0: case 1: case 2: case 3: + sigval = inw (BSR(c->board->port)) >> 8; + break; + case 8: case 9: case 10: case 11: + sigval = inw (BSR(c->board->port+0x10)) >> 8; + break; + } + return (~sigval >> (c->num & 3) & 1); +} + +/* + * Get the state of CARRIER signal of the channel. + */ +int cx_chan_cd (cx_chan_t *c) +{ + unsigned char sigval; + + if (c->mode == M_ASYNC) { + outb (CAR(c->chip->port), c->num & 3); + return (inb (MSVR(c->chip->port)) & MSV_CD ? 1 : 0); + } + + /* + * Channels 4..7 and 12..15 don't have CD signal available. + */ + switch (c->num) { + default: + return (1); + case 0: case 1: case 2: case 3: + sigval = inw (BSR(c->board->port)) >> 8; + break; + case 8: case 9: case 10: case 11: + sigval = inw (BSR(c->board->port+0x10)) >> 8; + break; + } + return (~sigval >> 4 >> (c->num & 3) & 1); +} + +/* + * Compute CD2400 clock values. + */ +void cx_clock (long hz, long ba, int *clk, int *div) +{ + static short clocktab[] = { 8, 32, 128, 512, 2048, 0 }; + + for (*clk=0; clocktab[*clk]; ++*clk) { + long c = ba * clocktab[*clk]; + if (hz <= c*256) { + *div = (2 * hz + c) / (2 * c) - 1; + return; + } + } + /* Incorrect baud rate. Return some meaningful values. */ + *clk = 0; + *div = 255; +} + +void cx_disable_dma (cx_board_t *b) +{ + /* Disable DMA channel. */ + outb (DMA_MASK, (b->dma & 3) | DMA_MASK_CLEAR); +} + +cx_chan_opt_t chan_opt_dflt = { /* mode-independent options */ + { /* cor4 */ + 7, /* FIFO threshold, odd is better */ + 0, + 0, /* don't detect 1 to 0 on CTS */ + 1, /* detect 1 to 0 on CD */ + 0, /* detect 1 to 0 on DSR */ + }, + { /* cor5 */ + 0, /* receive flow control FIFO threshold */ + 0, + 0, /* don't detect 0 to 1 on CTS */ + 1, /* detect 0 to 1 on CD */ + 0, /* detect 0 to 1 on DSR */ + }, + { /* rcor */ + 0, /* dummy clock source */ + ENCOD_NRZ, /* NRZ mode */ + 0, /* disable DPLL */ + 0, + 0, /* transmit line value */ + }, + { /* tcor */ + 0, + 0, /* local loopback mode */ + 0, + 1, /* external 1x clock mode */ + 0, + 0, /* dummy transmit clock source */ + }, +}; + +cx_opt_async_t opt_async_dflt = { /* default async options */ + { /* cor1 */ + 8-1, /* 8-bit char length */ + 0, /* don't ignore parity */ + PARM_NOPAR, /* no parity */ + PAR_EVEN, /* even parity */ + }, + { /* cor2 */ + 0, /* disable automatic DSR */ + 1, /* enable automatic CTS */ + 0, /* disable automatic RTS */ + 0, /* no remote loopback */ + 0, + 0, /* disable embedded cmds */ + 0, /* disable XON/XOFF */ + 0, /* disable XANY */ + }, + { /* cor3 */ + STOPB_1, /* 1 stop bit */ + 0, + 0, /* disable special char detection */ + FLOWCC_PASS, /* pass flow ctl chars to the host */ + 0, /* range detect disable */ + 0, /* disable extended spec. char detect */ + }, + { /* cor6 */ + PERR_INTR, /* generate exception on parity errors */ + BRK_INTR, /* generate exception on break condition */ + 0, /* don't translate NL to CR on input */ + 0, /* don't translate CR to NL on input */ + 0, /* don't discard CR on input */ + }, + { /* cor7 */ + 0, /* don't translate CR to NL on output */ + 0, /* don't translate NL to CR on output */ + 0, + 0, /* don't process flow ctl err chars */ + 0, /* disable LNext option */ + 0, /* don't strip 8 bit on input */ + }, + 0, 0, 0, 0, 0, 0, 0, /* clear schr1-4, scrl, scrh, lnxt */ +}; + +cx_opt_hdlc_t opt_hdlc_dflt = { /* default hdlc options */ + { /* cor1 */ + 2, /* 2 inter-frame flags */ + 0, /* no-address mode */ + CLRDET_DISABLE, /* disable clear detect */ + AFLO_1OCT, /* 1-byte address field length */ + }, + { /* cor2 */ + 0, /* disable automatic DSR */ + 0, /* disable automatic CTS */ + 0, /* disable automatic RTS */ + 0, + CRC_INVERT, /* use CRC V.41 */ + 0, + FCS_NOTPASS, /* don't pass received CRC to the host */ + 0, + }, + { /* cor3 */ + 0, /* 0 pad characters sent */ + IDLE_FLAG, /* idle in flag */ + 0, /* enable FCS */ + FCSP_ONES, /* FCS preset to all ones (V.41) */ + SYNC_AA, /* use AAh as sync char */ + 0, /* disable pad characters */ + }, + 0, 0, 0, 0, /* clear rfar1-4 */ + POLY_V41, /* use V.41 CRC polynomial */ +}; + +cx_opt_bisync_t opt_bisync_dflt = { /* default bisync options */ + { /* cor1 */ + 8-1, /* 8-bit char length */ + 0, /* don't ignore parity */ + PARM_NOPAR, /* no parity */ + PAR_EVEN, /* even parity */ + }, + { /* cor2 */ + 3-2, /* send three SYN chars */ + CRC_DONT_INVERT,/* don't invert CRC (CRC-16) */ + 0, /* use ASCII, not EBCDIC */ + 0, /* disable bcc append */ + BCC_CRC16, /* user CRC16, not LRC */ + }, + { /* cor3 */ + 0, /* send 0 pad chars */ + IDLE_FLAG, /* idle in SYN */ + 0, /* enable FCS */ + FCSP_ZEROS, /* FCS preset to all zeros (CRC-16) */ + PAD_AA, /* use AAh as pad char */ + 0, /* disable pad characters */ + }, + { /* cor6 */ + 10, /* DLE - disable special termination char */ + }, + POLY_16, /* use CRC-16 polynomial */ +}; + +cx_opt_x21_t opt_x21_dflt = { /* default x21 options */ + { /* cor1 */ + 8-1, /* 8-bit char length */ + 0, /* don't ignore parity */ + PARM_NOPAR, /* no parity */ + PAR_EVEN, /* even parity */ + }, + { /* cor2 */ + 0, + 0, /* disable embedded transmitter cmds */ + 0, + }, + { /* cor3 */ + 0, + 0, /* disable special character detect */ + 0, /* don't treat SYN as special condition */ + 0, /* disable steady state detect */ + X21SYN_2, /* 2 SYN chars on receive are required */ + }, + { /* cor6 */ + 16, /* SYN - standard SYN character */ + }, + 0, 0, 0, /* clear schr1-3 */ +}; diff --git a/sys/i386/isa/cx.c b/sys/i386/isa/cx.c new file mode 100644 index 0000000..80ecaa7 --- /dev/null +++ b/sys/i386/isa/cx.c @@ -0,0 +1,898 @@ +/* + * Cronyx-Sigma adapter driver for FreeBSD. + * Supports PPP/HDLC protocol in synchronous mode, + * and asyncronous channels with full modem control. + * + * Copyright (C) 1994 Cronyx Ltd. + * Author: Serge Vakulenko, <vak@zebub.msk.su> + * + * This software is distributed with NO WARRANTIES, not even the implied + * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Authors grant any other persons or organisations permission to use + * or modify this software as long as this message is kept with the software, + * all derivative works or modified versions. + * + * Version 1.2, Tue Nov 22 18:57:27 MSK 1994 + */ +#undef DEBUG + +#include "cx.h" +#if NCX > 0 + +#include <sys/param.h> +#include <systm.h> +#include <kernel.h> +#include <sys/mbuf.h> +#include <sys/ioctl.h> +#include <sys/file.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/tty.h> +#include <sys/errno.h> +#include <sys/syslog.h> +#include <sys/socket.h> +#include <net/if.h> + +#ifdef __FreeBSD__ +# if __FreeBSD__ < 2 +# include <i386/include/pio.h> +# define RB_GETC(q) getc(q) +# else /* BSD 4.4 Lite */ +# include <i386/include/cpufunc.h> +# include <sys/devconf.h> +# endif +# define oproc_func_t void(*)(struct tty*) +#endif +#ifdef __bsdi__ +# include <sys/ttystats.h> +# include <i386/include/inline.h> +# define tsleep(tp,pri,msg,x) ((tp)->t_state |= TS_WOPEN,\ + ttysleep (tp, (caddr_t)&tp->t_rawq, pri, msg, x)) +# define oproc_func_t int(*)() +# define timeout_func_t void(*)() +#endif +#if !defined (__FreeBSD__) || __FreeBSD__ >= 2 +# define t_out t_outq +# define RB_LEN(q) ((q).c_cc) +# define RB_GETC(q) getc(&q) +# define TSA_CARR_ON(tp) tp +# define TSA_OLOWAT(q) ((caddr_t)&(q)->t_out) +#endif + +#include <sys/cronyx.h> +#include <i386/isa/cxreg.h> + +#ifdef DEBUG +# define print(s) printf s +#else +# define print(s) /*void*/ +#endif + +#define DMABUFSZ (6*256) /* buffer size */ +#define BYTE *(unsigned char*)& +#define UNIT(u) ((u) & 077) +#define UNIT_CTL 077 + +extern cx_board_t cxboard [NCX]; /* adapter state structures */ +extern cx_chan_t *cxchan [NCX*NCHAN]; /* unit to channel struct pointer */ +#if __FreeBSD__ >= 2 +extern struct kern_devconf kdc_cx [NCX]; +struct tty cx_tty [NCX*NCHAN]; /* tty data */ +#else +struct tty *cx_tty [NCX*NCHAN]; /* tty data */ +#endif + +void cxoproc (struct tty *tp); +int cxparam (struct tty *tp, struct termios *t); +void cxswitch (cx_chan_t *c, cx_soft_opt_t new); + +int cxopen (dev_t dev, int flag, int mode, struct proc *p) +{ + int unit = UNIT (dev); + cx_chan_t *c = cxchan[unit]; + unsigned short port; + struct tty *tp; + int error = 0; + + if (unit == UNIT_CTL) { + print (("cx: cxopen /dev/cronyx\n")); + return (0); + } + if (unit >= NCX*NCHAN || !c || c->type==T_NONE) + return (ENXIO); + port = c->chip->port; + print (("cx%d.%d: cxopen unit=%d\n", c->board->num, c->num, unit)); + if (c->mode != M_ASYNC) + return (EBUSY); + if (! c->ttyp) { +#if __FreeBSD__ >= 2 + c->ttyp = &cx_tty[unit]; +#else + MALLOC (cx_tty[unit], struct tty*, sizeof (struct tty), M_DEVBUF, M_WAITOK); + bzero (cx_tty[unit], sizeof (*cx_tty[unit])); + c->ttyp = cx_tty[unit]; +#endif + c->ttyp->t_oproc = (oproc_func_t) cxoproc; + c->ttyp->t_param = cxparam; + } +#ifdef __bsdi__ + if (! c->ttydev) { + MALLOC (c->ttydev, struct ttydevice_tmp*, + sizeof (struct ttydevice_tmp), M_DEVBUF, M_WAITOK); + bzero (c->ttydev, sizeof (*c->ttydev)); + strcpy (c->ttydev->tty_name, "cx"); + c->ttydev->tty_unit = unit; + c->ttydev->tty_base = unit; + c->ttydev->tty_count = 1; + c->ttydev->tty_ttys = c->ttyp; + tty_attach (c->ttydev); + } +#endif + tp = c->ttyp; + tp->t_dev = dev; + if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE) && + p->p_ucred->cr_uid != 0) + return (EBUSY); + if (! (tp->t_state & TS_ISOPEN)) { + ttychars (tp); + if (tp->t_ispeed == 0) { +#ifdef __bsdi__ + tp->t_termios = deftermios; +#else + tp->t_iflag = 0; + tp->t_oflag = 0; + tp->t_lflag = 0; + tp->t_cflag = CREAD | CS8 | HUPCL; + tp->t_ispeed = c->rxbaud; + tp->t_ospeed = c->txbaud; +#endif + } + cxparam (tp, &tp->t_termios); + ttsetwater (tp); + } + + spltty (); + if (! (tp->t_state & TS_ISOPEN)) { + /* + * Compute optimal receiver buffer length. + * The best choice is rxbaud/400. + * Make it even, to avoid byte-wide DMA transfers. + * -------------------------- + * Baud rate Buffer length + * -------------------------- + * 300 4 + * 1200 4 + * 9600 24 + * 19200 48 + * 38400 96 + * 57600 192 + * 115200 288 + * -------------------------- + */ + int rbsz = (c->rxbaud + 800 - 1) / 800 * 2; + if (rbsz < 4) + rbsz = 4; + else if (rbsz > DMABUFSZ) + rbsz = DMABUFSZ; + + /* Initialize channel, enable receiver. */ + cx_cmd (port, CCR_INITCH | CCR_ENRX); + cx_cmd (port, CCR_INITCH | CCR_ENRX); + + /* Start receiver. */ + outw (ARBCNT(port), rbsz); + outw (BRBCNT(port), rbsz); + outw (ARBSTS(port), BSTS_OWN24); + outw (BRBSTS(port), BSTS_OWN24); + + /* Enable interrupts. */ + outb (IER(port), IER_RXD | IER_RET | IER_TXD | IER_MDM); + + cx_chan_dtr (c, 1); + cx_chan_rts (c, 1); + } + if (cx_chan_cd (c)) + tp->t_state |= TS_CARR_ON; + if (! (flag & O_NONBLOCK)) { + /* Lock the channel against cxconfig while we are + * waiting for carrier. */ + c->sopt.lock = 1; + while (!(tp->t_cflag & CLOCAL) && !(tp->t_state & TS_CARR_ON)) + if ((error = tsleep (TSA_CARR_ON(tp), TTIPRI | PCATCH, + "cxdcd", 0))) + break; + c->sopt.lock = 0; /* Unlock the channel. */ + } + print (("cx%d.%d: cxopen done csr=%b\n", c->board->num, c->num, + inb(CSR(c->chip->port)), CSRA_BITS)); + spl0 (); + if (error) + return (error); +#if __FreeBSD__ >= 2 + error = (*linesw[tp->t_line].l_open) (dev, tp); + if (tp->t_state & TS_ISOPEN) + /* Mark the board busy on the first startup. + * Never goes idle. */ + kdc_cx[c->board->num].kdc_state = DC_BUSY; +#else + error = (*linesw[tp->t_line].l_open) (dev, tp, 0); +#endif + return (error); +} + +int cxclose (dev_t dev, int flag, int mode, struct proc *p) +{ + int unit = UNIT (dev); + cx_chan_t *c = cxchan[unit]; + struct tty *tp; + int s; + + if (unit == UNIT_CTL) + return (0); + tp = c->ttyp; + (*linesw[tp->t_line].l_close) (tp, flag); + + /* Disable receiver. + * Transmitter continues sending the queued data. */ + s = spltty (); + outb (CAR(c->chip->port), c->num & 3); + outb (IER(c->chip->port), IER_TXD | IER_MDM); + cx_cmd (c->chip->port, CCR_DISRX); + + /* Clear DTR and RTS. */ + if ((tp->t_cflag & HUPCL) || ! (tp->t_state & TS_ISOPEN)) { + cx_chan_dtr (c, 0); + cx_chan_rts (c, 0); + } + + /* Stop sending break. */ + if (c->brk == BRK_SEND) { + c->brk = BRK_STOP; + if (! (tp->t_state & TS_BUSY)) + cxoproc (tp); + } + splx (s); + ttyclose (tp); + return (0); +} + +int cxread (dev_t dev, struct uio *uio, int flag) +{ + int unit = UNIT (dev); + struct tty *tp; + + if (unit == UNIT_CTL) + return (EIO); + tp = cxchan[unit]->ttyp; + return ((*linesw[tp->t_line].l_read) (tp, uio, flag)); +} + +int cxwrite (dev_t dev, struct uio *uio, int flag) +{ + int unit = UNIT (dev); + struct tty *tp; + + if (unit == UNIT_CTL) + return (EIO); + tp = cxchan[unit]->ttyp; + return ((*linesw[tp->t_line].l_write) (tp, uio, flag)); +} + +int cxioctl (dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) +{ + int unit = UNIT (dev); + cx_chan_t *c; + struct tty *tp; + int error, s; + unsigned char msv; + + if (unit == UNIT_CTL) { + /* Process an ioctl request on /dev/cronyx */ + cx_options_t *o = (cx_options_t*) data; + + if (o->board >= NCX || o->channel >= NCHAN) + return (EINVAL); + c = &cxboard[o->board].chan[o->channel]; + if (c->type == T_NONE) + return (ENXIO); + switch (cmd) { + default: + return (EINVAL); + + case CXIOCSETMODE: + print (("cx%d.%d: CXIOCSETMODE\n", o->board, o->channel)); + if (c->type == T_NONE) + return (EINVAL); + if (c->type == T_ASYNC && o->mode != M_ASYNC) + return (EINVAL); + if (o->mode == M_ASYNC) + switch (c->type) { + case T_SYNC_RS232: + case T_SYNC_V35: + case T_SYNC_RS449: + return (EINVAL); + } + /* Somebody is waiting for carrier? */ + if (c->sopt.lock) + return (EBUSY); + /* /dev/ttyXX is already opened by someone? */ + if (c->mode == M_ASYNC && c->ttyp && + (c->ttyp->t_state & TS_ISOPEN)) + return (EBUSY); + /* Network interface is up? */ + if (c->mode != M_ASYNC && (c->ifp->if_flags & IFF_UP)) + return (EBUSY); + c->mode = o->mode; + c->rxbaud = o->rxbaud; + c->txbaud = o->txbaud; + c->opt = o->opt; + c->aopt = o->aopt; + c->hopt = o->hopt; + c->bopt = o->bopt; + c->xopt = o->xopt; + switch (c->num) { + case 0: c->board->if0type = o->iftype; break; + case 8: c->board->if8type = o->iftype; break; + } + cxswitch (c, o->sopt); + s = spltty (); + cx_setup_chan (c); + outb (IER(c->chip->port), 0); + splx (s); + break; + + case CXIOCGETMODE: + print (("cx%d.%d: CXIOCGETMODE\n", o->board, o->channel)); + o->type = c->type; + o->mode = c->mode; + o->rxbaud = c->rxbaud; + o->txbaud = c->txbaud; + o->opt = c->opt; + o->aopt = c->aopt; + o->hopt = c->hopt; + o->bopt = c->bopt; + o->xopt = c->xopt; + o->sopt = c->sopt; + switch (c->num) { + case 0: o->iftype = c->board->if0type; break; + case 8: o->iftype = c->board->if8type; break; + } + break; + } + return (0); + } + + c = cxchan[unit]; + tp = c->ttyp; + if (! tp) + return (EINVAL); +#if __FreeBSD__ >= 2 + error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag, p); +#else + error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag); +#endif + if (error >= 0) + return (error); + error = ttioctl (tp, cmd, data, flag); + if (error >= 0) + return (error); + + s = spltty (); + switch (cmd) { + default: + splx (s); + return (ENOTTY); + case TIOCSBRK: /* Start sending line break */ + c->brk = BRK_SEND; + if (! (tp->t_state & TS_BUSY)) + cxoproc (tp); + break; + case TIOCCBRK: /* Stop sending line break */ + c->brk = BRK_STOP; + if (! (tp->t_state & TS_BUSY)) + cxoproc (tp); + break; + case TIOCSDTR: /* Set DTR */ + cx_chan_dtr (c, 1); + break; + case TIOCCDTR: /* Clear DTR */ + cx_chan_dtr (c, 0); + break; + case TIOCMSET: /* Set DTR/RTS */ + cx_chan_dtr (c, (*(int*)data & TIOCM_DTR) ? 1 : 0); + cx_chan_rts (c, (*(int*)data & TIOCM_RTS) ? 1 : 0); + break; + case TIOCMBIS: /* Add DTR/RTS */ + if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 1); + if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 1); + break; + case TIOCMBIC: /* Clear DTR/RTS */ + if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 0); + if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 0); + break; + case TIOCMGET: /* Get modem status */ + msv = inb (MSVR(c->chip->port)); + *(int*)data = TIOCM_LE; /* always enabled while open */ + if (msv & MSV_DSR) *(int*)data |= TIOCM_DSR; + if (msv & MSV_CTS) *(int*)data |= TIOCM_CTS; + if (msv & MSV_CD) *(int*)data |= TIOCM_CD; + if (c->dtr) *(int*)data |= TIOCM_DTR; + if (c->rts) *(int*)data |= TIOCM_RTS; + break; + } + splx (s); + return (0); +} + +/* + * Fill transmitter buffer with data. + */ +void cxout (cx_chan_t *c, char b) +{ + unsigned char *buf, *p, sym; + unsigned short port = c->chip->port, len = 0, cnt_port, sts_port; + struct tty *tp = c->ttyp; + int i; + + if (! tp) + return; + + /* Choose the buffer. */ + if (b == 'A') { + buf = c->atbuf; + cnt_port = ATBCNT(port); + sts_port = ATBSTS(port); + } else { + buf = c->btbuf; + cnt_port = BTBCNT(port); + sts_port = BTBSTS(port); + } + + /* Is it busy? */ + if (inb (sts_port) & BSTS_OWN24) { + tp->t_state |= TS_BUSY; + return; + } + + switch (c->brk) { + case BRK_SEND: + *buf++ = 0; /* extended transmit command */ + *buf++ = 0x81; /* send break */ + *buf++ = 0; /* extended transmit command */ + *buf++ = 0x82; /* insert delay */ + *buf++ = 250; /* 1/4 of second */ + *buf++ = 0; /* extended transmit command */ + *buf++ = 0x82; /* insert delay */ + *buf++ = 250; /* + 1/4 of second */ + len = 8; + c->brk = BRK_IDLE; + break; + case BRK_STOP: + *buf++ = 0; /* extended transmit command */ + *buf++ = 0x83; /* stop break */ + len = 2; + c->brk = BRK_IDLE; + break; + case BRK_IDLE: + len = RB_LEN (tp->t_out); + if (tp->t_iflag & IXOFF) + for (i=0, p=buf; i<len && p<buf+DMABUFSZ-1; ++i) { + sym = RB_GETC (tp->t_out); + /* Send XON/XOFF out of band. */ + if (sym == tp->t_cc[VSTOP]) { + outb (STCR(port), STC_SNDSPC|STC_SSPC_2); + continue; + } + if (sym == tp->t_cc[VSTART]) { + outb (STCR(port), STC_SNDSPC|STC_SSPC_1); + continue; + } + /* Duplicate NULLs in ETC mode. */ + if (! sym) + *p++ = 0; + *p++ = sym; + } + else + for (i=0, p=buf; i<len && p<buf+DMABUFSZ-1; ++i) { + sym = RB_GETC (tp->t_out); + /* Duplicate NULLs in ETC mode. */ + if (! sym) + *p++ = 0; + *p++ = sym; + } + len = p - buf; + break; + } + + /* Start transmitter. */ + if (len) { + outw (cnt_port, len); + outb (sts_port, BSTS_INTR | BSTS_OWN24); + tp->t_state |= TS_BUSY; + print (("cx%d.%d: out %d bytes to %c\n", + c->board->num, c->num, len, b)); + } +} + +void cxoproc (struct tty *tp) +{ + int unit = UNIT (tp->t_dev); + cx_chan_t *c = cxchan[unit]; + unsigned short port = c->chip->port; + int s = spltty (); + + /* Set current channel number */ + outb (CAR(port), c->num & 3); + + if (! (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))) { + /* Start transmitter. */ + if (! (inb (CSR(port)) & CSRA_TXEN)) + cx_cmd (port, CCR_ENTX); + + /* Determine the buffer order. */ + if (inb (DMABSTS(port)) & DMABSTS_NTBUF) { + cxout (c, 'B'); + cxout (c, 'A'); + } else { + cxout (c, 'A'); + cxout (c, 'B'); + } + } +#if defined (__FreeBSD__) && __FreeBSD__ < 2 + if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) + ttwwakeup (tp); +#else /* FreeBSD 2.x and BSDI */ + if (RB_LEN (tp->t_out) <= tp->t_lowat) { + if (tp->t_state & TS_ASLEEP) { + tp->t_state &= ~TS_ASLEEP; + wakeup(TSA_OLOWAT(tp)); + } + selwakeup(&tp->t_wsel); + } +#endif + /* + * Enable TXMPTY interrupt, + * to catch the case when the second buffer is empty. + */ + if ((inb (ATBSTS(port)) & BSTS_OWN24) && + (inb (BTBSTS(port)) & BSTS_OWN24)) { + outb (IER(port), IER_RXD|IER_RET|IER_TXD|IER_TXMPTY|IER_MDM); + } else + outb (IER(port), IER_RXD|IER_RET|IER_TXD|IER_MDM); + splx (s); +} + +int cxparam (struct tty *tp, struct termios *t) +{ + int unit = UNIT (tp->t_dev); + cx_chan_t *c = cxchan[unit]; + unsigned short port = c->chip->port; + int clock, period, s; + cx_cor1_async_t cor1; + + if (t->c_ospeed == 0) { + /* Clear DTR and RTS. */ + s = spltty (); + cx_chan_dtr (c, 0); + cx_chan_rts (c, 0); + splx (s); + print (("cx%d.%d: cxparam (hangup)\n", c->board->num, c->num)); + return (0); + } + print (("cx%d.%d: cxparam\n", c->board->num, c->num)); + + /* Check requested parameters. */ + if (t->c_ospeed < 300 || t->c_ospeed > 256*1024) + return(EINVAL); + if (t->c_ispeed && (t->c_ispeed < 300 || t->c_ispeed > 256*1024)) + return(EINVAL); + +#ifdef __bsdi__ + /* CLOCAL flag set -- wakeup everybody who waits for CD. */ + /* FreeBSD does this themselves. */ + if (! (tp->t_cflag & CLOCAL) && (t->c_cflag & CLOCAL)) + wakeup ((caddr_t) &tp->t_rawq); +#endif + /* And copy them to tty and channel structures. */ + c->rxbaud = tp->t_ispeed = t->c_ispeed; + c->txbaud = tp->t_ospeed = t->c_ospeed; + tp->t_cflag = t->c_cflag; + + /* Set character length and parity mode. */ + BYTE cor1 = 0; + switch (t->c_cflag & CSIZE) { + default: + case CS8: cor1.charlen = 7; break; + case CS7: cor1.charlen = 6; break; + case CS6: cor1.charlen = 5; break; + case CS5: cor1.charlen = 4; break; + } + if (t->c_cflag & PARENB) { + cor1.parmode = PARM_NORMAL; + cor1.ignpar = 0; + cor1.parity = (t->c_cflag & PARODD) ? PAR_ODD : PAR_EVEN; + } else { + cor1.parmode = PARM_NOPAR; + cor1.ignpar = 1; + } + + /* Enable/disable hardware CTS. */ + c->aopt.cor2.ctsae = (t->c_cflag & CRTSCTS) ? 1 : 0; + /* Handle DSR as CTS. */ + c->aopt.cor2.dsrae = (t->c_cflag & CRTSCTS) ? 1 : 0; + /* Enable extended transmit command mode. + * Unfortunately, there is no other method for sending break. */ + c->aopt.cor2.etc = 1; + /* Enable/disable hardware XON/XOFF. */ + c->aopt.cor2.ixon = (t->c_iflag & IXON) ? 1 : 0; + c->aopt.cor2.ixany = (t->c_iflag & IXANY) ? 1 : 0; + + /* Set the number of stop bits. */ + if (t->c_cflag & CSTOPB) + c->aopt.cor3.stopb = STOPB_2; + else + c->aopt.cor3.stopb = STOPB_1; + /* Disable/enable passing XON/XOFF chars to the host. */ + c->aopt.cor3.scde = (t->c_iflag & IXON) ? 1 : 0; + c->aopt.cor3.flowct = (t->c_iflag & IXON) ? FLOWCC_NOTPASS : FLOWCC_PASS; + + c->aopt.schr1 = t->c_cc[VSTART]; /* XON */ + c->aopt.schr2 = t->c_cc[VSTOP]; /* XOFF */ + + /* Set current channel number. */ + s = spltty (); + outb (CAR(port), c->num & 3); + + /* Set up receiver clock values. */ + cx_clock (c->chip->oscfreq, c->rxbaud, &clock, &period); + c->opt.rcor.clk = clock; + outb (RCOR(port), BYTE c->opt.rcor); + outb (RBPR(port), period); + + /* Set up transmitter clock values. */ + cx_clock (c->chip->oscfreq, c->txbaud, &clock, &period); + c->opt.tcor.clk = clock; + c->opt.tcor.ext1x = 0; + outb (TCOR(port), BYTE c->opt.tcor); + outb (TBPR(port), period); + + outb (COR2(port), BYTE c->aopt.cor2); + outb (COR3(port), BYTE c->aopt.cor3); + outb (SCHR1(port), c->aopt.schr1); + outb (SCHR2(port), c->aopt.schr2); + + if (BYTE c->aopt.cor1 != BYTE cor1) { + BYTE c->aopt.cor1 = BYTE cor1; + outb (COR1(port), BYTE c->aopt.cor1); + /* Any change to COR1 require reinitialization. */ + /* Unfortunately, it may cause transmitter glitches... */ + cx_cmd (port, CCR_INITCH); + } + splx (s); + return (0); +} + +int cxselect (dev_t dev, int flag, struct proc *p) +{ + int unit = UNIT (dev); + + if (unit == UNIT_CTL) + return (0); +#ifdef __FreeBSD__ + return (ttselect (dev, flag, p)); +#endif +#ifdef __bsdi__ + return (ttyselect (cxchan[unit]->ttyp, flag, p)); +#endif +} + +/* + * Stop output on a line + */ +void cxstop (struct tty *tp, int flag) +{ + cx_chan_t *c = cxchan[UNIT(tp->t_dev)]; + unsigned short port = c->chip->port; + int s = spltty (); + + if (tp->t_state & TS_BUSY) { + print (("cx%d.%d: cxstop\n", c->board->num, c->num)); + + /* Set current channel number */ + outb (CAR(port), c->num & 3); + + /* Stop transmitter */ + cx_cmd (port, CCR_DISTX); + } + splx (s); +} + +/* + * Handle receive interrupts, including receive errors and + * receive timeout interrupt. + */ +int cxrinta (cx_chan_t *c) +{ + unsigned short port = c->chip->port; + unsigned short len = 0, risr = inw (RISR(port)), reoir = 0; + struct tty *tp = c->ttyp; + + /* Compute optimal receiver buffer length. */ + int rbsz = (c->rxbaud + 800 - 1) / 800 * 2; + if (rbsz < 4) + rbsz = 4; + else if (rbsz > DMABUFSZ) + rbsz = DMABUFSZ; + + if (risr & RISA_TIMEOUT) { + unsigned long rcbadr = (unsigned short) inw (RCBADRL(port)) | + (long) inw (RCBADRU(port)) << 16; + unsigned char *buf = 0; + unsigned short cnt_port = 0, sts_port = 0; + if (rcbadr >= c->brphys && rcbadr < c->brphys+DMABUFSZ) { + buf = c->brbuf; + len = rcbadr - c->brphys; + cnt_port = BRBCNT(port); + sts_port = BRBSTS(port); + } else if (rcbadr >= c->arphys && rcbadr < c->arphys+DMABUFSZ) { + buf = c->arbuf; + len = rcbadr - c->arphys; + cnt_port = ARBCNT(port); + sts_port = ARBSTS(port); + } else + printf ("cx%d.%d: timeout: invalid buffer address\n", + c->board->num, c->num); + + if (len) { + print (("cx%d.%d: async receive timeout (%d bytes), risr=%b, arbsts=%b, brbsts=%b\n", + c->board->num, c->num, len, risr, RISA_BITS, + inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS)); + if (tp && (tp->t_state & TS_ISOPEN)) { + int i; + void (*rint)() = (void(*)()) + linesw[tp->t_line].l_rint; + + for (i=0; i<len; ++i) + (*rint) (buf[i], tp); + } + + /* Restart receiver. */ + outw (cnt_port, rbsz); + outb (sts_port, BSTS_OWN24); + } + return (REOI_TERMBUFF); + } + + print (("cx%d.%d: async receive interrupt, risr=%b, arbsts=%b, brbsts=%b\n", + c->board->num, c->num, risr, RISA_BITS, + inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS)); + + if (risr & RIS_BUSERR) + printf ("cx%d.%d: receive bus error\n", c->board->num, c->num); + + if (risr & (RIS_OVERRUN | RISA_PARERR | RISA_FRERR)) { + print (("cx%d.%d: receive error\n", c->board->num, c->num)); + if (tp && (tp->t_state & TS_ISOPEN)) + (*linesw[tp->t_line].l_rint) (' ' | + (risr & RISA_FRERR) ? TTY_FE : TTY_PE, tp); + } + + /* Handle line break condition. */ + if ((risr & RISA_BREAK) && tp && (tp->t_state & TS_ISOPEN)) + (*linesw[tp->t_line].l_rint) (TTY_FE, tp); + + /* Discard exception characters. */ + if ((risr & RISA_SCMASK) && (tp->t_iflag & IXON)) + reoir |= REOI_DISCEXC; + + /* Handle received data. */ + if ((risr & RIS_EOBUF) && tp && (tp->t_state & TS_ISOPEN)) { + void (*rint)() = (void(*)()) linesw[tp->t_line].l_rint; + unsigned char *buf; + int i; + + len = (risr & RIS_BB) ? inw(BRBCNT(port)) : inw(ARBCNT(port)); + + print (("cx%d.%d: async: %d bytes received\n", + c->board->num, c->num, len)); + + buf = (risr & RIS_BB) ? c->brbuf : c->arbuf; + for (i=0; i<len; ++i) + (*rint) (buf[i], tp); + } + + /* Restart receiver. */ + if (! (inb (ARBSTS(port)) & BSTS_OWN24)) { + outw (ARBCNT(port), rbsz); + outb (ARBSTS(port), BSTS_OWN24); + } + if (! (inb (BRBSTS(port)) & BSTS_OWN24)) { + outw (BRBCNT(port), rbsz); + outb (BRBSTS(port), BSTS_OWN24); + } + return (reoir); +} + +/* + * Handle transmit interrupt. + */ +void cxtinta (cx_chan_t *c) +{ + struct tty *tp = c->ttyp; + unsigned short port = c->chip->port; + unsigned char tisr = inb (TISR(port)); + + print (("cx%d.%d: async transmit interrupt, tisr=%b, atbsts=%b, btbsts=%b\n", + c->board->num, c->num, tisr, TIS_BITS, + inb (ATBSTS(port)), BSTS_BITS, inb (BTBSTS(port)), BSTS_BITS)); + + if (tisr & TIS_BUSERR) + printf ("cx%d.%d: transmit bus error\n", + c->board->num, c->num); + else if (tisr & TIS_UNDERRUN) + printf ("cx%d.%d: transmit underrun error\n", + c->board->num, c->num); + + if (tp) { + tp->t_state &= ~(TS_BUSY | TS_FLUSH); + if (tp->t_line) + (*linesw[tp->t_line].l_start) (tp); + else + cxoproc (tp); + } +} + +/* + * Handle modem interrupt. + */ +void cxmint (cx_chan_t *c) +{ + unsigned short port = c->chip->port; + unsigned char misr = inb (MISR(port)); + unsigned char msvr = inb (MSVR(port)); + struct tty *tp = c->ttyp; + + if (c->mode != M_ASYNC) { + printf ("cx%d.%d: unexpected modem interrupt, misr=%b, msvr=%b\n", + c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS); + return; + } + print (("cx%d.%d: modem interrupt, misr=%b, msvr=%b\n", + c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS)); + + /* Ignore DSR events. */ + /* Ignore RTC/CTS events, handled by hardware. */ + /* Handle carrier detect/loss. */ + if (tp && (misr & MIS_CCD)) + (*linesw[tp->t_line].l_modem) (tp, (msvr & MSV_CD) != 0); +} + +/* + * Recover after lost transmit interrupts. + */ +void cxtimeout (caddr_t a) +{ + cx_board_t *b; + cx_chan_t *c; + struct tty *tp; + int s; + + for (b=cxboard; b<cxboard+NCX; ++b) + for (c=b->chan; c<b->chan+NCHAN; ++c) { + tp = c->ttyp; + if (c->type==T_NONE || c->mode!=M_ASYNC || !tp) + continue; + s = spltty (); + if (tp->t_state & TS_BUSY) { + tp->t_state &= ~TS_BUSY; + if (tp->t_line) + (*linesw[tp->t_line].l_start) (tp); + else + cxoproc (tp); + } + splx (s); + } + timeout ((timeout_func_t) cxtimeout, 0, hz*5); +} +#endif /* NCX */ diff --git a/sys/i386/isa/cxreg.h b/sys/i386/isa/cxreg.h new file mode 100644 index 0000000..491c7fb --- /dev/null +++ b/sys/i386/isa/cxreg.h @@ -0,0 +1,429 @@ +/* + * Defines for Cronyx-Sigma adapter, based on Cirrus Logic multiprotocol + * controller RISC processor CL-CD2400/2401. + * + * Copyright (C) 1994 Cronyx Ltd. + * Author: Serge Vakulenko, <vak@zebub.msk.su> + * + * This software is distributed with NO WARRANTIES, not even the implied + * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Authors grant any other persons or organisations permission to use + * or modify this software as long as this message is kept with the software, + * all derivative works or modified versions. + * + * Version 1.0, Fri Oct 7 19:34:06 MSD 1994 + */ +#define NBRD 3 /* the maximum number of installed boards */ +#define NPORT 16 /* the number of i/o ports per board */ + +#define REVCL_MIN 7 /* CD2400 min. revision number G */ +#define REVCL_MAX 11 /* CD2400 max. revision number K */ + +#define BRD_INTR_LEVEL 0x5a /* interrupt level (arbitrary PILR value) */ + +#define CS0(p) ((p) | 0x8000) /* chip select 0 */ +#define CS1(p) ((p) | 0xc000) /* chip select 1 */ +#define BSR(p) (p) /* board status register, read only */ +#define BCR0(p) (p) /* board command register 0, write only */ +#define BCR1(p) ((p) | 0x2000) /* board command register 1, write only */ + +/* + * Chip register address, B is chip base port, R is chip register number. + */ +#define R(b,r) ((b) | (((r)<<6 & 0x3c00) | ((r) & 0xf))) + +/* + * Interrupt acknowledge register, P is board port, L is interrupt level, + * as prodrammed in PILR. + */ +#define IACK(p,l) (R(p,l) | 0x4000) + +/* + * Global registers. + */ +#define GFRCR(b) R(b,0x82) /* global firmware revision code register */ +#define CAR(b) R(b,0xec) /* channel access register */ + +/* + * Option registers. + */ +#define CMR(b) R(b,0x18) /* channel mode register */ +#define COR1(b) R(b,0x13) /* channel option register 1 */ +#define COR2(b) R(b,0x14) /* channel option register 2 */ +#define COR3(b) R(b,0x15) /* channel option register 3 */ +#define COR4(b) R(b,0x16) /* channel option register 4 */ +#define COR5(b) R(b,0x17) /* channel option register 5 */ +#define COR6(b) R(b,0x1b) /* channel option register 6 */ +#define COR7(b) R(b,0x04) /* channel option register 7 */ +#define SCHR1(b) R(b,0x1c) /* special character register 1 */ +#define SCHR2(b) R(b,0x1d) /* special character register 2 */ +#define SCHR3(b) R(b,0x1e) /* special character register 3 */ +#define SCHR4(b) R(b,0x1f) /* special character register 4 */ +#define SCRL(b) R(b,0x20) /* special character range low */ +#define SCRH(b) R(b,0x21) /* special character range high */ +#define LNXT(b) R(b,0x2d) /* LNext character */ +#define RFAR1(b) R(b,0x1c) /* receive frame address register 1 */ +#define RFAR2(b) R(b,0x1d) /* receive frame address register 2 */ +#define RFAR3(b) R(b,0x1e) /* receive frame address register 3 */ +#define RFAR4(b) R(b,0x1f) /* receive frame address register 4 */ +#define CPSR(b) R(b,0xd4) /* CRC polynomial select register */ + +/* + * Bit rate and clock option registers. + */ +#define RBPR(b) R(b,0xc9) /* receive baud rate period register */ +#define RCOR(b) R(b,0xca) /* receive clock option register */ +#define TBPR(b) R(b,0xc1) /* transmit baud rate period register */ +#define TCOR(b) R(b,0xc2) /* receive clock option register */ + +/* + * Channel command and status registers. + */ +#define CCR(b) R(b,0x10) /* channel command register */ +#define STCR(b) R(b,0x11) /* special transmit command register */ +#define CSR(b) R(b,0x19) /* channel status register */ +#define MSVR(b) R(b,0xdc) /* modem signal value register */ +#define MSVR_RTS(b) R(b,0xdc) /* modem RTS setup register */ +#define MSVR_DTR(b) R(b,0xdd) /* modem DTR setup register */ + +/* + * Interrupt registers. + */ +#define LIVR(b) R(b,0x0a) /* local interrupt vector register */ +#define IER(b) R(b,0x12) /* interrupt enable register */ +#define LICR(b) R(b,0x25) /* local interrupting channel register */ +#define STK(b) R(b,0xe0) /* stack register */ + +/* + * Receive interrupt registers. + */ +#define RPILR(b) R(b,0xe3) /* receive priority interrupt level register */ +#define RIR(b) R(b,0xef) /* receive interrupt register */ +#define RISR(b) R(b,0x8a) /* receive interrupt status register */ +#define RISRL(b) R(b,0x8a) /* receive interrupt status register low */ +#define RISRH(b) R(b,0x8b) /* receive interrupt status register high */ +#define RFOC(b) R(b,0x33) /* receive FIFO output count */ +#define RDR(b) R(b,0xf8) /* receive data register */ +#define REOIR(b) R(b,0x87) /* receive end of interrupt register */ + +/* + * Transmit interrupt registers. + */ +#define TPILR(b) R(b,0xe2) /* transmit priority interrupt level reg */ +#define TIR(b) R(b,0xee) /* transmit interrupt register */ +#define TISR(b) R(b,0x89) /* transmit interrupt status register */ +#define TFTC(b) R(b,0x83) /* transmit FIFO transfer count */ +#define TDR(b) R(b,0xf8) /* transmit data register */ +#define TEOIR(b) R(b,0x86) /* transmit end of interrupt register */ + +/* + * Modem interrupt registers. + */ +#define MPILR(b) R(b,0xe1) /* modem priority interrupt level register */ +#define MIR(b) R(b,0xed) /* modem interrupt register */ +#define MISR(b) R(b,0x88) /* modem/timer interrupt status register */ +#define MEOIR(b) R(b,0x85) /* modem end of interrupt register */ + +/* + * DMA registers. + */ +#define DMR(b) R(b,0xf4) /* DMA mode register */ +#define BERCNT(b) R(b,0x8d) /* bus error retry count */ +#define DMABSTS(b) R(b,0x1a) /* DMA buffer status */ + +/* + * DMA receive registers. + */ +#define ARBADRL(b) R(b,0x40) /* A receive buffer address lower */ +#define ARBADRU(b) R(b,0x42) /* A receive buffer address upper */ +#define BRBADRL(b) R(b,0x44) /* B receive buffer address lower */ +#define BRBADRU(b) R(b,0x46) /* B receive buffer address upper */ +#define ARBCNT(b) R(b,0x48) /* A receive buffer byte count */ +#define BRBCNT(b) R(b,0x4a) /* B receive buffer byte count */ +#define ARBSTS(b) R(b,0x4c) /* A receive buffer status */ +#define BRBSTS(b) R(b,0x4d) /* B receive buffer status */ +#define RCBADRL(b) R(b,0x3c) /* receive current buffer address lower */ +#define RCBADRU(b) R(b,0x3e) /* receive current buffer address upper */ + +/* + * DMA transmit registers. + */ +#define ATBADRL(b) R(b,0x50) /* A transmit buffer address lower */ +#define ATBADRU(b) R(b,0x52) /* A transmit buffer address upper */ +#define BTBADRL(b) R(b,0x54) /* B transmit buffer address lower */ +#define BTBADRU(b) R(b,0x56) /* B transmit buffer address upper */ +#define ATBCNT(b) R(b,0x58) /* A transmit buffer byte count */ +#define BTBCNT(b) R(b,0x5a) /* B transmit buffer byte count */ +#define ATBSTS(b) R(b,0x5c) /* A transmit buffer status */ +#define BTBSTS(b) R(b,0x5d) /* B transmit buffer status */ +#define TCBADRL(b) R(b,0x38) /* transmit current buffer address lower */ +#define TCBADRU(b) R(b,0x3a) /* transmit current buffer address upper */ + +/* + * Timer registers. + */ +#define TPR(b) R(b,0xd8) /* timer period register */ +#define RTPR(b) R(b,0x26) /* receive timeout period register */ +#define RTPRL(b) R(b,0x26) /* receive timeout period register low */ +#define RTPTH(b) R(b,0x27) /* receive timeout period register high */ +#define GT1(b) R(b,0x28) /* general timer 1 */ +#define GT1L(b) R(b,0x28) /* general timer 1 low */ +#define GT1H(b) R(b,0x29) /* general timer 1 high */ +#define GT2(b) R(b,0x2a) /* general timer 2 */ +#define TTR(b) R(b,0x2a) /* transmit timer register */ + +/* + * Board status register bits. + */ +#define BSR_NOINTR 0x01 /* no interrupt pending flag */ + +#define BSR_VAR_MASK 0x66 /* adapter variant mask */ + +#define BSR_OSC_MASK 0x18 /* oscillator frequency mask */ +#define BSR_OSC_20 0x18 /* 20 MHz */ +#define BSR_OSC_18432 0x10 /* 18.432 MHz */ + +#define BSR_NOCHAIN 0x80 /* no daisy chained board */ + +#define BSR_NODSR(n) (0x100 << (n)) /* DSR from channels 0-3, inverted */ +#define BSR_NOCD(n) (0x1000 << (n)) /* CD from channels 0-3, inverted */ + +/* + * Board revision mask. + */ +#define BSR_REV_MASK (BSR_OSC_MASK|BSR_VAR_MASK|BSR_NOCHAIN) + +/* + * Board control register 0 bits. + */ +#define BCR0_IRQ_DIS 0x00 /* no interrupt generated */ +#define BCR0_IRQ_3 0x01 /* select IRQ number 3 */ +#define BCR0_IRQ_5 0x02 /* select IRQ number 5 */ +#define BCR0_IRQ_7 0x03 /* select IRQ number 7 */ +#define BCR0_IRQ_10 0x04 /* select IRQ number 10 */ +#define BCR0_IRQ_11 0x05 /* select IRQ number 11 */ +#define BCR0_IRQ_12 0x06 /* select IRQ number 12 */ +#define BCR0_IRQ_15 0x07 /* select IRQ number 15 */ + +#define BCR0_NORESET 0x08 /* CD2400 reset flag (inverted) */ + +#define BCR0_DMA_DIS 0x00 /* no interrupt generated */ +#define BCR0_DMA_5 0x10 /* select DMA channel 5 */ +#define BCR0_DMA_6 0x20 /* select DMA channel 6 */ +#define BCR0_DMA_7 0x30 /* select DMA channel 7 */ + +#define BCR0_UM_ASYNC 0x00 /* channel 0 mode - async */ +#define BCR0_UM_SYNC 0x80 /* channel 0 mode - sync */ +#define BCR0_UI_RS232 0x00 /* channel 0 interface - RS-232 */ +#define BCR0_UI_RS449 0x40 /* channel 0 interface - RS-449/V.35 */ +#define BCR0_UMASK 0xc0 /* channel 0 interface mask */ + +/* + * Board control register 1 bits. + */ +#define BCR1_DTR(n) (0x100 << (n)) /* DTR for channels 0-3 sync */ + +/* + * Cronyx board variants. + */ +#define CRONYX_100 0x64 +#define CRONYX_400 0x62 +#define CRONYX_500 0x60 +#define CRONYX_410 0x24 +#define CRONYX_810 0x20 +#define CRONYX_410s 0x04 +#define CRONYX_810s 0x00 +#define CRONYX_440 0x44 +#define CRONYX_840 0x40 +#define CRONYX_401 0x26 +#define CRONYX_801 0x22 +#define CRONYX_401s 0x06 +#define CRONYX_801s 0x02 +#define CRONYX_404 0x46 +#define CRONYX_703 0x42 + +/* + * Channel commands (CCR). + */ +#define CCR_CLRCH 0x40 /* clear channel */ +#define CCR_INITCH 0x20 /* initialize channel */ +#define CCR_RSTALL 0x10 /* reset all channels */ +#define CCR_ENTX 0x08 /* enable transmitter */ +#define CCR_DISTX 0x04 /* disable transmitter */ +#define CCR_ENRX 0x02 /* enable receiver */ +#define CCR_DISRX 0x01 /* disable receiver */ +#define CCR_CLRT1 0xc0 /* clear timer 1 */ +#define CCR_CLRT2 0xa0 /* clear timer 2 */ +#define CCR_CLRRCV 0x90 /* clear receiver */ + +/* + * Interrupt enable register (IER) bits. + */ +#define IER_MDM 0x80 /* modem status changed */ +#define IER_RET 0x20 /* receive exception timeout */ +#define IER_RXD 0x08 /* data received */ +#define IER_TIMER 0x04 /* timer expired */ +#define IER_TXMPTY 0x02 /* transmitter empty */ +#define IER_TXD 0x01 /* data transmitted */ + +/* + * Modem signal values register bits (MSVR). + */ +#define MSV_DSR 0x80 /* state of Data Set Ready input */ +#define MSV_CD 0x40 /* state of Carrier Detect input */ +#define MSV_CTS 0x20 /* state of Clear to Send input */ +#define MSV_TXCOUT 0x10 /* TXCout/DTR pin output flag */ +#define MSV_PORTID 0x04 /* device is CL-CD2401 (not 2400) */ +#define MSV_DTR 0x02 /* state of Data Terminal Ready output */ +#define MSV_RTS 0x01 /* state of Request to Send output */ +#define MSV_BITS "\20\1rts\2dtr\3cd2400\5txcout\6cts\7cd\10dsr" + +/* + * DMA buffer status register bits (DMABSTS). + */ +#define DMABSTS_TDALIGN 0x80 /* internal data alignment in transmit FIFO */ +#define DMABSTS_RSTAPD 0x40 /* reset append mode */ +#define DMABSTS_CRTTBUF 0x20 /* internal current transmit buffer in use */ +#define DMABSTS_APPEND 0x10 /* append buffer is in use */ +#define DMABSTS_NTBUF 0x08 /* next transmit buffer is B (not A) */ +#define DMABSTS_TBUSY 0x04 /* current transmit buffer is in use */ +#define DMABSTS_NRBUF 0x02 /* next receive buffer is B (not A) */ +#define DMABSTS_RBUSY 0x01 /* current receive buffer is in use */ + +/* + * Buffer status register bits ([AB][RT]BSTS). + */ +#define BSTS_BUSERR 0x80 /* bus error */ +#define BSTS_EOFR 0x40 /* end of frame */ +#define BSTS_EOBUF 0x20 /* end of buffer */ +#define BSTS_APPEND 0x08 /* append mode */ +#define BSTS_INTR 0x02 /* interrupt required */ +#define BSTS_OWN24 0x01 /* buffer is (free to be) used by CD2400 */ +#define BSTS_BITS "\20\1own24\2intr\4append\6eobuf\7eofr\10buserr" + +/* + * Receive interrupt status register (RISR) bits. + */ +#define RIS_OVERRUN 0x0008 /* overrun error */ +#define RIS_BB 0x0800 /* buffer B status (not A) */ +#define RIS_EOBUF 0x2000 /* end of buffer reached */ +#define RIS_EOFR 0x4000 /* frame reception complete */ +#define RIS_BUSERR 0x8000 /* bus error */ + +#define RISH_CLRDCT 0x0001 /* X.21 clear detect */ +#define RISH_RESIND 0x0004 /* residual indication */ +#define RISH_CRCERR 0x0010 /* CRC error */ +#define RISH_RXABORT 0x0020 /* abort sequence received */ +#define RISH_EOFR 0x0040 /* complete frame received */ +#define RISH_BITS "\20\1clrdct\3resind\4overrun\5crcerr\6rxabort\7eofr\14bb\16eobuf\17eofr\20buserr" + +#define RISA_BREAK 0x0001 /* break signal detected */ +#define RISA_FRERR 0x0002 /* frame error (bad stop bits) */ +#define RISA_PARERR 0x0004 /* parity error */ +#define RISA_SCMASK 0x0070 /* special character detect mask */ +#define RISA_SCHR1 0x0010 /* special character 1 detected */ +#define RISA_SCHR2 0x0020 /* special character 2 detected */ +#define RISA_SCHR3 0x0030 /* special character 3 detected */ +#define RISA_SCHR4 0x0040 /* special character 4 detected */ +#define RISA_SCRANGE 0x0070 /* special character in range detected */ +#define RISA_TIMEOUT 0x0080 /* receive timeout, no data */ +#define RISA_BITS "\20\1break\2frerr\3parerr\4overrun\5schr1\6schr2\7schr4\10timeout\14bb\16eobuf\17eofr\20buserr" + +#define RISB_CRCERR 0x0010 /* CRC error */ +#define RISB_RXABORT 0x0020 /* abort sequence received */ +#define RISB_EOFR 0x0040 /* complete frame received */ + +#define RISX_LEADCHG 0x0001 /* CTS lead change */ +#define RISX_PARERR 0x0004 /* parity error */ +#define RISX_SCMASK 0x0070 /* special character detect mask */ +#define RISX_SCHR1 0x0010 /* special character 1 detected */ +#define RISX_SCHR2 0x0020 /* special character 2 detected */ +#define RISX_SCHR3 0x0030 /* special character 3 detected */ +#define RISX_ALLZERO 0x0040 /* all 0 condition detected */ +#define RISX_ALLONE 0x0050 /* all 1 condition detected */ +#define RISX_ALTOZ 0x0060 /* alternating 1 0 condition detected */ +#define RISX_SYN 0x0070 /* SYN detected */ +#define RISX_LEAD 0x0080 /* leading value */ + +/* + * Channel mode register (CMR) bits. + */ +#define CMR_RXDMA 0x80 /* DMA receive transfer mode */ +#define CMR_TXDMA 0x40 /* DMA transmit transfer mode */ +#define CMR_HDLC 0x00 /* HDLC protocol mode */ +#define CMR_BISYNC 0x01 /* BISYNC protocol mode */ +#define CMR_ASYNC 0x02 /* ASYNC protocol mode */ +#define CMR_X21 0x03 /* X.21 protocol mode */ + +/* + * Modem interrupt status register (MISR) bits. + */ +#define MIS_CDSR 0x80 /* DSR changed */ +#define MIS_CCD 0x40 /* CD changed */ +#define MIS_CCTS 0x20 /* CTS changed */ +#define MIS_CGT2 0x02 /* GT2 timer expired */ +#define MIS_CGT1 0x01 /* GT1 timer expired */ +#define MIS_BITS "\20\1gt1\2gt2\6ccts\7ccd\10cdsr" + +/* + * Transmit interrupt status register (TISR) bits. + */ +#define TIS_BUSERR 0x80 /* Bus error */ +#define TIS_EOFR 0x40 /* End of frame */ +#define TIS_EOBUF 0x20 /* end of transmit buffer reached */ +#define TIS_UNDERRUN 0x10 /* transmit underrun */ +#define TIS_BB 0x08 /* buffer B status (not A) */ +#define TIS_TXEMPTY 0x02 /* transmitter empty */ +#define TIS_TXDATA 0x01 /* transmit data below threshold */ +#define TIS_BITS "\20\1txdata\2txempty\4bb\5underrun\6eobuf\7eofr\10buserr" + +/* + * Local interrupt vector register (LIVR) bits. + */ +#define LIV_EXCEP 0 +#define LIV_MODEM 1 +#define LIV_TXDATA 2 +#define LIV_RXDATA 3 + +/* + * Transmit end of interrupt registers (TEOIR) bits. + */ +#define TEOI_TERMBUFF 0x80 /* force current buffer to be discarded */ +#define TEOI_EOFR 0x40 /* end of frame in interrupt mode */ +#define TEOI_SETTM2 0x20 /* set general timer 2 in sync mode */ +#define TEOI_SETTM1 0x10 /* set general timer 1 in sync mode */ +#define TEOI_NOTRANSF 0x08 /* no transfer of data on this interrupt */ + +/* + * Receive end of interrupt registers (REOIR) bits. + */ +#define REOI_TERMBUFF 0x80 /* force current buffer to be terminated */ +#define REOI_DISCEXC 0x40 /* discard exception character */ +#define REOI_SETTM2 0x20 /* set general timer 2 */ +#define REOI_SETTM1 0x10 /* set general timer 1 */ +#define REOI_NOTRANSF 0x08 /* no transfer of data */ +#define REOI_GAP_MASK 0x07 /* optional gap size to leave in buffer */ + +/* + * Special transmit command register (STCR) bits. + */ +#define STC_ABORTTX 0x40 /* abort transmission (HDLC mode) */ +#define STC_APPDCMP 0x20 /* append complete (async DMA mode) */ +#define STC_SNDSPC 0x08 /* send special characters (async mode) */ +#define STC_SSPC_MASK 0x07 /* special character select */ +#define STC_SSPC_1 0x01 /* send special character #1 */ +#define STC_SSPC_2 0x02 /* send special character #2 */ +#define STC_SSPC_3 0x03 /* send special character #3 */ +#define STC_SSPC_4 0x04 /* send special character #4 */ + +/* + * Channel status register (CSR) bits, asynchronous mode. + */ +#define CSRA_RXEN 0x80 /* receiver enable */ +#define CSRA_RXFLOFF 0x40 /* receiver flow off */ +#define CSRA_RXFLON 0x20 /* receiver flow on */ +#define CSRA_TXEN 0x08 /* transmitter enable */ +#define CSRA_TXFLOFF 0x04 /* transmitter flow off */ +#define CSRA_TXFLON 0x02 /* transmitter flow on */ +#define CSRA_BITS "\20\2txflon\3txfloff\4txen\6rxflon\7rxfloff\10rxen" diff --git a/sys/i386/isa/if_cx.c b/sys/i386/isa/if_cx.c new file mode 100644 index 0000000..d6c0367 --- /dev/null +++ b/sys/i386/isa/if_cx.c @@ -0,0 +1,818 @@ +/* + * Cronyx-Sigma adapter driver for FreeBSD. + * Supports PPP/HDLC and Cisco/HDLC protocol in synchronous mode, + * and asyncronous channels with full modem control. + * Keepalive protocol implemented in both Cisco and PPP modes. + * + * Copyright (C) 1994 Cronyx Ltd. + * Author: Serge Vakulenko, <vak@zebub.msk.su> + * + * This software is distributed with NO WARRANTIES, not even the implied + * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Authors grant any other persons or organisations permission to use + * or modify this software as long as this message is kept with the software, + * all derivative works or modified versions. + * + * Version 1.2, Tue Nov 22 18:57:27 MSK 1994 + */ +#undef DEBUG + +#include "cx.h" +#if NCX > 0 +#include <bpfilter.h> + +#include <sys/param.h> +#include <systm.h> +#include <kernel.h> +#include <sys/mbuf.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/conf.h> +#include <sys/errno.h> + +#include <net/if.h> +#include <net/if_types.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#ifdef __FreeBSD__ +# include <i386/isa/isa_device.h> +# if __FreeBSD__ < 2 +# include <i386/include/pio.h> +# else +# include <i386/include/cpufunc.h> +# include <sys/devconf.h> +# endif +# define init_func_t void(*)(int) +# define watchdog_func_t void(*)(int) +# define start_func_t void(*)(struct ifnet*) +#endif + +#ifdef __bsdi__ +# if INET +# include <netinet/in.h> +# include <netinet/in_systm.h> +# include <netinet/ip.h> +# endif +# include <sys/device.h> +# include <i386/isa/isavar.h> +# include <i386/isa/icu.h> +# include <i386/include/inline.h> +# include <net/if_slvar.h> +# include <net/if_p2p.h> +# define timeout_func_t void(*)() +# define init_func_t int(*)() +# define watchdog_func_t int(*)() +# define start_func_t int(*)() +struct cxsoftc { + struct device dev; /* base device */ + struct isadev isadev; /* ISA device */ + struct intrhand intr; /* interrupt vectoring */ +}; +#endif + +#include <net/if_sppp.h> +#include <sys/cronyx.h> +#include <i386/isa/cxreg.h> + +#ifdef DEBUG +# define print(s) printf s +#else +# define print(s) /*void*/ +#endif + +#define TXTIMEOUT 2 /* transmit timeout in seconds */ +#define DMABUFSZ (6*256) /* buffer size */ +#define PPP_HEADER_LEN 4 /* size of PPP header */ + +/* + * Under BSDI it's possible to use general p2p protocol scheme, + * as well as our own one. Switching is done via IFF_ALTPHYS flag. + * Our ifnet pointer holds the buffer large enough to contain + * any of sppp and p2p structures. + */ +#ifdef __bsdi__ +# define SPPPSZ (sizeof (struct sppp)) +# define P2PSZ (sizeof (struct p2pcom)) +# define IFSTRUCTSZ (SPPPSZ>P2PSZ ? SPPPSZ : P2PSZ) +#else +# define IFSTRUCTSZ (sizeof (struct sppp)) +#endif +#define IFNETSZ (sizeof (struct ifnet)) + +int cxoutput (struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt); +int cxsioctl (struct ifnet *ifp, int cmd, caddr_t data); +void cxinit (int unit); +void cxstart (struct ifnet *ifp); +void cxwatchdog (int unit); +void cxinput (cx_chan_t *c, void *buf, unsigned len); +int cxrinta (cx_chan_t *c); +void cxtinta (cx_chan_t *c); +void cxmint (cx_chan_t *c); +void cxtimeout (caddr_t a); + +cx_board_t cxboard [NCX]; /* adapter state structures */ +cx_chan_t *cxchan [NCX*NCHAN]; /* unit to channel struct pointer */ + +static unsigned short irq_valid_values [] = { 3, 5, 7, 10, 11, 12, 15, 0 }; +static unsigned short drq_valid_values [] = { 5, 6, 7, 0 }; +static unsigned short port_valid_values [] = { + 0x240, 0x260, 0x280, 0x300, 0x320, 0x380, 0x3a0, 0, +}; + +#if __FreeBSD__ >= 2 +static char cxdescription [80]; +struct kern_devconf kdc_cx [NCX] = { { + 0, 0, 0, "cx", 0, { MDDT_ISA, 0, "net" }, + isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, 0, + DC_IDLE, cxdescription, +} }; +#endif + +/* + * Check that the value is contained in the list of correct values. + */ +static int valid (unsigned short value, unsigned short *list) +{ + while (*list) + if (value == *list++) + return (1); + return (0); +} + +/* + * Print the mbuf chain, for debug purposes only. + */ +static void printmbuf (struct mbuf *m) +{ + printf ("mbuf:"); + for (; m; m=m->m_next) { + if (m->m_flags & M_PKTHDR) + printf (" HDR %d:", m->m_pkthdr.len); + if (m->m_flags & M_EXT) + printf (" EXT:"); + printf (" %d", m->m_len); + } + printf ("\n"); +} + +/* + * Make an mbuf from data. + */ +static struct mbuf *makembuf (void *buf, unsigned len) +{ + struct mbuf *m, *o, *p; + + MGETHDR (m, M_DONTWAIT, MT_DATA); + if (! m) + return (0); + if (len >= MINCLSIZE) + MCLGET (m, M_DONTWAIT); + m->m_pkthdr.len = len; + m->m_len = 0; + + p = m; + while (len) { + unsigned n = M_TRAILINGSPACE (p); + if (n > len) + n = len; + + if (! n) { + /* Allocate new mbuf. */ + o = p; + MGET (p, M_DONTWAIT, MT_DATA); + if (! p) { + m_freem (m); + return (0); + } + if (len >= MINCLSIZE) + MCLGET (p, M_DONTWAIT); + p->m_len = 0; + o->m_next = p; + + n = M_TRAILINGSPACE (p); + if (n > len) + n = len; + } + + bcopy (buf, mtod (p, caddr_t) + p->m_len, n); + + p->m_len += n; + buf += n; + len -= n; + } + return (m); +} + +/* + * Test the presence of the adapter on the given i/o port. + */ +#ifdef __FreeBSD__ +int cxprobe (struct isa_device *id) +{ + int unit = id->id_unit; + int iobase = id->id_iobase; + int irq = id->id_irq; + int drq = id->id_drq; + int irqnum; +#endif +#ifdef __bsdi__ +int cxprobe (struct device *parent, struct cfdata *cf, void *aux) +{ + int unit = cf->cf_unit; + int iobase = ((struct isa_attach_args*)aux)->ia_iobase; + int irq = ((struct isa_attach_args*)aux)->ia_irq; + int drq = ((struct isa_attach_args*)aux)->ia_drq; + int irqnum, i; + + for (i=0; i<NCX; ++i) + if (i != unit && cxboard[i].port == iobase) + return (0); + if (irq == IRQUNK) { + irq = isa_irqalloc (IRQ3|IRQ5|IRQ7|IRQ10|IRQ11|IRQ12|IRQ15); + if (! irq) + return (0); + ((struct isa_attach_args*)aux)->ia_irq = irq; + } +#endif + irqnum = ffs (irq) - 1; + + print (("cx%d: probe iobase=0x%x irq=%d drq=%d\n", + unit, iobase, irqnum, drq)); + if (! valid (irqnum, irq_valid_values)) { + printf ("cx%d: Incorrect IRQ: %d\n", unit, irqnum); + return (0); + } + if (! valid (iobase, port_valid_values)) { + printf ("cx%d: Incorrect port address: 0x%x\n", unit, iobase); + return (0); + } + if (! valid (drq, drq_valid_values)) { + printf ("cx%d: Incorrect DMA channel: %d\n", unit, drq); + return (0); + } + if (! cx_probe_board (iobase)) + return (0); + return (1); +} + +/* + * The adapter is present, initialize the driver structures. + */ +#ifdef __FreeBSD__ +int cxattach (struct isa_device *id) +{ + int unit = id->id_unit; + int iobase = id->id_iobase; + int irq = id->id_irq; + int drq = id->id_drq; +#endif +#ifdef __bsdi__ +void cxattach (struct device *parent, struct device *self, void *aux) +{ + int unit = self->dv_unit; + int iobase = ((struct isa_attach_args*)aux)->ia_iobase; + int irq = ((struct isa_attach_args*)aux)->ia_irq; + int drq = ((struct isa_attach_args*)aux)->ia_drq; + struct cxsoftc *sc = (struct cxsoftc*) self; + void cxintr (cx_board_t *b); +#endif + cx_board_t *b = cxboard + unit; + int i; + + /* Initialize the board structure. */ + cx_init (b, unit, iobase, ffs(irq)-1, drq); + + for (i=0; i<NCHAN; ++i) { + cx_chan_t *c = b->chan + i; + int u = b->num*NCHAN + i; + cxchan[u] = c; + + if (c->type == T_NONE) + continue; + + /* Allocate the buffer memory. */ + c->arbuf = malloc (DMABUFSZ, M_DEVBUF, M_NOWAIT); + c->brbuf = malloc (DMABUFSZ, M_DEVBUF, M_NOWAIT); + c->atbuf = malloc (DMABUFSZ, M_DEVBUF, M_NOWAIT); + c->btbuf = malloc (DMABUFSZ, M_DEVBUF, M_NOWAIT); + + /* All buffers should be located in lower 16M of memory! */ + if (!c->arbuf || !c->brbuf || !c->atbuf || !c->btbuf) { + printf ("cx%d.%d: No memory for channel buffers\n", + c->board->num, c->num); + c->type = T_NONE; + } + + switch (c->type) { + case T_SYNC_RS232: + case T_SYNC_V35: + case T_SYNC_RS449: + case T_UNIV_RS232: + case T_UNIV_RS449: + case T_UNIV_V35: + c->ifp = malloc (IFSTRUCTSZ, M_DEVBUF, M_NOWAIT); + if (! c->ifp) { + printf ("cx%d.%d: No memory for ifnet buffer\n", + c->board->num, c->num); + c->type = T_NONE; + continue; + } + c->ifp->if_unit = u; + c->ifp->if_name = "cx"; + c->ifp->if_mtu = PP_MTU; + c->ifp->if_flags = IFF_POINTOPOINT|IFF_MULTICAST; + c->ifp->if_ioctl = cxsioctl; + c->ifp->if_start = (start_func_t) cxstart; + c->ifp->if_watchdog = (watchdog_func_t) cxwatchdog; + /* Init routine is never called by upper level? */ + c->ifp->if_init = (init_func_t) cxinit; + sppp_attach (c->ifp); + if_attach (c->ifp); +#if NBPFILTER > 0 + /* If BPF is in the kernel, call the attach for it. */ + bpfattach (&c->bpf, c->ifp, DLT_PPP, PPP_HEADER_LEN); +#endif + } + } + + /* Reset the adapter. */ + cx_setup_board (b); + + /* Activate the timeout routine. */ + if (unit == 0) + timeout ((timeout_func_t) cxtimeout, 0, hz*5); + +#if __FreeBSD__ >= 2 + if (unit != 0) + kdc_cx[unit] = kdc_cx[0]; + kdc_cx[unit].kdc_unit = unit; + kdc_cx[unit].kdc_isa = id; + sprintf (cxdescription, "Cronyx-Sigma-%s sync/async serial adapter", + b->name); + dev_attach (&kdc_cx[unit]); +#endif +#ifdef __FreeBSD__ + printf ("cx%d: <Cronyx-%s>\n", unit, b->name); + return (1); +#endif +#ifdef __bsdi__ + printf (": <Cronyx-%s>\n", b->name); + isa_establish (&sc->isadev, &sc->dev); + sc->intr.ih_fun = (int(*)()) cxintr; + sc->intr.ih_arg = (void*) b; + intr_establish (irq, &sc->intr, DV_NET); +#endif +} + +#ifdef __FreeBSD__ +struct isa_driver cxdriver = { cxprobe, cxattach, "cx" }; +#endif +#ifdef __bsdi__ +struct cfdriver cxcd = { 0, "cx", cxprobe, cxattach, sizeof (struct cxsoftc) }; +#endif + +/* + * Process an ioctl request. + */ +int cxsioctl (struct ifnet *ifp, int cmd, caddr_t data) +{ + cx_chan_t *c = cxchan[ifp->if_unit]; + int error, s; + + if (c->type==T_NONE || c->mode==M_ASYNC) + return (EINVAL); +#ifdef __bsdi__ + if (c->sopt.ext) { + /* Save RUNNING flag. */ + int running = (ifp->if_flags & IFF_RUNNING); + error = p2p_ioctl (ifp, cmd, data); + ifp->if_flags &= ~IFF_RUNNING; + ifp->if_flags |= running; + } else +#endif + error = sppp_ioctl (ifp, cmd, data); + if (error) + return (error); + if (cmd != SIOCSIFFLAGS) + return (0); + + s = splimp (); + if (! (ifp->if_flags & IFF_UP) && (ifp->if_flags & IFF_RUNNING)) + /* Interface is down and running -- stop it. */ + cxinit (ifp->if_unit); + else if ((ifp->if_flags & IFF_UP) && ! (ifp->if_flags & IFF_RUNNING)) + /* Interface is up and not running -- start it. */ + cxinit (ifp->if_unit); + splx (s); + return (0); +} + +/* + * Initialization of interface. + */ +void cxinit (int unit) +{ + cx_chan_t *c = cxchan[unit]; + unsigned short port = c->chip->port; + int s; + + print (("cx%d.%d: cxinit\n", c->board->num, c->num)); + + /* Disable interrupts */ + s = splimp(); + + /* Reset the channel (for sync modes only) */ + if (c->ifp->if_flags & IFF_OACTIVE) { + outb (CAR(port), c->num & 3); + outb (STCR(port), STC_ABORTTX | STC_SNDSPC); + } + cx_setup_chan (c); + c->ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + if (c->ifp->if_flags & IFF_UP) { + /* The interface is up, start it */ + c->ifp->if_flags |= IFF_RUNNING; + +#if __FreeBSD__ >= 2 + /* Mark the board busy on the first startup. + * Never goes idle. */ + kdc_cx[c->board->num].kdc_state = DC_BUSY; +#endif + /* Initialize channel, enable receiver and transmitter */ + cx_cmd (port, CCR_INITCH | CCR_ENRX | CCR_ENTX); + /* Repeat the command, to avoid the rev.H bug */ + cx_cmd (port, CCR_INITCH | CCR_ENRX | CCR_ENTX); + + /* Start receiver */ + outw (ARBCNT(port), DMABUFSZ); + outb (ARBSTS(port), BSTS_OWN24); + outw (BRBCNT(port), DMABUFSZ); + outb (BRBSTS(port), BSTS_OWN24); + + /* Raise DTR and RTS */ + cx_chan_dtr (c, 1); + cx_chan_rts (c, 1); + + /* Enable interrupts */ + outb (IER(port), IER_RXD | IER_TXD); + + cxstart (c->ifp); + + } else if (! c->sopt.ext) + /* Flush the interface output queue, if it is down */ + sppp_flush (c->ifp); + + splx (s); +} + +/* + * Fill transmitter buffer with data. + */ +void cxput (cx_chan_t *c, char b) +{ + struct mbuf *m; + unsigned char *buf; + unsigned short port = c->chip->port, len, cnt_port, sts_port; + + /* Choose the buffer. */ + if (b == 'A') { + buf = c->atbuf; + cnt_port = ATBCNT(port); + sts_port = ATBSTS(port); + } else { + buf = c->btbuf; + cnt_port = BTBCNT(port); + sts_port = BTBSTS(port); + } + + /* Is it busy? */ + if (inb (sts_port) & BSTS_OWN24) { + c->ifp->if_flags |= IFF_OACTIVE; + return; + } + + /* Get the packet to send. */ +#ifdef __bsdi__ + if (c->sopt.ext) { + struct p2pcom *p = (struct p2pcom*) c->ifp; + int s = splimp (); + + IF_DEQUEUE (&p->p2p_isnd, m) + if (! m) + IF_DEQUEUE (&c->ifp->if_snd, m) + splx (s); + } else +#endif + m = sppp_dequeue (c->ifp); + if (! m) + return; + len = m->m_pkthdr.len; + if (len >= DMABUFSZ) { + printf ("cx%d.%d: too long packet: %d bytes\n", + c->board->num, c->num, len); + printmbuf (m); + m_freem (m); + return; + } + m_copydata (m, 0, len, buf); +#if NBPFILTER > 0 + if (c->bpf) + bpf_mtap (c->bpf, m); +#endif + m_freem (m); + + /* Start transmitter. */ + outw (cnt_port, len); + outb (sts_port, BSTS_EOFR | BSTS_INTR | BSTS_OWN24); +#ifdef DEBUG + if (c->ifp->if_flags & IFF_DEBUG) + printf ("cx%d.%d: enqueue %d bytes to %c\n", + c->board->num, c->num, len, buf==c->atbuf ? 'A' : 'B'); +#endif + c->ifp->if_flags |= IFF_OACTIVE; +} + +/* + * Start output on interface. Get another datagram to send + * off of the interface queue, and copy it to the interface + * before starting the output. + */ +void cxstart (struct ifnet *ifp) +{ + cx_chan_t *c = cxchan[ifp->if_unit]; + unsigned short port = c->chip->port; + + /* No output if the interface is down. */ + if (! (ifp->if_flags & IFF_RUNNING)) + return; + + /* Set the current channel number. */ + outb (CAR(port), c->num & 3); + + /* Determine the buffer order. */ + if (inb (DMABSTS(port)) & DMABSTS_NTBUF) { + cxput (c, 'B'); + cxput (c, 'A'); + } else { + cxput (c, 'A'); + cxput (c, 'B'); + } + + /* Set up transmit timeout. */ + if (c->ifp->if_flags & IFF_OACTIVE) + c->ifp->if_timer = TXTIMEOUT; + + /* + * Enable TXMPTY interrupt, + * to catch the case when the second buffer is empty. + */ + if ((inb (ATBSTS(port)) & BSTS_OWN24) && + (inb (BTBSTS(port)) & BSTS_OWN24)) { + outb (IER(port), IER_RXD | IER_TXD | IER_TXMPTY); + } else + outb (IER(port), IER_RXD | IER_TXD); +} + +/* + * Handle transmit timeouts. + * Recover after lost transmit interrupts. + */ +void cxwatchdog (int unit) +{ + cx_chan_t *c = cxchan[unit]; + + if (c->ifp->if_flags & IFF_OACTIVE) { + c->ifp->if_flags &= ~IFF_OACTIVE; + cxstart (c->ifp); + } +} + +/* + * Handle receive interrupts, including receive errors and + * receive timeout interrupt. + */ +void cxrinth (cx_chan_t *c) +{ + unsigned short port = c->chip->port; + unsigned short len, risr = inw (RISR(port)); + + print (("cx%d.%d: hdlc receive interrupt, risr=%b, arbsts=%b, brbsts=%b\n", + c->board->num, c->num, risr, RISH_BITS, + inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS)); + + /* Receive errors. */ + if (risr & (RIS_BUSERR | RIS_OVERRUN | RISH_CRCERR | RISH_RXABORT)) { + if (c->ifp->if_flags & IFF_DEBUG) + printf ("cx%d.%d: receive error, risr=%b\n", + c->board->num, c->num, risr, RISH_BITS); + ++c->ifp->if_ierrors; + if (risr & RIS_OVERRUN) + ++c->ifp->if_collisions; + } else if (risr & RIS_EOBUF) { + /* Handle received data. */ + len = (risr & RIS_BB) ? inw(BRBCNT(port)) : inw(ARBCNT(port)); + if (len > DMABUFSZ) { + /* Fatal error: actual DMA transfer size + * exceeds our buffer size. It could be caused + * by incorrectly programmed DMA register or + * hardware fault. Possibly, should panic here. */ + printf ("cx%d.%d: panic! DMA buffer overflow: %d bytes\n", + c->board->num, c->num, len); + ++c->ifp->if_ierrors; + } else if (! (risr & RIS_EOFR)) { + /* The received frame does not fit in the DMA buffer. + * It could be caused by serial lie noise, + * or if the peer has too big MTU. */ + if (c->ifp->if_flags & IFF_DEBUG) + printf ("cx%d.%d: received frame length exceeds MTU, risr=%b\n", + c->board->num, c->num, risr, RISH_BITS); + ++c->ifp->if_ierrors; + } else { + /* Valid frame received. */ + print (("cx%d.%d: HDLC: %d bytes received\n", + c->board->num, c->num, len)); + cxinput (c, (risr & RIS_BB) ? c->brbuf : c->arbuf, len); + ++c->ifp->if_ipackets; + } + } + + /* Restart receiver. */ + if (! (inb (ARBSTS(port)) & BSTS_OWN24)) { + outw (ARBCNT(port), DMABUFSZ); + outb (ARBSTS(port), BSTS_OWN24); + } + if (! (inb (BRBSTS(port)) & BSTS_OWN24)) { + outw (BRBCNT(port), DMABUFSZ); + outb (BRBSTS(port), BSTS_OWN24); + } +} + +/* + * Handle transmit interrupt. + */ +int cxtinth (cx_chan_t *c) +{ + unsigned short port = c->chip->port; + unsigned char tisr = inb (TISR(port)); + unsigned char teoir = 0; + + print (("cx%d.%d: hdlc transmit interrupt, tisr=%b, atbsts=%b, btbsts=%b\n", + c->board->num, c->num, tisr, TIS_BITS, + inb (ATBSTS(port)), BSTS_BITS, inb (BTBSTS(port)), BSTS_BITS)); + + if (tisr & (TIS_BUSERR | TIS_UNDERRUN)) { + if (c->ifp->if_flags & IFF_DEBUG) + printf ("cx%d.%d: transmit error, tisr=%b\n", + c->board->num, c->num, tisr, TIS_BITS); + ++c->ifp->if_oerrors; + /* Terminate the failed buffer. */ + teoir |= TEOI_TERMBUFF; + } else if (tisr & (TIS_EOFR)) + ++c->ifp->if_opackets; + + c->ifp->if_flags &= ~IFF_OACTIVE; + cxstart (c->ifp); + return (teoir); +} + +#ifdef __FreeBSD__ +void cxintr (int bnum) +{ + cx_board_t *b = cxboard + bnum; +#endif +#ifdef __bsdi__ +void cxintr (cx_board_t *b) +{ +#endif + while (! (inw (BSR(b->port)) & BSR_NOINTR)) { + /* Acknowledge the interrupt to enter the interrupt context. */ + /* Read the local interrupt vector register. */ + unsigned char livr = inb (IACK(b->port, BRD_INTR_LEVEL)); + cx_chan_t *c = b->chan + (livr>>2 & 0xf); + unsigned short port = c->chip->port; + unsigned short eoiport = REOIR(port); + unsigned char eoi = 0; + + if (c->type == T_NONE) { + printf ("cx%d.%d: unexpected interrupt, livr=0x%x\n", + c->board->num, c->num, livr); + continue; /* incorrect channel number? */ + } + /* print (("cx%d.%d: interrupt, livr=0x%x\n", + c->board->num, c->num, livr)); */ + + /* Clear RTS to stop receiver data flow while we are busy + * processing the interrupt, thus avoiding underruns. */ + if (! c->sopt.norts) { + outb (MSVR_RTS(port), 0); + c->rts = 0; + } + + switch (livr & 3) { + case LIV_EXCEP: /* receive exception */ + case LIV_RXDATA: /* receive interrupt */ + switch (c->mode) { + case M_ASYNC: eoi = cxrinta (c); break; + case M_HDLC: cxrinth (c); break; + default:; /* No bisync and X.21 yet */ + } + break; + case LIV_TXDATA: /* transmit interrupt */ + eoiport = TEOIR(port); + switch (c->mode) { + case M_ASYNC: cxtinta (c); break; + case M_HDLC: eoi = cxtinth (c); break; + default:; /* No bisync and X.21 yet */ + } + break; + case LIV_MODEM: /* modem/timer interrupt */ + eoiport = MEOIR(port); + cxmint (c); + break; + } + + /* Raise RTS for this channel if and only if + * both receive buffers are empty. */ + if (! c->sopt.norts && (inb (CSR(port)) & CSRA_RXEN) && + (inb (ARBSTS(port)) & BSTS_OWN24) && + (inb (BRBSTS(port)) & BSTS_OWN24)) { + outb (MSVR_RTS(port), MSV_RTS); + c->rts = 1; + } + + /* Exit from interrupt context. */ + outb (eoiport, eoi); + } +} + +/* + * Process the received packet. + */ +void cxinput (cx_chan_t *c, void *buf, unsigned len) +{ + /* Make an mbuf. */ + struct mbuf *m = makembuf (buf, len); + if (! m) { + if (c->ifp->if_flags & IFF_DEBUG) + printf ("cx%d.%d: no memory for packet\n", + c->board->num, c->num); + ++c->ifp->if_iqdrops; + return; + } + m->m_pkthdr.rcvif = c->ifp; +#ifdef DEBUG + printmbuf (m); +#endif + +#if NBPFILTER > 0 + /* + * Check if there's a BPF listener on this interface. + * If so, hand off the raw packet to bpf. + */ + if (c->bpf) + bpf_tap (c->bpf, buf, len); +#endif +#ifdef __bsdi__ + if (c->sopt.ext) { + struct p2pcom *p = (struct p2pcom*) c->ifp; + (*p->p2p_input) (p, m); + } else +#endif + sppp_input (c->ifp, m); +} + +void cxswitch (cx_chan_t *c, cx_soft_opt_t new) +{ +#ifdef __bsdi__ + if (new.ext && ! c->sopt.ext) { + /* Switch to external ppp implementation (BSDI) */ + sppp_detach (c->ifp); + bzero ((void*) c->ifp + IFNETSZ, IFSTRUCTSZ-IFNETSZ); + } else if (! new.ext && c->sopt.ext) { + /* Switch to built-in ppp implementation */ + bzero ((void*) c->ifp + IFNETSZ, IFSTRUCTSZ-IFNETSZ); + sppp_attach (c->ifp); + } +#else + new.ext = 0; +#endif + if (! new.ext) { + struct sppp *sp = (struct sppp*) c->ifp; + + if (new.cisco) + sp->pp_flags |= PP_CISCO; + else + sp->pp_flags &= ~PP_CISCO; + if (new.keepalive) + sp->pp_flags |= PP_KEEPALIVE; + else + sp->pp_flags &= ~PP_KEEPALIVE; + } + c->sopt = new; +} +#endif /* NCX */ diff --git a/sys/net/if_sppp.h b/sys/net/if_sppp.h new file mode 100644 index 0000000..c915977 --- /dev/null +++ b/sys/net/if_sppp.h @@ -0,0 +1,70 @@ +/* + * Defines for synchronous PPP/Cisco link level subroutines. + * + * Copyright (C) 1994 Cronyx Ltd. + * Author: Serge Vakulenko, <vak@zebub.msk.su> + * + * This software is distributed with NO WARRANTIES, not even the implied + * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Authors grant any other persons or organisations permission to use + * or modify this software as long as this message is kept with the software, + * all derivative works or modified versions. + * + * Version 1.1, Thu Oct 27 21:15:02 MSK 1994 + */ + +#ifndef _NET_IF_HDLC_H_ +#define _NET_IF_HDLC_H_ 1 + +struct slcp { + u_short state; /* state machine */ + u_long magic; /* local magic number */ + u_long rmagic; /* remote magic number */ + u_char lastid; /* id of last keepalive echo request */ +}; + +struct sipcp { + u_short state; /* state machine */ +}; + +struct sppp { + struct ifnet pp_if; /* network interface data */ + struct ifqueue pp_fastq; /* fast output queue */ + struct sppp *pp_next; /* next interface in keepalive list */ + u_int pp_flags; /* use Cisco protocol instead of PPP */ + u_short pp_alivecnt; /* keepalive packets counter */ + u_short pp_loopcnt; /* loopback detection counter */ + u_long pp_seq; /* local sequence number */ + u_long pp_rseq; /* remote sequence number */ + struct slcp lcp; /* LCP params */ + struct sipcp ipcp; /* IPCP params */ +}; + +#define PP_KEEPALIVE 0x01 /* use keepalive protocol */ +#define PP_CISCO 0x02 /* use Cisco protocol instead of PPP */ + +#define PP_MTU 1500 /* max. transmit unit */ + +#define LCP_STATE_CLOSED 0 /* LCP state: closed (conf-req sent) */ +#define LCP_STATE_ACK_RCVD 1 /* LCP state: conf-ack received */ +#define LCP_STATE_ACK_SENT 2 /* LCP state: conf-ack sent */ +#define LCP_STATE_OPENED 3 /* LCP state: opened */ + +#define IPCP_STATE_CLOSED 0 /* IPCP state: closed (conf-req sent) */ +#define IPCP_STATE_ACK_RCVD 1 /* IPCP state: conf-ack received */ +#define IPCP_STATE_ACK_SENT 2 /* IPCP state: conf-ack sent */ +#define IPCP_STATE_OPENED 3 /* IPCP state: opened */ + +#ifdef KERNEL +void sppp_attach (struct ifnet *ifp); +void sppp_detach (struct ifnet *ifp); +void sppp_input (struct ifnet *ifp, struct mbuf *m); +int sppp_output (struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, + struct rtentry *rt); +int sppp_ioctl (struct ifnet *ifp, int cmd, caddr_t data); +struct mbuf *sppp_dequeue (struct ifnet *ifp); +void sppp_flush (struct ifnet *ifp); +#endif + +#endif /* _NET_IF_HDLC_H_ */ diff --git a/sys/net/if_spppsubr.c b/sys/net/if_spppsubr.c new file mode 100644 index 0000000..8c97231 --- /dev/null +++ b/sys/net/if_spppsubr.c @@ -0,0 +1,1219 @@ +/* + * Synchronous PPP/Cisco link level subroutines. + * Keepalive protocol implemented in both Cisco and PPP modes. + * + * Copyright (C) 1994 Cronyx Ltd. + * Author: Serge Vakulenko, <vak@zebub.msk.su> + * + * This software is distributed with NO WARRANTIES, not even the implied + * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Authors grant any other persons or organisations permission to use + * or modify this software as long as this message is kept with the software, + * all derivative works or modified versions. + * + * Version 1.1, Thu Oct 27 21:13:59 MSK 1994 + */ +#undef DEBUG + +#include <sys/param.h> +#include <systm.h> +#include <kernel.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/mbuf.h> + +#include <net/if.h> +#include <net/netisr.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/if_ether.h> +#endif + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#ifdef ISO +#include <netiso/argo_debug.h> +#include <netiso/iso.h> +#include <netiso/iso_var.h> +#include <netiso/iso_snpac.h> +#endif + +#include <net/if_sppp.h> + +#ifdef DEBUG +#define print(s) printf s +#else +#define print(s) /*void*/ +#endif + +#define MAXALIVECNT 3 /* max. alive packets */ + +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_IP 0x0021 /* Internet Protocol */ +#define PPP_ISO 0x0023 /* ISO OSI Protocol */ +#define PPP_XNS 0x0025 /* Xerox NS Protocol */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ +#define PPP_IPCP 0x8021 /* Internet Protocol Control Protocol */ + +#define LCP_CONF_REQ 1 /* PPP LCP configure request */ +#define LCP_CONF_ACK 2 /* PPP LCP configure acknowledge */ +#define LCP_CONF_NAK 3 /* PPP LCP configure negative ack */ +#define LCP_CONF_REJ 4 /* PPP LCP configure reject */ +#define LCP_TERM_REQ 5 /* PPP LCP terminate request */ +#define LCP_TERM_ACK 6 /* PPP LCP terminate acknowledge */ +#define LCP_CODE_REJ 7 /* PPP LCP code reject */ +#define LCP_PROTO_REJ 8 /* PPP LCP protocol reject */ +#define LCP_ECHO_REQ 9 /* PPP LCP echo request */ +#define LCP_ECHO_REPLY 10 /* PPP LCP echo reply */ +#define LCP_DISC_REQ 11 /* PPP LCP discard request */ + +#define LCP_OPT_MRU 1 /* maximum receive unit */ +#define LCP_OPT_ASYNC_MAP 2 /* async control character map */ +#define LCP_OPT_AUTH_PROTO 3 /* authentication protocol */ +#define LCP_OPT_QUAL_PROTO 4 /* quality protocol */ +#define LCP_OPT_MAGIC 5 /* magic number */ +#define LCP_OPT_RESERVED 6 /* reserved */ +#define LCP_OPT_PROTO_COMP 7 /* protocol field compression */ +#define LCP_OPT_ADDR_COMP 8 /* address/control field compression */ + +#define IPCP_CONF_REQ LCP_CONF_REQ /* PPP IPCP configure request */ +#define IPCP_CONF_ACK LCP_CONF_ACK /* PPP IPCP configure acknowledge */ +#define IPCP_CONF_NAK LCP_CONF_NAK /* PPP IPCP configure negative ack */ +#define IPCP_CONF_REJ LCP_CONF_REJ /* PPP IPCP configure reject */ +#define IPCP_TERM_REQ LCP_TERM_REQ /* PPP IPCP terminate request */ +#define IPCP_TERM_ACK LCP_TERM_ACK /* PPP IPCP terminate acknowledge */ +#define IPCP_CODE_REJ LCP_CODE_REJ /* PPP IPCP code reject */ + +#define CISCO_MULTICAST 0x8f /* Cisco multicast address */ +#define CISCO_UNICAST 0x0f /* Cisco unicast address */ +#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */ +#define CISCO_ADDR_REQ 0 /* Cisco address request */ +#define CISCO_ADDR_REPLY 1 /* Cisco address reply */ +#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */ + +struct ppp_header { + unsigned char address; + unsigned char control; + unsigned short protocol; +}; +#define PPP_HEADER_LEN sizeof (struct ppp_header) + +struct lcp_header { + unsigned char type; + unsigned char ident; + unsigned short len; +}; +#define LCP_HEADER_LEN sizeof (struct lcp_header) + +struct cisco_packet { + unsigned long type; + unsigned long par1; + unsigned long par2; + unsigned short rel; + unsigned short time0; + unsigned short time1; +}; +#define CISCO_PACKET_LEN 18 + +struct sppp *spppq; + +extern void if_down (struct ifnet *ifp); + +/* + * The following disgusting hack gets around the problem that IP TOS + * can't be set yet. We want to put "interactive" traffic on a high + * priority queue. To decide if traffic is interactive, we check that + * a) it is TCP and b) one of its ports is telnet, rlogin or ftp control. + */ +static unsigned short interactive_ports[8] = { + 0, 513, 0, 0, + 0, 21, 0, 23, +}; +#define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p)) + +void sppp_keepalive (caddr_t dummy1, int dummy2); +void sppp_cp_send (struct sppp *sp, unsigned short proto, unsigned char type, + unsigned char ident, unsigned short len, void *data); +void sppp_cisco_send (struct sppp *sp, int type, long par1, long par2); +void sppp_lcp_input (struct sppp *sp, struct mbuf *m); +void sppp_cisco_input (struct sppp *sp, struct mbuf *m); +void sppp_lcp_conf_rej (struct sppp *sp, struct lcp_header *h); +void sppp_ipcp_input (struct sppp *sp, struct mbuf *m); +void sppp_lcp_open (struct sppp *sp); +void sppp_ipcp_open (struct sppp *sp); +int sppp_lcp_conf_unknown_options (int len, unsigned char *p); +void sppp_cp_timeout (caddr_t arg, int dummy); +char *sppp_lcp_type_name (unsigned char type); +char *sppp_ipcp_type_name (unsigned char type); +void sppp_print_bytes (unsigned char *p, unsigned short len); + +/* + * Flush interface queue. + */ +static void qflush (struct ifqueue *ifq) +{ + struct mbuf *m, *n; + + n = ifq->ifq_head; + while ((m = n)) { + n = m->m_act; + m_freem (m); + } + ifq->ifq_head = 0; + ifq->ifq_tail = 0; + ifq->ifq_len = 0; +} + +/* + * Process the received packet. + */ +void sppp_input (struct ifnet *ifp, struct mbuf *m) +{ + struct ppp_header *h; + struct sppp *sp; + struct ifqueue *inq = 0; + + ifp->if_lastchange = time; + if (ifp->if_flags & IFF_UP) + /* Count received bytes, add FCS and one flag */ + ifp->if_ibytes += m->m_pkthdr.len + 3; + + if (m->m_pkthdr.len <= PPP_HEADER_LEN) { + /* Too small packet, drop it. */ + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: input packet is too small, %d bytes\n", + ifp->if_name, ifp->if_unit, m->m_pkthdr.len); +drop: ++ifp->if_iqdrops; + m_freem (m); + return; + } + + /* Get PPP header. */ + h = mtod (m, struct ppp_header*); + m_adj (m, PPP_HEADER_LEN); + + switch (h->address) { + default: /* Invalid PPP packet. */ +invalid: if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: invalid input packet <0x%x 0x%x 0x%x>\n", + ifp->if_name, ifp->if_unit, + h->address, h->control, ntohs (h->protocol)); + goto drop; + case PPP_ALLSTATIONS: + if (h->control != PPP_UI) + goto invalid; + sp = (struct sppp*) ifp; + switch (ntohs (h->protocol)) { + default: + if (sp->lcp.state == LCP_STATE_OPENED) + sppp_cp_send (sp, PPP_LCP, LCP_PROTO_REJ, + ++sp->pp_seq, m->m_pkthdr.len - 2, + &h->protocol); + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: invalid input protocol <0x%x 0x%x 0x%x>\n", + ifp->if_name, ifp->if_unit, + h->address, h->control, ntohs (h->protocol)); + ++ifp->if_noproto; + goto drop; + case PPP_LCP: + sppp_lcp_input ((struct sppp*) ifp, m); + m_freem (m); + return; +#ifdef INET + case PPP_IPCP: + if (sp->lcp.state == LCP_STATE_OPENED) + sppp_ipcp_input ((struct sppp*) ifp, m); + m_freem (m); + return; + case PPP_IP: + if (sp->ipcp.state == IPCP_STATE_OPENED) { + schednetisr (NETISR_IP); + inq = &ipintrq; + } + break; +#endif +#ifdef NS + case PPP_XNS: + /* XNS IDPCP not implemented yet */ + if (sp->lcp.state == LCP_STATE_OPENED) { + schednetisr (NETISR_NS); + inq = &nsintrq; + } + break; +#endif +#ifdef ISO + case PPP_ISO: + /* OSI NLCP not implemented yet */ + if (sp->lcp.state == LCP_STATE_OPENED) { + schednetisr (NETISR_ISO); + inq = &clnlintrq; + } + break; +#endif + } + break; + case CISCO_MULTICAST: + case CISCO_UNICAST: + /* Don't check the control field here (RFC 1547). */ + switch (ntohs (h->protocol)) { + default: + ++ifp->if_noproto; + goto invalid; + case CISCO_KEEPALIVE: + sppp_cisco_input ((struct sppp*) ifp, m); + m_freem (m); + return; +#ifdef INET + case ETHERTYPE_IP: + schednetisr (NETISR_IP); + inq = &ipintrq; + break; +#endif +#ifdef NS + case ETHERTYPE_NS: + schednetisr (NETISR_NS); + inq = &nsintrq; + break; +#endif + } + break; + } + + if (! (ifp->if_flags & IFF_UP) || ! inq) + goto drop; + + /* Check queue. */ + if (IF_QFULL (inq)) { + /* Queue overflow. */ + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: protocol queue overflow\n", + ifp->if_name, ifp->if_unit); + IF_DROP (inq); + goto drop; + } + IF_ENQUEUE (inq, m); +} + +/* + * Enqueue transmit packet. + */ +int sppp_output (struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt) +{ + struct sppp *sp = (struct sppp*) ifp; + struct ppp_header *h; + struct ifqueue *ifq; + int s = splimp (); + + if (! (ifp->if_flags & IFF_UP) || ! (ifp->if_flags & IFF_RUNNING)) { + m_freem (m); + splx (s); + return (ENETDOWN); + } + + ifq = &ifp->if_snd; +#ifdef INET + /* + * Put low delay, telnet, rlogin and ftp control packets + * in front of the queue. + */ + { + struct ip *ip = mtod (m, struct ip*); + struct tcphdr *tcp = (struct tcphdr*) ((long*)ip + ip->ip_hl); + + if (! IF_QFULL (&sp->pp_fastq) && ((ip->ip_tos & IPTOS_LOWDELAY) || + ip->ip_p == IPPROTO_TCP && + m->m_len >= sizeof (struct ip) + sizeof (struct tcphdr) && + (INTERACTIVE (ntohs (tcp->th_sport)) || + INTERACTIVE (ntohs (tcp->th_dport))))) + ifq = &sp->pp_fastq; + } +#endif + + /* + * Prepend general data packet PPP header. For now, IP only. + */ + M_PREPEND (m, PPP_HEADER_LEN, M_DONTWAIT); + if (! m) { + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: no memory for transmit header\n", + ifp->if_name, ifp->if_unit); + splx (s); + return (ENOBUFS); + } + h = mtod (m, struct ppp_header*); + if (sp->pp_flags & PP_CISCO) { + h->address = CISCO_MULTICAST; /* broadcast address */ + h->control = 0; + } else { + h->address = PPP_ALLSTATIONS; /* broadcast address */ + h->control = PPP_UI; /* Unnumbered Info */ + } + + switch (dst->sa_family) { +#ifdef INET + case AF_INET: /* Internet Protocol */ + h->protocol = htons ((sp->pp_flags & PP_CISCO) ? + ETHERTYPE_IP : PPP_IP); + break; +#endif +#ifdef NS + case AF_NS: /* Xerox NS Protocol */ + h->protocol = htons ((sp->pp_flags & PP_CISCO) ? + ETHERTYPE_NS : PPP_XNS); + break; +#endif +#ifdef ISO + case AF_ISO: /* ISO OSI Protocol */ + if (sp->pp_flags & PP_CISCO) + goto nosupport; + h->protocol = htons (PPP_ISO); + break; +#endif + default: + m_freem (m); + splx (s); + return (EAFNOSUPPORT); + } + + /* + * Queue message on interface, and start output if interface + * not yet active. + */ + if (IF_QFULL (ifq)) { + IF_DROP (&ifp->if_snd); + m_freem (m); + splx (s); + return (ENOBUFS); + } + IF_ENQUEUE (ifq, m); + if (! (ifp->if_flags & IFF_OACTIVE)) + (*ifp->if_start) (ifp); + + /* + * Count output packets and bytes. + * The packet length includes header, FCS and 1 flag, + * according to RFC 1333. + */ + ifp->if_obytes += m->m_pkthdr.len + 3; + ifp->if_lastchange = time; + splx (s); + return (0); +} + +void sppp_attach (struct ifnet *ifp) +{ + struct sppp *sp = (struct sppp*) ifp; + + /* Initialize keepalive handler. */ + if (! spppq) + timeout (sppp_keepalive, 0, hz * 10); + + /* Insert new entry into the keepalive list. */ + sp->pp_next = spppq; + spppq = sp; + + sp->pp_if.if_type = IFT_PPP; + sp->pp_if.if_output = sppp_output; + sp->pp_fastq.ifq_maxlen = 32; + sp->pp_loopcnt = 0; + sp->pp_alivecnt = 0; + sp->lcp.magic = time.tv_sec + time.tv_usec; + sp->lcp.rmagic = 0; + sp->pp_seq = sp->lcp.magic; + sp->pp_rseq = 0; +} + +void sppp_detach (struct ifnet *ifp) +{ + struct sppp **q, *p, *sp = (struct sppp*) ifp; + + /* Remove the entry from the keepalive list. */ + for (q = &spppq; (p = *q); q = &p->pp_next) + if (p == sp) { + *q = p->pp_next; + break; + } + + /* Stop keepalive handler. */ + if (! spppq) + untimeout (sppp_keepalive, 0); + untimeout (sppp_cp_timeout, (caddr_t) sp); +} + +/* + * Flush the interface output queue. + */ +void sppp_flush (struct ifnet *ifp) +{ + struct sppp *sp = (struct sppp*) ifp; + + qflush (&sp->pp_if.if_snd); + qflush (&sp->pp_fastq); +} + +/* + * Get next packet to send. + */ +struct mbuf *sppp_dequeue (struct ifnet *ifp) +{ + struct sppp *sp = (struct sppp*) ifp; + struct mbuf *m; + int s = splimp (); + + IF_DEQUEUE (&sp->pp_fastq, m); + if (! m) + IF_DEQUEUE (&sp->pp_if.if_snd, m); + splx (s); + return (m); +} + +/* + * Send keepalive packets, every 10 seconds. + */ +void sppp_keepalive (caddr_t dummy1, int dummy2) +{ + struct sppp *sp; + int s = splimp (); + + for (sp=spppq; sp; sp=sp->pp_next) { + struct ifnet *ifp = &sp->pp_if; + + if (! (sp->pp_flags & PP_KEEPALIVE) || + ! (ifp->if_flags & IFF_RUNNING) || + sp->lcp.state != LCP_STATE_OPENED) + continue; + + if (sp->pp_alivecnt == MAXALIVECNT) { + /* No keepalive packets got. Stop the interface. */ + printf ("%s%d: down\n", ifp->if_name, ifp->if_unit); + if_down (ifp); + qflush (&sp->pp_fastq); + } + if (sp->pp_loopcnt >= MAXALIVECNT) + printf ("%s%d: loopback\n", ifp->if_name, ifp->if_unit); + + if (sp->pp_alivecnt <= MAXALIVECNT) + ++sp->pp_alivecnt; + if (sp->pp_flags & PP_CISCO) + sppp_cisco_send (sp, CISCO_KEEPALIVE_REQ, ++sp->pp_seq, + sp->pp_rseq); + else if (sp->lcp.state == LCP_STATE_OPENED) { + long nmagic = htonl (sp->lcp.magic); + sp->lcp.lastid = ++sp->pp_seq; + sppp_cp_send (sp, PPP_LCP, LCP_ECHO_REQ, + sp->lcp.lastid, 4, &nmagic); + } + } + splx (s); + timeout (sppp_keepalive, 0, hz * 10); +} + +/* + * Handle incoming PPP Link Control Protocol packets. + */ +void sppp_lcp_input (struct sppp *sp, struct mbuf *m) +{ + struct lcp_header *h; + struct ifnet *ifp = &sp->pp_if; + int len = m->m_pkthdr.len; + unsigned char *p; + + if (len < 4) { + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: invalid lcp packet length: %d bytes\n", + ifp->if_name, ifp->if_unit, len); + return; + } + h = mtod (m, struct lcp_header*); + if (ifp->if_flags & IFF_DEBUG) { + printf ("%s%d: lcp input: %d bytes <%s id=%xh len=%xh", + ifp->if_name, ifp->if_unit, len, + sppp_lcp_type_name (h->type), h->ident, ntohs (h->len)); + if (len > 4) + sppp_print_bytes ((unsigned char*) (h+1), len-4); + printf (">\n"); + } + if (len > ntohs (h->len)) + len = ntohs (h->len); + switch (h->type) { + default: + /* Unknown packet type -- send Code-Reject packet. */ + sppp_cp_send (sp, PPP_LCP, LCP_CODE_REJ, ++sp->pp_seq, len, h); + break; + case LCP_CONF_REQ: + if (len < 4) { + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: invalid lcp configure request packet length: %d bytes\n", + ifp->if_name, ifp->if_unit, len); + return; + } + if (len>4 && sppp_lcp_conf_unknown_options (len-4, (unsigned char*) (h+1))) { + sppp_lcp_conf_rej (sp, h); + if (sp->lcp.state == LCP_STATE_OPENED) + /* Initiate renegotiation. */ + sppp_lcp_open (sp); + if (sp->lcp.state != LCP_STATE_ACK_RCVD) { + /* Go to closed state. */ + sp->lcp.state = LCP_STATE_CLOSED; + sp->ipcp.state = IPCP_STATE_CLOSED; + } + } else { + /* Extract remote magic number. */ + p = (unsigned char*) (h+1); + if (len>=10 && p[0] == LCP_OPT_MAGIC && p[1] >= 4) + sp->lcp.rmagic = (unsigned long)p[2] << 24 | + (unsigned long)p[3] << 16 | + p[4] << 8 | p[5]; + if (sp->lcp.rmagic == sp->lcp.magic) { + /* Local and remote magics are equal -- loop? */ + sp->lcp.rmagic = ~sp->lcp.magic; + /* Send Configure-Nack packet. */ + p[2] = sp->lcp.rmagic >> 24; + p[3] = sp->lcp.rmagic >> 16; + p[4] = sp->lcp.rmagic >> 8; + p[5] = sp->lcp.rmagic; + sppp_cp_send (sp, PPP_LCP, LCP_CONF_NAK, + h->ident, len-4, h+1); + if (sp->lcp.state != LCP_STATE_ACK_RCVD) { + /* Go to closed state. */ + sp->lcp.state = LCP_STATE_CLOSED; + sp->ipcp.state = IPCP_STATE_CLOSED; + } + } else { + /* Send Configure-Ack packet. */ + sp->pp_loopcnt = 0; + sppp_cp_send (sp, PPP_LCP, LCP_CONF_ACK, + h->ident, len-4, h+1); + if (sp->lcp.state == LCP_STATE_OPENED) + /* Initiate renegotiation. */ + sppp_lcp_open (sp); + /* Change the state. */ + if (sp->lcp.state == LCP_STATE_ACK_RCVD) { + sp->lcp.state = LCP_STATE_OPENED; + sppp_ipcp_open (sp); + } else + sp->lcp.state = LCP_STATE_ACK_SENT; + } + } + break; + case LCP_CONF_ACK: + if (h->ident != sp->pp_seq) + return; + untimeout (sppp_cp_timeout, (caddr_t) sp); + switch (sp->lcp.state) { + case LCP_STATE_CLOSED: + sp->lcp.state = LCP_STATE_ACK_RCVD; + break; + case LCP_STATE_ACK_SENT: + sp->lcp.state = LCP_STATE_OPENED; + sppp_ipcp_open (sp); + break; + case LCP_STATE_ACK_RCVD: + case LCP_STATE_OPENED: + /* Initiate renegotiation. */ + sppp_lcp_open (sp); + /* Go to closed state. */ + sp->lcp.state = LCP_STATE_CLOSED; + sp->ipcp.state = IPCP_STATE_CLOSED; + break; + } + break; + case LCP_CONF_NAK: + if (h->ident != sp->pp_seq) + return; + p = (unsigned char*) (h+1); + if (len>=10 && p[0] == LCP_OPT_MAGIC && p[1] >= 4) { + sp->lcp.rmagic = (unsigned long)p[2] << 24 | + (unsigned long)p[3] << 16 | + p[4] << 8 | p[5]; + if (sp->lcp.rmagic == ~sp->lcp.magic) { + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: conf nak: magic glitch\n", + ifp->if_name, ifp->if_unit); + ++sp->pp_loopcnt; + sp->lcp.magic = time.tv_sec + time.tv_usec; + } + } + /* Fall through. */ + case LCP_CONF_REJ: + if (h->ident != sp->pp_seq) + return; + untimeout (sppp_cp_timeout, (caddr_t) sp); + /* Initiate renegotiation. */ + sppp_lcp_open (sp); + if (sp->lcp.state != LCP_STATE_ACK_SENT) { + /* Go to closed state. */ + sp->lcp.state = LCP_STATE_CLOSED; + sp->ipcp.state = IPCP_STATE_CLOSED; + } + break; + case LCP_TERM_REQ: + /* Send Terminate-Ack packet. */ + sppp_cp_send (sp, PPP_LCP, LCP_TERM_ACK, h->ident, 0, 0); + if (sp->lcp.state == LCP_STATE_OPENED) + /* Initiate renegotiation. */ + sppp_lcp_open (sp); + /* Go to closed state. */ + sp->lcp.state = LCP_STATE_CLOSED; + sp->ipcp.state = IPCP_STATE_CLOSED; + break; + case LCP_TERM_ACK: + if (h->ident != sp->pp_seq) + return; + if (sp->lcp.state == LCP_STATE_OPENED) + /* Initiate renegotiation. */ + sppp_lcp_open (sp); + if (sp->lcp.state != LCP_STATE_ACK_SENT) { + /* Go to closed state. */ + sp->lcp.state = LCP_STATE_CLOSED; + sp->ipcp.state = IPCP_STATE_CLOSED; + } + break; + case LCP_CODE_REJ: + case LCP_PROTO_REJ: + /* Ignore for now. */ + break; + case LCP_DISC_REQ: + /* Discard the packet. */ + break; + case LCP_ECHO_REQ: + if (len < 8) { + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: invalid lcp echo request packet length: %d bytes\n", + ifp->if_name, ifp->if_unit, len); + return; + } + if (ntohl (*(long*)(h+1)) == sp->lcp.magic) { + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: echo reply: magic glitch\n", + ifp->if_name, ifp->if_unit); + ++sp->pp_loopcnt; + } + *(long*)(h+1) = htonl (sp->lcp.magic); + sppp_cp_send (sp, PPP_LCP, LCP_ECHO_REPLY, h->ident, len-4, h+1); + break; + case LCP_ECHO_REPLY: + if (h->ident != sp->lcp.lastid) + return; + if (len < 8) { + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: invalid lcp echo reply packet length: %d bytes\n", + ifp->if_name, ifp->if_unit, len); + return; + } + if (ntohl (*(long*)(h+1)) == sp->lcp.magic) + return; + if (! (ifp->if_flags & IFF_UP) && + (ifp->if_flags & IFF_RUNNING)) { + ifp->if_flags |= IFF_UP; + printf ("%s%d: up\n", ifp->if_name, ifp->if_unit); + } + sp->pp_alivecnt = 0; + break; + } +} + +/* + * Handle incoming Cisco keepalive protocol packets. + */ +void sppp_cisco_input (struct sppp *sp, struct mbuf *m) +{ + struct cisco_packet *h; + struct ifaddr *ifa; + struct ifnet *ifp = &sp->pp_if; + + if (m->m_pkthdr.len != CISCO_PACKET_LEN) { + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: invalid cisco packet length: %d bytes\n", + ifp->if_name, ifp->if_unit, m->m_pkthdr.len); + return; + } + h = mtod (m, struct cisco_packet*); + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: cisco input: %d bytes <%xh %xh %xh %xh %xh-%xh>\n", + ifp->if_name, ifp->if_unit, m->m_pkthdr.len, + ntohl (h->type), h->par1, h->par2, h->rel, + h->time0, h->time1); + switch (ntohl (h->type)) { + default: + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: unknown cisco packet type: 0x%x\n", + ifp->if_name, ifp->if_unit, ntohl (h->type)); + break; + case CISCO_ADDR_REPLY: + /* Reply on address request, ignore */ + break; + case CISCO_KEEPALIVE_REQ: + if (! (ifp->if_flags & IFF_UP) && + (ifp->if_flags & IFF_RUNNING)) { + ifp->if_flags |= IFF_UP; + printf ("%s%d: up\n", ifp->if_name, ifp->if_unit); + } + sp->pp_alivecnt = 0; + sp->pp_rseq = ntohl (h->par1); + if (sp->pp_seq == sp->pp_rseq) { + /* Local and remote sequence numbers are equal. + * Probably, the line is in loopback mode. */ + ++sp->pp_loopcnt; + + /* Generate new local sequence number */ + sp->pp_seq ^= time.tv_sec ^ time.tv_usec; + } else + sp->pp_loopcnt = 0; + break; + case CISCO_ADDR_REQ: + for (ifa=ifp->if_addrlist; ifa; ifa=ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_INET) + break; + if (! ifa) { + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: unknown address for cisco request\n", + ifp->if_name, ifp->if_unit); + return; + } + sppp_cisco_send (sp, CISCO_ADDR_REPLY, + ntohl (((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr), + ntohl (((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr.s_addr)); + break; + } +} + +/* + * Send PPP LCP packet. + */ +void sppp_cp_send (struct sppp *sp, unsigned short proto, unsigned char type, + unsigned char ident, unsigned short len, void *data) +{ + struct ppp_header *h; + struct lcp_header *lh; + struct mbuf *m; + struct ifnet *ifp = &sp->pp_if; + + if (len > MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN) + len = MHLEN - PPP_HEADER_LEN - LCP_HEADER_LEN; + MGETHDR (m, M_DONTWAIT, MT_DATA); + if (! m) + return; + m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + LCP_HEADER_LEN + len; + m->m_pkthdr.rcvif = 0; + + h = mtod (m, struct ppp_header*); + h->address = PPP_ALLSTATIONS; /* broadcast address */ + h->control = PPP_UI; /* Unnumbered Info */ + h->protocol = htons (proto); /* Link Control Protocol */ + + lh = (struct lcp_header*) (h + 1); + lh->type = type; + lh->ident = ident; + lh->len = htons (LCP_HEADER_LEN + len); + if (len) + bcopy (data, lh+1, len); + + if (ifp->if_flags & IFF_DEBUG) { + printf ("%s%d: %s output <%s id=%xh len=%xh", + ifp->if_name, ifp->if_unit, + proto==PPP_LCP ? "lcp" : "ipcp", + proto==PPP_LCP ? sppp_lcp_type_name (lh->type) : + sppp_ipcp_type_name (lh->type), lh->ident, + ntohs (lh->len)); + if (len) + sppp_print_bytes ((unsigned char*) (lh+1), len); + printf (">\n"); + } + if (IF_QFULL (&sp->pp_fastq)) { + IF_DROP (&ifp->if_snd); + m_freem (m); + } else + IF_ENQUEUE (&sp->pp_fastq, m); + if (! (ifp->if_flags & IFF_OACTIVE)) + (*ifp->if_start) (ifp); + ifp->if_obytes += m->m_pkthdr.len + 3; +} + +/* + * Send Cisco keepalive packet. + */ +void sppp_cisco_send (struct sppp *sp, int type, long par1, long par2) +{ + struct ppp_header *h; + struct cisco_packet *ch; + struct mbuf *m; + struct ifnet *ifp = &sp->pp_if; + unsigned long t = (time.tv_sec - boottime.tv_sec) * 1000; + + MGETHDR (m, M_DONTWAIT, MT_DATA); + if (! m) + return; + m->m_pkthdr.len = m->m_len = PPP_HEADER_LEN + CISCO_PACKET_LEN; + m->m_pkthdr.rcvif = 0; + + h = mtod (m, struct ppp_header*); + h->address = CISCO_MULTICAST; + h->control = 0; + h->protocol = htons (CISCO_KEEPALIVE); + + ch = (struct cisco_packet*) (h + 1); + ch->type = htonl (type); + ch->par1 = htonl (par1); + ch->par2 = htonl (par2); + ch->rel = -1; + ch->time0 = htons ((unsigned short) (t >> 16)); + ch->time1 = htons ((unsigned short) t); + + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: cisco output: <%xh %xh %xh %xh %xh-%xh>\n", + ifp->if_name, ifp->if_unit, ntohl (ch->type), ch->par1, + ch->par2, ch->rel, ch->time0, ch->time1); + + if (IF_QFULL (&sp->pp_fastq)) { + IF_DROP (&ifp->if_snd); + m_freem (m); + } else + IF_ENQUEUE (&sp->pp_fastq, m); + if (! (ifp->if_flags & IFF_OACTIVE)) + (*ifp->if_start) (ifp); + ifp->if_obytes += m->m_pkthdr.len + 3; +} + +/* + * Process an ioctl request. Called on low priority level. + */ +int sppp_ioctl (struct ifnet *ifp, int cmd, caddr_t data) +{ + struct ifreq *ifr = (struct ifreq*) data; + struct sppp *sp; + int s; + + switch (cmd) { + default: + return (EINVAL); + + case SIOCSIFADDR: + case SIOCAIFADDR: + case SIOCSIFDSTADDR: + break; + + case SIOCSIFFLAGS: + s = splimp (); + if (! (ifp->if_flags & IFF_UP) && (ifp->if_flags & IFF_RUNNING)) { + /* Interface is stopping. */ + sp = (struct sppp*) ifp; + sp->lcp.state = LCP_STATE_CLOSED; + sp->ipcp.state = IPCP_STATE_CLOSED; + sppp_cp_send (sp, PPP_LCP, LCP_TERM_REQ, ++sp->pp_seq, + 0, 0); + } else if ((ifp->if_flags & IFF_UP) && ! (ifp->if_flags & IFF_RUNNING)) { + /* Interface is starting. */ + sp = (struct sppp*) ifp; + sp->lcp.state = LCP_STATE_CLOSED; + sp->ipcp.state = IPCP_STATE_CLOSED; + sppp_lcp_open (sp); + } + splx (s); + break; + +#ifdef SIOCSIFMTU +#ifndef ifr_mtu +#define ifr_mtu ifr_metric +#endif + case SIOCSIFMTU: + if (ifr->ifr_mtu < 128 || ifr->ifr_mtu > PP_MTU) + return (EINVAL); + ifp->if_mtu = ifr->ifr_mtu; + break; +#endif +#ifdef SLIOCSETMTU + case SLIOCSETMTU: + if (*(short*)data < 128 || *(short*)data > PP_MTU) + return (EINVAL); + ifp->if_mtu = *(short*)data; + break; +#endif +#ifdef SIOCGIFMTU + case SIOCGIFMTU: + ifr->ifr_mtu = ifp->if_mtu; + break; +#endif +#ifdef SLIOCGETMTU + case SLIOCGETMTU: + *(short*)data = ifp->if_mtu; + break; +#endif +#ifdef MULTICAST + case SIOCADDMULTI: + case SIOCDELMULTI: + break; +#endif + } + return (0); +} + +int sppp_lcp_conf_unknown_options (int len, unsigned char *p) +{ + /* Analyze the LCP Configure-Request options list + * for the presence of unknown options. */ + while (len > 0) { + if (*p != LCP_OPT_MAGIC) + return (1); + len -= p[1]; + p += p[1]; + } + return (0); +} + +void sppp_lcp_conf_rej (struct sppp *sp, struct lcp_header *h) +{ + /* The LCP Configure-Request contains unknown options. + * Send Configure-reject packet, containing only unknown options. */ + unsigned char buf [PP_MTU], *r = buf, *p = (void*) (h+1); + unsigned rlen = 0, len = h->len - 4; + + while (len > 0) { + if (*p != LCP_OPT_MAGIC) { + bcopy (p, r, p[1]); + r += p[1]; + } + len -= p[1]; + p += p[1]; + } + sppp_cp_send (sp, PPP_LCP, LCP_CONF_REJ, h->ident, rlen, buf); +} + +void sppp_ipcp_input (struct sppp *sp, struct mbuf *m) +{ + struct lcp_header *h; + struct ifnet *ifp = &sp->pp_if; + int len = m->m_pkthdr.len; + + if (len < 4) { + /* if (ifp->if_flags & IFF_DEBUG) */ + printf ("%s%d: invalid ipcp packet length: %d bytes\n", + ifp->if_name, ifp->if_unit, len); + return; + } + h = mtod (m, struct lcp_header*); + if (ifp->if_flags & IFF_DEBUG) { + printf ("%s%d: ipcp input: %d bytes <%s id=%xh len=%xh", + ifp->if_name, ifp->if_unit, len, + sppp_ipcp_type_name (h->type), h->ident, ntohs (h->len)); + if (len > 4) + sppp_print_bytes ((unsigned char*) (h+1), len-4); + printf (">\n"); + } + if (len > ntohs (h->len)) + len = ntohs (h->len); + switch (h->type) { + default: + /* Unknown packet type -- send Code-Reject packet. */ + sppp_cp_send (sp, PPP_IPCP, IPCP_CODE_REJ, ++sp->pp_seq, len, h); + break; + case IPCP_CONF_REQ: + if (len < 4) { + if (ifp->if_flags & IFF_DEBUG) + printf ("%s%d: invalid ipcp configure request packet length: %d bytes\n", + ifp->if_name, ifp->if_unit, len); + return; + } + if (len > 4) { + sppp_cp_send (sp, PPP_IPCP, LCP_CONF_REJ, h->ident, + len-4, h+1); + if (sp->lcp.state == LCP_STATE_OPENED && + sp->ipcp.state == IPCP_STATE_OPENED) + /* Initiate renegotiation. */ + sppp_ipcp_open (sp); + if (sp->ipcp.state != IPCP_STATE_ACK_RCVD) + /* Go to closed state. */ + sp->ipcp.state = IPCP_STATE_CLOSED; + } else { + /* Send Configure-Ack packet. */ + sppp_cp_send (sp, PPP_IPCP, IPCP_CONF_ACK, h->ident, + 0, 0); + if (sp->lcp.state == LCP_STATE_OPENED && + sp->ipcp.state == IPCP_STATE_OPENED) + /* Initiate renegotiation. */ + sppp_ipcp_open (sp); + /* Change the state. */ + sp->ipcp.state = (sp->ipcp.state == IPCP_STATE_ACK_RCVD) ? + IPCP_STATE_OPENED : IPCP_STATE_ACK_SENT; + } + break; + case IPCP_CONF_ACK: + untimeout (sppp_cp_timeout, (caddr_t) sp); + switch (sp->ipcp.state) { + case IPCP_STATE_CLOSED: + sp->ipcp.state = IPCP_STATE_ACK_RCVD; + break; + case IPCP_STATE_ACK_SENT: + sp->ipcp.state = IPCP_STATE_OPENED; + break; + case IPCP_STATE_ACK_RCVD: + case IPCP_STATE_OPENED: + if (sp->lcp.state == LCP_STATE_OPENED) + /* Initiate renegotiation. */ + sppp_ipcp_open (sp); + /* Go to closed state. */ + sp->ipcp.state = IPCP_STATE_CLOSED; + break; + } + break; + case IPCP_CONF_NAK: + case IPCP_CONF_REJ: + untimeout (sppp_cp_timeout, (caddr_t) sp); + /* Initiate renegotiation. */ + sppp_ipcp_open (sp); + if (sp->lcp.state == LCP_STATE_OPENED) + /* Initiate renegotiation. */ + sppp_ipcp_open (sp); + if (sp->ipcp.state != IPCP_STATE_ACK_SENT) + /* Go to closed state. */ + sp->ipcp.state = IPCP_STATE_CLOSED; + break; + case IPCP_TERM_REQ: + /* Send Terminate-Ack packet. */ + sppp_cp_send (sp, PPP_IPCP, IPCP_TERM_ACK, h->ident, 0, 0); + if (sp->lcp.state == LCP_STATE_OPENED && + sp->ipcp.state == IPCP_STATE_OPENED) + /* Initiate renegotiation. */ + sppp_ipcp_open (sp); + /* Go to closed state. */ + sp->ipcp.state = IPCP_STATE_CLOSED; + break; + case IPCP_TERM_ACK: + if (sp->lcp.state == LCP_STATE_OPENED && + sp->ipcp.state == IPCP_STATE_OPENED) + /* Initiate renegotiation. */ + sppp_ipcp_open (sp); + if (sp->ipcp.state != IPCP_STATE_ACK_SENT) + /* Go to closed state. */ + sp->ipcp.state = IPCP_STATE_CLOSED; + break; + case IPCP_CODE_REJ: + /* Ignore for now. */ + break; + } +} + +void sppp_lcp_open (struct sppp *sp) +{ + char opt[6]; + + /* Make new magic number. */ + sp->lcp.magic = time.tv_sec + time.tv_usec; + opt[0] = LCP_OPT_MAGIC; + opt[1] = sizeof (opt); + opt[2] = sp->lcp.magic >> 24; + opt[3] = sp->lcp.magic >> 16; + opt[4] = sp->lcp.magic >> 8; + opt[5] = sp->lcp.magic; + sppp_cp_send (sp, PPP_LCP, LCP_CONF_REQ, ++sp->pp_seq, + sizeof (opt), &opt); + timeout (sppp_cp_timeout, (caddr_t) sp, hz * 5); +} + +void sppp_ipcp_open (struct sppp *sp) +{ + sppp_cp_send (sp, PPP_IPCP, IPCP_CONF_REQ, ++sp->pp_seq, 0, 0); + timeout (sppp_cp_timeout, (caddr_t) sp, hz * 5); +} + +/* + * Process PPP control protocol timeouts. + */ +void sppp_cp_timeout (caddr_t arg, int dummy) +{ + struct sppp *sp = (struct sppp*) arg; + struct ifnet *ifp = &sp->pp_if; + int s = splimp (); + + switch (sp->lcp.state) { + case LCP_STATE_CLOSED: + /* No ACK for Configure-Request, retry. */ + sppp_lcp_open (sp); + break; + case LCP_STATE_ACK_RCVD: + /* ACK got, but no Configure-Request for peer, retry. */ + sppp_lcp_open (sp); + sp->lcp.state = LCP_STATE_CLOSED; + break; + case LCP_STATE_ACK_SENT: + /* ACK sent but no ACK for Configure-Request, retry. */ + sppp_lcp_open (sp); + break; + case LCP_STATE_OPENED: + /* LCP is already OK, try IPCP. */ + switch (sp->ipcp.state) { + case IPCP_STATE_CLOSED: + /* No ACK for Configure-Request, retry. */ + sppp_ipcp_open (sp); + break; + case IPCP_STATE_ACK_RCVD: + /* ACK got, but no Configure-Request for peer, retry. */ + sppp_ipcp_open (sp); + sp->ipcp.state = IPCP_STATE_CLOSED; + break; + case IPCP_STATE_ACK_SENT: + /* ACK sent but no ACK for Configure-Request, retry. */ + sppp_ipcp_open (sp); + break; + case IPCP_STATE_OPENED: + /* IPCP is OK. */ + break; + } + break; + } + splx (s); +} + +char *sppp_lcp_type_name (unsigned char type) +{ + static char buf [8]; + switch (type) { + case LCP_CONF_REQ: return ("conf-req"); + case LCP_CONF_ACK: return ("conf-ack"); + case LCP_CONF_NAK: return ("conf-nack"); + case LCP_CONF_REJ: return ("conf-rej"); + case LCP_TERM_REQ: return ("term-req"); + case LCP_TERM_ACK: return ("term-ack"); + case LCP_CODE_REJ: return ("code-rej"); + case LCP_PROTO_REJ: return ("proto-rej"); + case LCP_ECHO_REQ: return ("echo-req"); + case LCP_ECHO_REPLY: return ("echo-reply"); + case LCP_DISC_REQ: return ("discard-req"); + } + sprintf (buf, "%xh", type); + return (buf); +} + +char *sppp_ipcp_type_name (unsigned char type) +{ + static char buf [8]; + switch (type) { + case IPCP_CONF_REQ: return ("conf-req"); + case IPCP_CONF_ACK: return ("conf-ack"); + case IPCP_CONF_NAK: return ("conf-nack"); + case IPCP_CONF_REJ: return ("conf-rej"); + case IPCP_TERM_REQ: return ("term-req"); + case IPCP_TERM_ACK: return ("term-ack"); + case IPCP_CODE_REJ: return ("code-rej"); + } + sprintf (buf, "%xh", type); + return (buf); +} + +void sppp_print_bytes (unsigned char *p, unsigned short len) +{ + printf (" %x", *p++); + while (--len > 0) + printf ("-%x", *p++); +} |