diff options
author | imp <imp@FreeBSD.org> | 2003-12-03 07:29:38 +0000 |
---|---|---|
committer | imp <imp@FreeBSD.org> | 2003-12-03 07:29:38 +0000 |
commit | f6ae273bd0ee3c14e5c7cc2650865f5ead7fa808 (patch) | |
tree | 87af7efeab367d034ff04731cacb4fe3c29d84fc /sys/dev/cx/cxddk.c | |
parent | 9524d14ce664a11e19c13090ddfa8d80344b8d91 (diff) | |
download | FreeBSD-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.c | 905 |
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)); +} |