summaryrefslogtreecommitdiffstats
path: root/sys/dev/cx/cxddk.c
diff options
context:
space:
mode:
authorimp <imp@FreeBSD.org>2003-12-03 07:29:38 +0000
committerimp <imp@FreeBSD.org>2003-12-03 07:29:38 +0000
commitf6ae273bd0ee3c14e5c7cc2650865f5ead7fa808 (patch)
tree87af7efeab367d034ff04731cacb4fe3c29d84fc /sys/dev/cx/cxddk.c
parent9524d14ce664a11e19c13090ddfa8d80344b8d91 (diff)
downloadFreeBSD-src-f6ae273bd0ee3c14e5c7cc2650865f5ead7fa808.zip
FreeBSD-src-f6ae273bd0ee3c14e5c7cc2650865f5ead7fa808.tar.gz
Updated cx driver commit part 1: bring in the new kernel driver.
This is the vastly updated cx drvier from Roman Kurakin <rik@cronyx.ru> who has been patiently waiting for this update for sometime. The driver is mostly a rewrite from the version we have in the tree. While some similarities remain, losing the little history that the old driver has is not a big loss, and the re@ felt it was easier this way (less error prone). The userland parts of this update will be committed shortly. The driver is not connected to the build yet. I want to make sure I don't break any platform at any time, so I want to test that with these files in the tree before I continue (on the off chance I'm forgetting a file). I changed the DEBUG macro to CX_DEBUG from the code that was submitted (to not break when we go to building with opt_global.h after the release), as well adding $FreeBSD$. Submitted by: Roman Kurakin Approved by: re@ <scottl>
Diffstat (limited to 'sys/dev/cx/cxddk.c')
-rw-r--r--sys/dev/cx/cxddk.c905
1 files changed, 905 insertions, 0 deletions
diff --git a/sys/dev/cx/cxddk.c b/sys/dev/cx/cxddk.c
new file mode 100644
index 0000000..e4328b5
--- /dev/null
+++ b/sys/dev/cx/cxddk.c
@@ -0,0 +1,905 @@
+/*
+ * Cronyx-Sigma Driver Development Kit.
+ *
+ * Copyright (C) 1998 Cronyx Engineering.
+ * Author: Pavel Novikov, <pavel@inr.net.kiae.su>
+ *
+ * Copyright (C) 1998-2003 Cronyx Engineering.
+ * Author: Roman Kurakin, <rik@cronyx.ru>
+ *
+ * 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.
+ *
+ * Cronyx Id: cxddk.c,v 1.1.2.2 2003/11/27 14:24:50 rik Exp $
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <dev/cx/machdep.h>
+#include <dev/cx/cxddk.h>
+#include <dev/cx/cxreg.h>
+#include <dev/cx/cronyxfw.h>
+#include <dev/cx/csigmafw.h>
+
+#define BYTE *(unsigned char*)&
+
+/* standard base port set */
+static short porttab [] = {
+ 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0,
+ 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0x3c0, 0x3e0, 0
+};
+
+/*
+ * Compute the optimal size of the receive buffer.
+ */
+static int cx_compute_buf_len (cx_chan_t *c)
+{
+ int rbsz;
+ if (c->mode == M_ASYNC) {
+ rbsz = (c->rxbaud + 800 - 1) / 800 * 2;
+ if (rbsz < 4)
+ rbsz = 4;
+ else if (rbsz > DMABUFSZ)
+ rbsz = DMABUFSZ;
+ }
+ else
+ rbsz = DMABUFSZ;
+
+ return rbsz;
+}
+
+/*
+ * Auto-detect the installed adapters.
+ */
+int cx_find (port_t *board_ports)
+{
+ int i, n;
+
+ for (i=0, n=0; porttab[i] && n<NBRD; i++)
+ if (cx_probe_board (porttab[i], -1, -1))
+ board_ports[n++] = porttab[i];
+ return n;
+}
+
+/*
+ * Initialize the adapter.
+ */
+int cx_open_board (cx_board_t *b, int num, port_t port, int irq, int dma)
+{
+ cx_chan_t *c;
+
+ if (num >= NBRD || ! cx_probe_board (port, irq, dma))
+ return 0;
+
+ /* init callback pointers */
+ for (c=b->chan; c<b->chan+NCHAN; ++c) {
+ c->call_on_tx = 0;
+ c->call_on_rx = 0;
+ c->call_on_msig = 0;
+ c->call_on_err = 0;
+ }
+
+ cx_init (b, num, port, irq, dma);
+
+ /* Loading firmware */
+ if (! cx_setup_board (b, csigma_fw_data, csigma_fw_len, csigma_fw_tvec))
+ return 0;
+ return 1;
+}
+
+/*
+ * Shutdown the adapter.
+ */
+void cx_close_board (cx_board_t *b)
+{
+ cx_setup_board (b, 0, 0, 0);
+
+ /* Reset the controller. */
+ outb (BCR0(b->port), 0);
+ if (b->chan[8].type || b->chan[12].type)
+ outb (BCR0(b->port+0x10), 0);
+}
+
+/*
+ * Start the channel.
+ */
+void cx_start_chan (cx_chan_t *c, cx_buf_t *cb, unsigned long phys)
+{
+ int command = 0;
+ int mode = 0;
+ int ier = 0;
+ int rbsz;
+
+ c->overflow = 0;
+
+ /* Setting up buffers */
+ if (cb) {
+ c->arbuf = cb->rbuffer[0];
+ c->brbuf = cb->rbuffer[1];
+ c->atbuf = cb->tbuffer[0];
+ c->btbuf = cb->tbuffer[1];
+ c->arphys = phys + ((char*)c->arbuf - (char*)cb);
+ c->brphys = phys + ((char*)c->brbuf - (char*)cb);
+ c->atphys = phys + ((char*)c->atbuf - (char*)cb);
+ c->btphys = phys + ((char*)c->btbuf - (char*)cb);
+ }
+
+ /* Set current channel number */
+ outb (CAR(c->port), c->num & 3);
+
+ /* set receiver A buffer physical address */
+ outw (ARBADRU(c->port), (unsigned short) (c->arphys>>16));
+ outw (ARBADRL(c->port), (unsigned short) c->arphys);
+
+ /* set receiver B buffer physical address */
+ outw (BRBADRU(c->port), (unsigned short) (c->brphys>>16));
+ outw (BRBADRL(c->port), (unsigned short) c->brphys);
+
+ /* set transmitter A buffer physical address */
+ outw (ATBADRU(c->port), (unsigned short) (c->atphys>>16));
+ outw (ATBADRL(c->port), (unsigned short) c->atphys);
+
+ /* set transmitter B buffer physical address */
+ outw (BTBADRU(c->port), (unsigned short) (c->btphys>>16));
+ outw (BTBADRL(c->port), (unsigned short) c->btphys);
+
+ /* rx */
+ command |= CCR_ENRX;
+ ier |= IER_RXD;
+ if (c->board->dma) {
+ mode |= CMR_RXDMA;
+ if (c->mode == M_ASYNC)
+ ier |= IER_RET;
+ }
+
+ /* tx */
+ command |= CCR_ENTX;
+ ier |= (c->mode == M_ASYNC) ? IER_TXD : (IER_TXD | IER_TXMPTY);
+ if (c->board->dma)
+ mode |= CMR_TXDMA;
+
+ /* Set mode */
+ outb (CMR(c->port), mode | (c->mode == M_ASYNC ? CMR_ASYNC : CMR_HDLC));
+
+ /* Clear and initialize channel */
+ cx_cmd (c->port, CCR_CLRCH);
+ cx_cmd (c->port, CCR_INITCH | command);
+ if (c->mode == M_ASYNC)
+ cx_cmd (c->port, CCR_ENTX);
+
+ /* Start receiver */
+ rbsz = cx_compute_buf_len(c);
+ outw (ARBCNT(c->port), rbsz);
+ outw (BRBCNT(c->port), rbsz);
+ outw (ARBSTS(c->port), BSTS_OWN24);
+ outw (BRBSTS(c->port), BSTS_OWN24);
+
+ if (c->mode == M_ASYNC)
+ ier |= IER_MDM;
+
+ /* Enable interrupts */
+ outb (IER(c->port), ier);
+
+ /* Clear DTR and RTS */
+ cx_set_dtr (c, 0);
+ cx_set_rts (c, 0);
+}
+
+/*
+ * Turn the receiver on/off.
+ */
+void cx_enable_receive (cx_chan_t *c, int on)
+{
+ unsigned char ier;
+
+ if (cx_receive_enabled(c) && ! on) {
+ outb (CAR(c->port), c->num & 3);
+ if (c->mode == M_ASYNC) {
+ ier = inb (IER(c->port));
+ outb (IER(c->port), ier & ~ (IER_RXD | IER_RET));
+ }
+ cx_cmd (c->port, CCR_DISRX);
+ } else if (! cx_receive_enabled(c) && on) {
+ outb (CAR(c->port), c->num & 3);
+ ier = inb (IER(c->port));
+ if (c->mode == M_ASYNC)
+ outb (IER(c->port), ier | (IER_RXD | IER_RET));
+ else
+ outb (IER(c->port), ier | IER_RXD);
+ cx_cmd (c->port, CCR_ENRX);
+ }
+}
+
+/*
+ * Turn the transmiter on/off.
+ */
+void cx_enable_transmit (cx_chan_t *c, int on)
+{
+ if (cx_transmit_enabled(c) && ! on) {
+ outb (CAR(c->port), c->num & 3);
+ if (c->mode != M_ASYNC)
+ outb (STCR(c->port), STC_ABORTTX | STC_SNDSPC);
+ cx_cmd (c->port, CCR_DISTX);
+ } else if (! cx_transmit_enabled(c) && on) {
+ outb (CAR(c->port), c->num & 3);
+ cx_cmd (c->port, CCR_ENTX);
+ }
+}
+
+/*
+ * Get channel status.
+ */
+int cx_receive_enabled (cx_chan_t *c)
+{
+ outb (CAR(c->port), c->num & 3);
+ return (inb (CSR(c->port)) & CSRA_RXEN) != 0;
+}
+
+int cx_transmit_enabled (cx_chan_t *c)
+{
+ outb (CAR(c->port), c->num & 3);
+ return (inb (CSR(c->port)) & CSRA_TXEN) != 0;
+}
+
+unsigned long cx_get_baud (cx_chan_t *c)
+{
+ return (c->opt.tcor.clk == CLK_EXT) ? 0 : c->txbaud;
+}
+
+int cx_get_loop (cx_chan_t *c)
+{
+ return c->opt.tcor.llm ? 1 : 0;
+}
+
+int cx_get_nrzi (cx_chan_t *c)
+{
+ return c->opt.rcor.encod == ENCOD_NRZI;
+}
+
+int cx_get_dpll (cx_chan_t *c)
+{
+ return c->opt.rcor.dpll ? 1 : 0;
+}
+
+void cx_set_baud (cx_chan_t *c, unsigned long bps)
+{
+ int clock, period;
+
+ c->txbaud = c->rxbaud = bps;
+
+ /* Set current channel number */
+ outb (CAR(c->port), c->num & 3);
+ if (bps) {
+ if (c->mode == M_ASYNC || c->opt.rcor.dpll || c->opt.tcor.llm) {
+ /* Receive baud - internal */
+ cx_clock (c->oscfreq, c->rxbaud, &clock, &period);
+ c->opt.rcor.clk = clock;
+ outb (RCOR(c->port), BYTE c->opt.rcor);
+ outb (RBPR(c->port), period);
+ } else {
+ /* Receive baud - external */
+ c->opt.rcor.clk = CLK_EXT;
+ outb (RCOR(c->port), BYTE c->opt.rcor);
+ outb (RBPR(c->port), 1);
+ }
+
+ /* Transmit baud - internal */
+ cx_clock (c->oscfreq, c->txbaud, &clock, &period);
+ c->opt.tcor.clk = clock;
+ c->opt.tcor.ext1x = 0;
+ outb (TBPR(c->port), period);
+ } else if (c->mode != M_ASYNC) {
+ /* External clock - disable local loopback and DPLL */
+ c->opt.tcor.llm = 0;
+ c->opt.rcor.dpll = 0;
+
+ /* Transmit baud - external */
+ c->opt.tcor.ext1x = 1;
+ c->opt.tcor.clk = CLK_EXT;
+ outb (TBPR(c->port), 1);
+
+ /* Receive baud - external */
+ c->opt.rcor.clk = CLK_EXT;
+ outb (RCOR(c->port), BYTE c->opt.rcor);
+ outb (RBPR(c->port), 1);
+ }
+ if (c->opt.tcor.llm)
+ outb (COR2(c->port), (BYTE c->hopt.cor2) & ~3);
+ else
+ outb (COR2(c->port), BYTE c->hopt.cor2);
+ outb (TCOR(c->port), BYTE c->opt.tcor);
+}
+
+void cx_set_loop (cx_chan_t *c, int on)
+{
+ if (! c->txbaud)
+ return;
+
+ c->opt.tcor.llm = on ? 1 : 0;
+ cx_set_baud (c, c->txbaud);
+}
+
+void cx_set_dpll (cx_chan_t *c, int on)
+{
+ if (! c->txbaud)
+ return;
+
+ c->opt.rcor.dpll = on ? 1 : 0;
+ cx_set_baud (c, c->txbaud);
+}
+
+void cx_set_nrzi (cx_chan_t *c, int nrzi)
+{
+ c->opt.rcor.encod = (nrzi ? ENCOD_NRZI : ENCOD_NRZ);
+ outb (CAR(c->port), c->num & 3);
+ outb (RCOR(c->port), BYTE c->opt.rcor);
+}
+
+static int cx_send (cx_chan_t *c, char *data, int len,
+ void *attachment)
+{
+ unsigned char *buf;
+ port_t cnt_port, sts_port;
+ void **attp;
+
+ /* Set the current channel number. */
+ outb (CAR(c->port), c->num & 3);
+
+ /* Determine the buffer order. */
+ if (inb (DMABSTS(c->port)) & DMABSTS_NTBUF) {
+ if (inb (BTBSTS(c->port)) & BSTS_OWN24) {
+ buf = c->atbuf;
+ cnt_port = ATBCNT(c->port);
+ sts_port = ATBSTS(c->port);
+ attp = &c->attach[0];
+ } else {
+ buf = c->btbuf;
+ cnt_port = BTBCNT(c->port);
+ sts_port = BTBSTS(c->port);
+ attp = &c->attach[1];
+ }
+ } else {
+ if (inb (ATBSTS(c->port)) & BSTS_OWN24) {
+ buf = c->btbuf;
+ cnt_port = BTBCNT(c->port);
+ sts_port = BTBSTS(c->port);
+ attp = &c->attach[1];
+ } else {
+ buf = c->atbuf;
+ cnt_port = ATBCNT(c->port);
+ sts_port = ATBSTS(c->port);
+ attp = &c->attach[0];
+ }
+ }
+ /* Is it busy? */
+ if (inb (sts_port) & BSTS_OWN24)
+ return -1;
+
+ memcpy (buf, data, len);
+ *attp = attachment;
+
+ /* Start transmitter. */
+ outw (cnt_port, len);
+ outb (sts_port, BSTS_EOFR | BSTS_INTR | BSTS_OWN24);
+
+ /* Enable TXMPTY interrupt,
+ * to catch the case when the second buffer is empty. */
+ if (c->mode != M_ASYNC) {
+ if ((inb(ATBSTS(c->port)) & BSTS_OWN24) &&
+ (inb(BTBSTS(c->port)) & BSTS_OWN24)) {
+ outb (IER(c->port), IER_RXD | IER_TXD | IER_TXMPTY);
+ } else
+ outb (IER(c->port), IER_RXD | IER_TXD);
+ }
+ return 0;
+}
+
+/*
+ * Number of free buffs
+ */
+int cx_buf_free (cx_chan_t *c)
+{
+ return ! (inb (ATBSTS(c->port)) & BSTS_OWN24) +
+ ! (inb (BTBSTS(c->port)) & BSTS_OWN24);
+}
+
+/*
+ * Send the data packet.
+ */
+int cx_send_packet (cx_chan_t *c, char *data, int len, void *attachment)
+{
+ if (len >= DMABUFSZ)
+ return -2;
+ if (c->mode == M_ASYNC) {
+ static char buf [DMABUFSZ];
+ char *p, *t = buf;
+
+ /* Async -- double all nulls. */
+ for (p=data; p < data+len && t < buf+DMABUFSZ-1; ++p)
+ if ((*t++ = *p) == 0)
+ *t++ = 0;
+ return cx_send (c, buf, t-buf, attachment);
+ }
+ return cx_send (c, data, len, attachment);
+}
+
+static int cx_receive_interrupt (cx_chan_t *c)
+{
+ unsigned short risr;
+ int len = 0, rbsz;
+
+ ++c->rintr;
+ risr = inw (RISR(c->port));
+
+ /* Compute optimal receiver buffer length */
+ rbsz = cx_compute_buf_len(c);
+ if (c->mode == M_ASYNC && (risr & RISA_TIMEOUT)) {
+ unsigned long rcbadr = (unsigned short) inw (RCBADRL(c->port)) |
+ (long) inw (RCBADRU(c->port)) << 16;
+ unsigned char *buf = 0;
+ port_t cnt_port = 0, sts_port = 0;
+
+ if (rcbadr >= c->brphys && rcbadr < c->brphys+DMABUFSZ) {
+ buf = c->brbuf;
+ len = rcbadr - c->brphys;
+ cnt_port = BRBCNT(c->port);
+ sts_port = BRBSTS(c->port);
+ } else if (rcbadr >= c->arphys && rcbadr < c->arphys+DMABUFSZ) {
+ buf = c->arbuf;
+ len = rcbadr - c->arphys;
+ cnt_port = ARBCNT(c->port);
+ sts_port = ARBSTS(c->port);
+ }
+
+ if (len) {
+ c->ibytes += len;
+ c->received_data = buf;
+ c->received_len = len;
+
+ /* Restart receiver. */
+ outw (cnt_port, rbsz);
+ outb (sts_port, BSTS_OWN24);
+ }
+ return (REOI_TERMBUFF);
+ }
+
+ /* Receive errors. */
+ if (risr & RIS_OVERRUN) {
+ ++c->ierrs;
+ if (c->call_on_err)
+ c->call_on_err (c, CX_OVERRUN);
+ } else if (c->mode != M_ASYNC && (risr & RISH_CRCERR)) {
+ ++c->ierrs;
+ if (c->call_on_err)
+ c->call_on_err (c, CX_CRC);
+ } else if (c->mode != M_ASYNC && (risr & (RISH_RXABORT | RISH_RESIND))) {
+ ++c->ierrs;
+ if (c->call_on_err)
+ c->call_on_err (c, CX_FRAME);
+ } else if (c->mode == M_ASYNC && (risr & RISA_PARERR)) {
+ ++c->ierrs;
+ if (c->call_on_err)
+ c->call_on_err (c, CX_CRC);
+ } else if (c->mode == M_ASYNC && (risr & RISA_FRERR)) {
+ ++c->ierrs;
+ if (c->call_on_err)
+ c->call_on_err (c, CX_FRAME);
+ } else if (c->mode == M_ASYNC && (risr & RISA_BREAK)) {
+ if (c->call_on_err)
+ c->call_on_err (c, CX_BREAK);
+ } else if (! (risr & RIS_EOBUF)) {
+ ++c->ierrs;
+ } else {
+ /* Handle received data. */
+ len = (risr & RIS_BB) ? inw(BRBCNT(c->port)) : inw(ARBCNT(c->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. */
+ len = DMABUFSZ;
+ } else if (c->mode != M_ASYNC && ! (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->overflow) {
+ if (c->call_on_err)
+ c->call_on_err (c, CX_OVERFLOW);
+ c->overflow = 1;
+ ++c->ierrs;
+ }
+ } else if (! c->overflow) {
+ if (risr & RIS_BB) {
+ c->received_data = c->brbuf;
+ c->received_len = len;
+ } else {
+ c->received_data = c->arbuf;
+ c->received_len = len;
+ }
+ if (c->mode != M_ASYNC)
+ ++c->ipkts;
+ c->ibytes += len;
+ } else
+ c->overflow = 0;
+ }
+
+ /* Restart receiver. */
+ if (! (inb (ARBSTS(c->port)) & BSTS_OWN24)) {
+ outw (ARBCNT(c->port), rbsz);
+ outb (ARBSTS(c->port), BSTS_OWN24);
+ }
+ if (! (inb (BRBSTS(c->port)) & BSTS_OWN24)) {
+ outw (BRBCNT(c->port), rbsz);
+ outb (BRBSTS(c->port), BSTS_OWN24);
+ }
+
+ /* Discard exception characters. */
+ if ((risr & RISA_SCMASK) && c->aopt.cor2.ixon)
+ return (REOI_DISCEXC);
+ else
+ return (0);
+}
+
+static void cx_transmit_interrupt (cx_chan_t *c)
+{
+ unsigned char tisr;
+ int len = 0;
+
+ ++c->tintr;
+ tisr = inb (TISR(c->port));
+ if (tisr & TIS_UNDERRUN) { /* Transmit underrun error */
+ if (c->call_on_err)
+ c->call_on_err (c, CX_UNDERRUN);
+ ++c->oerrs;
+ } else if (tisr & (TIS_EOBUF | TIS_TXEMPTY | TIS_TXDATA)) {
+ /* Call processing function */
+ if (tisr & TIS_BB) {
+ len = inw(BTBCNT(c->port));
+ if (c->call_on_tx)
+ c->call_on_tx (c, c->attach[1], len);
+ } else {
+ len = inw(ATBCNT(c->port));
+ if (c->call_on_tx)
+ c->call_on_tx (c, c->attach[0], len);
+ }
+ if (c->mode != M_ASYNC && len != 0)
+ ++c->opkts;
+ c->obytes += len;
+ }
+
+ /* Enable TXMPTY interrupt,
+ * to catch the case when the second buffer is empty. */
+ if (c->mode != M_ASYNC) {
+ if ((inb (ATBSTS(c->port)) & BSTS_OWN24) &&
+ (inb (BTBSTS(c->port)) & BSTS_OWN24)) {
+ outb (IER(c->port), IER_RXD | IER_TXD | IER_TXMPTY);
+ } else
+ outb (IER(c->port), IER_RXD | IER_TXD);
+ }
+}
+
+void cx_int_handler (cx_board_t *b)
+{
+ unsigned char livr;
+ cx_chan_t *c;
+
+ while (! (inw (BSR(b->port)) & BSR_NOINTR)) {
+ /* Enter the interrupt context, using IACK bus cycle.
+ Read the local interrupt vector register. */
+ livr = inb (IACK(b->port, BRD_INTR_LEVEL));
+ c = b->chan + (livr>>2 & 0xf);
+ if (c->type == T_NONE)
+ continue;
+ switch (livr & 3) {
+ case LIV_MODEM: /* modem interrupt */
+ ++c->mintr;
+ if (c->call_on_msig)
+ c->call_on_msig (c);
+ outb (MEOIR(c->port), 0);
+ break;
+ case LIV_EXCEP: /* receive exception */
+ case LIV_RXDATA: /* receive interrupt */
+ outb (REOIR(c->port), cx_receive_interrupt (c));
+ if (c->call_on_rx && c->received_data) {
+ c->call_on_rx (c, c->received_data,
+ c->received_len);
+ c->received_data = 0;
+ }
+ break;
+ case LIV_TXDATA: /* transmit interrupt */
+ cx_transmit_interrupt (c);
+ outb (TEOIR(c->port), 0);
+ break;
+ }
+ }
+}
+
+/*
+ * Register event processing functions
+ */
+void cx_register_transmit (cx_chan_t *c,
+ void (*func) (cx_chan_t *c, void *attachment, int len))
+{
+ c->call_on_tx = func;
+}
+
+void cx_register_receive (cx_chan_t *c,
+ void (*func) (cx_chan_t *c, char *data, int len))
+{
+ c->call_on_rx = func;
+}
+
+void cx_register_modem (cx_chan_t *c, void (*func) (cx_chan_t *c))
+{
+ c->call_on_msig = func;
+}
+
+void cx_register_error (cx_chan_t *c, void (*func) (cx_chan_t *c, int data))
+{
+ c->call_on_err = func;
+}
+
+/*
+ * Async protocol functions.
+ */
+
+/*
+ * Enable/disable transmitter.
+ */
+void cx_transmitter_ctl (cx_chan_t *c,int start)
+{
+ outb (CAR(c->port), c->num & 3);
+ cx_cmd (c->port, start ? CCR_ENTX : CCR_DISTX);
+}
+
+/*
+ * Discard all data queued in transmitter.
+ */
+void cx_flush_transmit (cx_chan_t *c)
+{
+ outb (CAR(c->port), c->num & 3);
+ cx_cmd (c->port, CCR_CLRTX);
+}
+
+/*
+ * Send the XON/XOFF flow control symbol.
+ */
+void cx_xflow_ctl (cx_chan_t *c, int on)
+{
+ outb (CAR(c->port), c->num & 3);
+ outb (STCR(c->port), STC_SNDSPC | (on ? STC_SSPC_1 : STC_SSPC_2));
+}
+
+/*
+ * Send the break signal for a given number of milliseconds.
+ */
+void cx_send_break (cx_chan_t *c, int msec)
+{
+ static unsigned char buf [128];
+ unsigned char *p;
+
+ p = buf;
+ *p++ = 0; /* extended transmit command */
+ *p++ = 0x81; /* send break */
+
+ if (msec > 10000) /* max 10 seconds */
+ msec = 10000;
+ if (msec < 10) /* min 10 msec */
+ msec = 10;
+ while (msec > 0) {
+ int ms = 250; /* 250 msec */
+ if (ms > msec)
+ ms = msec;
+ msec -= ms;
+ *p++ = 0; /* extended transmit command */
+ *p++ = 0x82; /* insert delay */
+ *p++ = ms;
+ }
+ *p++ = 0; /* extended transmit command */
+ *p++ = 0x83; /* stop break */
+
+ cx_send (c, buf, p-buf, 0);
+}
+
+/*
+ * Set async parameters.
+ */
+void cx_set_async_param (cx_chan_t *c, int baud, int bits, int parity,
+ int stop2, int ignpar, int rtscts,
+ int ixon, int ixany, int symstart, int symstop)
+{
+ int clock, period;
+ cx_cor1_async_t cor1;
+
+ /* Set character length and parity mode. */
+ BYTE cor1 = 0;
+ cor1.charlen = bits - 1;
+ cor1.parmode = parity ? PARM_NORMAL : PARM_NOPAR;
+ cor1.parity = parity==1 ? PAR_ODD : PAR_EVEN;
+ cor1.ignpar = ignpar ? 1 : 0;
+
+ /* Enable/disable hardware CTS. */
+ c->aopt.cor2.ctsae = rtscts ? 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 = ixon ? 1 : 0;
+ c->aopt.cor2.ixany = ixany ? 1 : 0;
+
+ /* Set the number of stop bits. */
+ if (stop2)
+ 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 = ixon ? 1 : 0;
+ c->aopt.cor3.flowct = ixon ? FLOWCC_NOTPASS : FLOWCC_PASS;
+
+ c->aopt.schr1 = symstart; /* XON */
+ c->aopt.schr2 = symstop; /* XOFF */
+
+ /* Set current channel number. */
+ outb (CAR(c->port), c->num & 3);
+
+ /* Set up clock values. */
+ if (baud) {
+ c->rxbaud = c->txbaud = baud;
+
+ /* Receiver. */
+ cx_clock (c->oscfreq, c->rxbaud, &clock, &period);
+ c->opt.rcor.clk = clock;
+ outb (RCOR(c->port), BYTE c->opt.rcor);
+ outb (RBPR(c->port), period);
+
+ /* Transmitter. */
+ cx_clock (c->oscfreq, c->txbaud, &clock, &period);
+ c->opt.tcor.clk = clock;
+ c->opt.tcor.ext1x = 0;
+ outb (TCOR(c->port), BYTE c->opt.tcor);
+ outb (TBPR(c->port), period);
+ }
+ outb (COR2(c->port), BYTE c->aopt.cor2);
+ outb (COR3(c->port), BYTE c->aopt.cor3);
+ outb (SCHR1(c->port), c->aopt.schr1);
+ outb (SCHR2(c->port), c->aopt.schr2);
+
+ if (BYTE c->aopt.cor1 != BYTE cor1) {
+ BYTE c->aopt.cor1 = BYTE cor1;
+ outb (COR1(c->port), BYTE c->aopt.cor1);
+ /* Any change to COR1 require reinitialization. */
+ /* Unfortunately, it may cause transmitter glitches... */
+ cx_cmd (c->port, CCR_INITCH);
+ }
+}
+
+/*
+ * Set mode: M_ASYNC or M_HDLC.
+ * Both receiver and transmitter are disabled.
+ */
+int cx_set_mode (cx_chan_t *c, int mode)
+{
+ if (mode == M_HDLC) {
+ if (c->type == T_ASYNC)
+ return -1;
+
+ if (c->mode == M_HDLC)
+ return 0;
+
+ c->mode = M_HDLC;
+ } else if (mode == M_ASYNC) {
+ if (c->type == T_SYNC_RS232 ||
+ c->type == T_SYNC_V35 ||
+ c->type == T_SYNC_RS449)
+ return -1;
+
+ if (c->mode == M_ASYNC)
+ return 0;
+
+ c->mode = M_ASYNC;
+ c->opt.tcor.ext1x = 0;
+ c->opt.tcor.llm = 0;
+ c->opt.rcor.dpll = 0;
+ c->opt.rcor.encod = ENCOD_NRZ;
+ if (! c->txbaud || ! c->rxbaud)
+ c->txbaud = c->rxbaud = 9600;
+ } else
+ return -1;
+
+ cx_setup_chan (c);
+ cx_start_chan (c, 0, 0);
+ cx_enable_receive (c, 0);
+ cx_enable_transmit (c, 0);
+ return 0;
+}
+
+/*
+ * Set port type for old models of Sigma
+ */
+void cx_set_port (cx_chan_t *c, int iftype)
+{
+ if (c->board->type == B_SIGMA_XXX) {
+ switch (c->num) {
+ case 0:
+ if ((c->board->if0type != 0) == (iftype != 0))
+ return;
+ c->board->if0type = iftype;
+ c->board->bcr0 &= ~BCR0_UMASK;
+ 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);
+ break;
+ case 8:
+ if ((c->board->if8type != 0) == (iftype != 0))
+ return;
+ c->board->if8type = iftype;
+ c->board->bcr0b &= ~BCR0_UMASK;
+ 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);
+ break;
+ }
+ }
+}
+
+/*
+ * Get port type for old models of Sigma
+ * -1 Fixed port type or auto detect
+ * 0 RS232
+ * 1 V35
+ * 2 RS449
+ */
+int cx_get_port (cx_chan_t *c)
+{
+ int iftype;
+
+ if (c->board->type == B_SIGMA_XXX) {
+ switch (c->num) {
+ case 0:
+ iftype = c->board->if0type; break;
+ case 8:
+ iftype = c->board->if8type; break;
+ default:
+ return -1;
+ }
+
+ if (iftype)
+ switch (c->type) {
+ case T_UNIV_V35: return 1; break;
+ case T_UNIV_RS449: return 2; break;
+ default: return -1; break;
+ }
+ else
+ return 0;
+ } else
+ return -1;
+}
+
+void cx_intr_off (cx_board_t *b)
+{
+ outb (BCR0(b->port), b->bcr0 & ~BCR0_IRQ_MASK);
+ if (b->chan[8].port || b->chan[12].port)
+ outb (BCR0(b->port+0x10), b->bcr0b & ~BCR0_IRQ_MASK);
+}
+
+void cx_intr_on (cx_board_t *b)
+{
+ outb (BCR0(b->port), b->bcr0);
+ if (b->chan[8].port || b->chan[12].port)
+ outb (BCR0(b->port+0x10), b->bcr0b);
+}
+
+int cx_checkintr (cx_board_t *b)
+{
+ return (!(inw (BSR(b->port)) & BSR_NOINTR));
+}
OpenPOWER on IntegriCloud