cy_isa.c revision 6261
16261Sjkh/* 26261Sjkh * cyclades cyclom-y serial driver 36261Sjkh * Andrew Herbert <andrew@werple.apana.org.au>, 17 August 1993 46261Sjkh * 56261Sjkh * Copyright (c) 1993 Andrew Herbert. 66261Sjkh * All rights reserved. 76261Sjkh * 86261Sjkh * Redistribution and use in source and binary forms, with or without 96261Sjkh * modification, are permitted provided that the following conditions 106261Sjkh * are met: 116261Sjkh * 1. Redistributions of source code must retain the above copyright 126261Sjkh * notice, this list of conditions and the following disclaimer. 136261Sjkh * 2. Redistributions in binary form must reproduce the above copyright 146261Sjkh * notice, this list of conditions and the following disclaimer in the 156261Sjkh * documentation and/or other materials provided with the distribution. 166261Sjkh * 3. The name Andrew Herbert may not be used to endorse or promote products 176261Sjkh * derived from this software without specific prior written permission. 186261Sjkh * 196261Sjkh * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED 206261Sjkh * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 216261Sjkh * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 226261Sjkh * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 236261Sjkh * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 246261Sjkh * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 256261Sjkh * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 266261Sjkh * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 276261Sjkh * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 286261Sjkh * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 296261Sjkh * 306261Sjkh * $Id: cy.c,v 1.7 1994/05/24 07:31:12 mycroft Exp $ 316261Sjkh */ 326261Sjkh 336261Sjkh/* 346261Sjkh * Device minor number encoding: 356261Sjkh * 366261Sjkh * c c x x u u u u - bits in the minor device number 376261Sjkh * 386261Sjkh * bits meaning 396261Sjkh * ---- ------- 406261Sjkh * uuuu physical serial line (i.e. unit) to use 416261Sjkh * 0-7 on a cyclom-8Y, 0-15 on a cyclom-16Y 426261Sjkh * xx unused 436261Sjkh * cc carrier control mode 446261Sjkh * 00 complete hardware carrier control of the tty. 456261Sjkh * DCD must be high for the open(2) to complete. 466261Sjkh * 01 dialin pseudo-device (not yet implemented) 476261Sjkh * 10 carrier ignored until a high->low transition 486261Sjkh * 11 carrier completed ignored 496261Sjkh */ 506261Sjkh 516261Sjkh/* 526261Sjkh * Known deficiencies: 536261Sjkh * 546261Sjkh * * no BREAK handling - breaks are ignored, and can't be sent either 556261Sjkh * * no support for bad-char reporting, except via PARMRK 566261Sjkh * * no support for dialin + dialout devices 576261Sjkh */ 586261Sjkh 596261Sjkh#include "cy.h" 606261Sjkh#if NCY > 0 616261Sjkh 626261Sjkh/* This disgusing hack because we actually have 16 units on one controller */ 636261Sjkh#if NCY < 2 646261Sjkh#undef NCY 656261Sjkh#define NCY (16) 666261Sjkh#endif 676261Sjkh 686261Sjkh#include <sys/param.h> 696261Sjkh#include <sys/systm.h> 706261Sjkh#include <sys/kernel.h> 716261Sjkh#include <sys/malloc.h> 726261Sjkh#include <sys/ioctl.h> 736261Sjkh#include <sys/tty.h> 746261Sjkh#include <sys/proc.h> 756261Sjkh#include <sys/user.h> 766261Sjkh#include <sys/conf.h> 776261Sjkh#include <sys/file.h> 786261Sjkh#include <sys/uio.h> 796261Sjkh#include <sys/kernel.h> 806261Sjkh#include <sys/syslog.h> 816261Sjkh 826261Sjkh#include <machine/cpu.h> 836261Sjkh#ifdef NetBSD 846261Sjkh#include <machine/pio.h> 856261Sjkh#endif 866261Sjkh#include <machine/cpufunc.h> 876261Sjkh 886261Sjkh#include <i386/isa/isa_device.h> 896261Sjkh#include <i386/isa/ic/cd1400.h> 906261Sjkh 916261Sjkh#define RxFifoThreshold 3 /* 3 characters (out of 12) in the receive 926261Sjkh * FIFO before an interrupt is generated 936261Sjkh */ 946261Sjkh#define FastRawInput /* bypass the regular char-by-char canonical input 956261Sjkh * processing whenever possible 966261Sjkh */ 976261Sjkh#define PollMode /* use polling-based irq service routine, not the 986261Sjkh * hardware svcack lines. Must be defined for 996261Sjkh * cyclom-16y boards. 1006261Sjkh * 1016261Sjkh * XXX cyclom-8y doesn't work without this defined 1026261Sjkh * either (!) 1036261Sjkh */ 1046261Sjkh#define LogOverruns /* log receive fifo overruns */ 1056261Sjkh#undef TxBuffer /* buffer driver output, to be slightly more 1066261Sjkh * efficient 1076261Sjkh * 1086261Sjkh * XXX presently buggy 1096261Sjkh */ 1106261Sjkh#undef Smarts /* enable slightly more CD1400 intelligence. Mainly 1116261Sjkh * the output CR/LF processing, plus we can avoid a 1126261Sjkh * few checks usually done in ttyinput(). 1136261Sjkh * 1146261Sjkh * XXX not yet implemented, and not particularly 1156261Sjkh * worthwhile either. 1166261Sjkh */ 1176261Sjkh#define CyDebug /* include debugging code (minimal effect on 1186261Sjkh * performance) 1196261Sjkh */ 1206261Sjkh 1216261Sjkh#define CY_RX_BUFS 2 /* two receive buffers per port */ 1226261Sjkh#define CY_RX_BUF_SIZE 256 /* bytes per receive buffer */ 1236261Sjkh#define CY_TX_BUF_SIZE 512 /* bytes per transmit buffer */ 1246261Sjkh 1256261Sjkh/* #define CD1400s_PER_CYCLOM 1 */ /* cyclom-4y */ 1266261Sjkh/* #define CD1400s_PER_CYCLOM 2 */ /* cyclom-8y */ 1276261Sjkh#define CD1400s_PER_CYCLOM 4 /* cyclom-16y */ 1286261Sjkh 1296261Sjkh/* FreeBSD's getty doesn't know option for setting RTS/CTS handshake. Its 1306261Sjkh getty, like a lot of other old cruft, should be replaced with something 1316261Sjkh which used POSIX tty interfaces which at least allow enabling it. In the 1326261Sjkh meantime, use the force. */ 1336261Sjkh#define ALWAYS_RTS_CTS 1 1346261Sjkh 1356261Sjkh#if CD1400s_PER_CYCLOM < 4 1366261Sjkh#define CD1400_MEMSIZE 0x400 /* 4*256 bytes per chip: cyclom-[48]y */ 1376261Sjkh#else 1386261Sjkh#define CD1400_MEMSIZE 0x100 /* 256 bytes per chip: cyclom-16y */ 1396261Sjkh /* XXX or is it 0x400 like the rest? */ 1406261Sjkh#define CYCLOM_16 1 /* This is a cyclom-16Y */ 1416261Sjkh#endif 1426261Sjkh 1436261Sjkh#define PORTS_PER_CYCLOM (CD1400_NO_OF_CHANNELS * CD1400s_PER_CYCLOM) 1446261Sjkh#define CYCLOM_RESET_16 0x1400 /* cyclom-16y reset */ 1456261Sjkh#define CYCLOM_CLEAR_INTR 0x1800 /* intr ack address */ 1466261Sjkh#define CYCLOM_CLOCK 25000000 /* baud rate clock */ 1476261Sjkh 1486261Sjkh#define CY_UNITMASK 0x0f 1496261Sjkh#define CY_CARRIERMASK 0xC0 1506261Sjkh#define CY_CARRIERSHIFT 6 1516261Sjkh 1526261Sjkh#define UNIT(x) (minor(x) & CY_UNITMASK) 1536261Sjkh#define CARRIER_MODE(x) ((minor(x) & CY_CARRIERMASK) >> CY_CARRIERSHIFT) 1546261Sjkh 1556261Sjkhtypedef u_char * volatile cy_addr; 1566261Sjkh 1576261Sjkhint cyprobe(struct isa_device *dev); 1586261Sjkhint cyattach(struct isa_device *isdp); 1596261Sjkhvoid cystart(struct tty *tp); 1606261Sjkhint cyparam(struct tty *tp, struct termios *t); 1616261Sjkhint cyspeed(int speed, int *prescaler_io); 1626261Sjkhstatic void cy_channel_init(dev_t dev, int reset); 1636261Sjkhstatic void cd1400_channel_cmd(cy_addr base, u_char cmd); 1646261Sjkh 1656261Sjkh/* hsu@clinet.fi: sigh */ 1666261Sjkh#ifdef __NetBSD__ 1676261Sjkh#define DELAY(foo) delay(foo) 1686261Sjkhvoid delay(int delay); 1696261Sjkh#endif 1706261Sjkh 1716261Sjkh/* Better get rid of this until the core people agree on kernel interfaces. 1726261Sjkh At least it will then compile on both WhichBSDs. 1736261Sjkh */ 1746261Sjkh#if 0 1756261Sjkhextern unsigned int delaycount; /* calibrated 1 ms cpu-spin delay */ 1766261Sjkh#endif 1776261Sjkh 1786261Sjkhstruct isa_driver cydriver = { 1796261Sjkh cyprobe, cyattach, "cy" 1806261Sjkh}; 1816261Sjkh 1826261Sjkh/* low-level ping-pong buffer structure */ 1836261Sjkh 1846261Sjkhstruct cy_buf { 1856261Sjkh u_char *next_char; /* location of next char to write */ 1866261Sjkh u_int free; /* free chars remaining in buffer */ 1876261Sjkh struct cy_buf *next_buf; /* circular, you know */ 1886261Sjkh u_char buf[CY_RX_BUF_SIZE]; /* start of the buffer */ 1896261Sjkh}; 1906261Sjkh 1916261Sjkh/* low-level ring buffer */ 1926261Sjkh 1936261Sjkh#ifdef TxBuffer 1946261Sjkhstruct cy_ring { 1956261Sjkh u_char buf[CY_TX_BUF_SIZE]; 1966261Sjkh u_char *head; 1976261Sjkh u_char *tail; /* next pos. to insert char */ 1986261Sjkh u_char *endish; /* physical end of buf */ 1996261Sjkh u_int used; /* no. of chars in queue */ 2006261Sjkh}; 2016261Sjkh#endif 2026261Sjkh 2036261Sjkh 2046261Sjkh/* 2056261Sjkh * define a structure to keep track of each serial line 2066261Sjkh */ 2076261Sjkh 2086261Sjkhstruct cy { 2096261Sjkh cy_addr base_addr; /* base address of this port's cd1400 */ 2106261Sjkh struct tty *tty; 2116261Sjkh u_int dtrwait; /* time (in ticks) to hold dtr low after close */ 2126261Sjkh u_int recv_exception; /* exception chars received */ 2136261Sjkh u_int recv_normal; /* normal chars received */ 2146261Sjkh u_int xmit; /* chars transmitted */ 2156261Sjkh u_int mdm; /* modem signal changes */ 2166261Sjkh#ifdef CyDebug 2176261Sjkh u_int start_count; /* no. of calls to cystart() */ 2186261Sjkh u_int start_real; /* no. of calls that did something */ 2196261Sjkh#endif 2206261Sjkh u_char carrier_mode; /* hardware carrier handling mode */ 2216261Sjkh /* 2226261Sjkh * 0 = always use 2236261Sjkh * 1 = always use (dialin port) 2246261Sjkh * 2 = ignore during open, then use it 2256261Sjkh * 3 = ignore completely 2266261Sjkh */ 2276261Sjkh u_char carrier_delta; /* true if carrier has changed state */ 2286261Sjkh u_char fifo_overrun; /* true if cd1400 receive fifo has... */ 2296261Sjkh u_char rx_buf_overrun; /* true if low-level buf overflow */ 2306261Sjkh u_char intr_enable; /* CD1400 SRER shadow */ 2316261Sjkh u_char modem_sig; /* CD1400 modem signal shadow */ 2326261Sjkh u_char channel_control;/* CD1400 CCR control command shadow */ 2336261Sjkh u_char cor[3]; /* CD1400 COR1-3 shadows */ 2346261Sjkh#ifdef Smarts 2356261Sjkh u_char spec_char[4]; /* CD1400 SCHR1-4 shadows */ 2366261Sjkh#endif 2376261Sjkh struct cy_buf *rx_buf; /* current receive buffer */ 2386261Sjkh struct cy_buf rx_buf_pool[CY_RX_BUFS];/* receive ping-pong buffers */ 2396261Sjkh#ifdef TxBuffer 2406261Sjkh struct cy_ring tx_buf; /* transmit buffer */ 2416261Sjkh#endif 2426261Sjkh}; 2436261Sjkh 2446261Sjkhint cydefaultrate = TTYDEF_SPEED; 2456261Sjkhcy_addr cyclom_base; /* base address of the card */ 2466261Sjkhstatic struct cy *info[NCY*PORTS_PER_CYCLOM]; 2476261Sjkhstruct tty *cy_tty[NCY*PORTS_PER_CYCLOM]; 2486261Sjkhstatic volatile u_char timeout_scheduled = 0; /* true if a timeout has been scheduled */ 2496261Sjkh 2506261Sjkh#ifdef CyDebug 2516261Sjkhu_int cy_svrr_probes = 0; /* debugging */ 2526261Sjkhu_int cy_timeouts = 0; 2536261Sjkhu_int cy_timeout_req = 0; 2546261Sjkh#endif 2556261Sjkh 2566261Sjkh/**********************************************************************/ 2576261Sjkh 2586261Sjkhint 2596261Sjkhcyprobe(struct isa_device *dev) 2606261Sjkh{ 2616261Sjkh int i, j; 2626261Sjkh u_char version = 0; /* firmware version */ 2636261Sjkh 2646261Sjkh /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ 2656261Sjkh i = *(cy_addr)(dev->id_maddr + CYCLOM_RESET_16); 2666261Sjkh 2676261Sjkh DELAY(500); /* wait for the board to get its act together (500 us) */ 2686261Sjkh 2696261Sjkh for (i = 0; i < CD1400s_PER_CYCLOM; i++) { 2706261Sjkh cy_addr base = dev->id_maddr + i * CD1400_MEMSIZE; 2716261Sjkh 2726261Sjkh /* wait for chip to become ready for new command */ 2736261Sjkh for (j = 0; j < 100; j += 50) { 2746261Sjkh DELAY(50); /* wait 50 us */ 2756261Sjkh 2766261Sjkh if (!*(base + CD1400_CCR)) 2776261Sjkh break; 2786261Sjkh } 2796261Sjkh 2806261Sjkh /* clear the GFRCR register */ 2816261Sjkh *(base + CD1400_GFRCR) = 0; 2826261Sjkh 2836261Sjkh /* issue a reset command */ 2846261Sjkh *(base + CD1400_CCR) = CD1400_CMD_RESET; 2856261Sjkh 2866261Sjkh /* wait for the CD1400 to initialise itself */ 2876261Sjkh for (j = 0; j < 1000; j += 50) { 2886261Sjkh DELAY(50); /* wait 50 us */ 2896261Sjkh 2906261Sjkh /* retrieve firmware version */ 2916261Sjkh version = *(base + CD1400_GFRCR); 2926261Sjkh if (version) 2936261Sjkh break; 2946261Sjkh } 2956261Sjkh 2966261Sjkh /* anything in the 40-4f range is fine */ 2976261Sjkh if ((version & 0xf0) != 0x40) { 2986261Sjkh return 0; 2996261Sjkh } 3006261Sjkh } 3016261Sjkh 3026261Sjkh return 1; /* found */ 3036261Sjkh} 3046261Sjkh 3056261Sjkh 3066261Sjkhint 3076261Sjkhcyattach(struct isa_device *isdp) 3086261Sjkh{ 3096261Sjkh/* u_char unit = UNIT(isdp->id_unit); */ 3106261Sjkh int i, j, k; 3116261Sjkh 3126261Sjkh /* global variable used various routines */ 3136261Sjkh cyclom_base = (cy_addr)isdp->id_maddr; 3146261Sjkh 3156261Sjkh for (i = 0, k = 0; i < CD1400s_PER_CYCLOM; i++) { 3166261Sjkh cy_addr base = cyclom_base + i * CD1400_MEMSIZE; 3176261Sjkh 3186261Sjkh /* setup a 1ms clock tick */ 3196261Sjkh *(base + CD1400_PPR) = CD1400_CLOCK_25_1MS; 3206261Sjkh 3216261Sjkh for (j = 0; j < CD1400_NO_OF_CHANNELS; j++, k++) { 3226261Sjkh struct cy *ip; 3236261Sjkh 3246261Sjkh /* 3256261Sjkh * grab some space. it'd be more polite to do this in cyopen(), 3266261Sjkh * but hey. 3276261Sjkh */ 3286261Sjkh info[k] = ip = malloc(sizeof(struct cy), M_DEVBUF, M_WAITOK); 3296261Sjkh 3306261Sjkh /* clear all sorts of junk */ 3316261Sjkh bzero(ip, sizeof(struct cy)); 3326261Sjkh 3336261Sjkh ip->base_addr = base; 3346261Sjkh 3356261Sjkh /* initialise the channel, without resetting it first */ 3366261Sjkh cy_channel_init(k, 0); 3376261Sjkh } 3386261Sjkh } 3396261Sjkh 3406261Sjkh /* clear interrupts */ 3416261Sjkh *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; 3426261Sjkh 3436261Sjkh return 1; 3446261Sjkh} 3456261Sjkh 3466261Sjkh 3476261Sjkhint 3486261Sjkhcyopen(dev_t dev, int flag, int mode, struct proc *p) 3496261Sjkh{ 3506261Sjkh u_int unit = UNIT(dev); 3516261Sjkh struct cy *infop; 3526261Sjkh cy_addr base; 3536261Sjkh struct tty *tp; 3546261Sjkh int error = 0; 3556261Sjkh u_char carrier; 3566261Sjkh 3576261Sjkh if (unit >= PORTS_PER_CYCLOM) 3586261Sjkh return (ENXIO); 3596261Sjkh 3606261Sjkh infop = info[unit]; 3616261Sjkh base = infop->base_addr; 3626261Sjkh if (!cy_tty[unit]) 3636261Sjkh infop->tty = cy_tty[unit] = ttymalloc(); 3646261Sjkh tp = infop->tty; 3656261Sjkh 3666261Sjkh tp->t_oproc = cystart; 3676261Sjkh tp->t_param = cyparam; 3686261Sjkh tp->t_dev = dev; 3696261Sjkh if (!(tp->t_state & TS_ISOPEN)) { 3706261Sjkh tp->t_state |= TS_WOPEN; 3716261Sjkh ttychars(tp); 3726261Sjkh if (tp->t_ispeed == 0) { 3736261Sjkh tp->t_iflag = TTYDEF_IFLAG; 3746261Sjkh tp->t_oflag = TTYDEF_OFLAG; 3756261Sjkh tp->t_cflag = TTYDEF_CFLAG; 3766261Sjkh tp->t_lflag = TTYDEF_LFLAG; 3776261Sjkh tp->t_ispeed = tp->t_ospeed = cydefaultrate; 3786261Sjkh } 3796261Sjkh 3806261Sjkh (void) spltty(); 3816261Sjkh cy_channel_init(unit, 1); /* reset the hardware */ 3826261Sjkh 3836261Sjkh /* 3846261Sjkh * raise dtr and generally set things up correctly. this 3856261Sjkh * has the side-effect of selecting the appropriate cd1400 3866261Sjkh * channel, to help us with subsequent channel control stuff 3876261Sjkh */ 3886261Sjkh cyparam(tp, &tp->t_termios); 3896261Sjkh 3906261Sjkh /* check carrier, and set t_state's TS_CARR_ON flag accordingly */ 3916261Sjkh infop->modem_sig = *(base + CD1400_MSVR); 3926261Sjkh carrier = infop->modem_sig & CD1400_MSVR_CD; 3936261Sjkh 3946261Sjkh if (carrier || (infop->carrier_mode >= 2)) 3956261Sjkh tp->t_state |= TS_CARR_ON; 3966261Sjkh else 3976261Sjkh tp->t_state &=~ TS_CARR_ON; 3986261Sjkh 3996261Sjkh /* 4006261Sjkh * enable modem & rx interrupts - relies on cyparam() 4016261Sjkh * having selected the appropriate cd1400 channel 4026261Sjkh */ 4036261Sjkh infop->intr_enable = (1 << 7) | (1 << 4); 4046261Sjkh *(base + CD1400_SRER) = infop->intr_enable; 4056261Sjkh 4066261Sjkh ttsetwater(tp); 4076261Sjkh } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) 4086261Sjkh return (EBUSY); 4096261Sjkh 4106261Sjkh if (!(flag & O_NONBLOCK)) 4116261Sjkh while (!(tp->t_cflag & CLOCAL) && 4126261Sjkh !(tp->t_state & TS_CARR_ON) && !error) 4136261Sjkh error = ttysleep(tp, (caddr_t)&tp->t_rawq, 4146261Sjkh TTIPRI|PCATCH, ttopen, 0); 4156261Sjkh (void) spl0(); 4166261Sjkh 4176261Sjkh if (!error) 4186261Sjkh error = (*linesw[(u_char)tp->t_line].l_open)(dev, tp); 4196261Sjkh return (error); 4206261Sjkh} /* end of cyopen() */ 4216261Sjkh 4226261Sjkh 4236261Sjkhvoid 4246261Sjkhcyclose_wakeup(caddr_t arg) 4256261Sjkh{ 4266261Sjkh wakeup(arg); 4276261Sjkh} /* end of cyclose_wakeup() */ 4286261Sjkh 4296261Sjkh 4306261Sjkhint 4316261Sjkhcyclose(dev_t dev, int flag, int mode, struct proc *p) 4326261Sjkh{ 4336261Sjkh u_int unit = UNIT(dev); 4346261Sjkh struct cy *infop = info[unit]; 4356261Sjkh struct tty *tp = infop->tty; 4366261Sjkh cy_addr base = infop->base_addr; 4376261Sjkh int s; 4386261Sjkh 4396261Sjkh (*linesw[(u_char)tp->t_line].l_close)(tp, flag); 4406261Sjkh 4416261Sjkh s = spltty(); 4426261Sjkh /* select the appropriate channel on the CD1400 */ 4436261Sjkh *(base + CD1400_CAR) = (u_char)(unit & 0x03); 4446261Sjkh 4456261Sjkh /* disable this channel and lower DTR */ 4466261Sjkh infop->intr_enable = 0; 4476261Sjkh *(base + CD1400_SRER) = (u_char)0; /* no intrs */ 4486261Sjkh *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; /* no DTR */ 4496261Sjkh infop->modem_sig &= ~CD1400_MSVR_DTR; 4506261Sjkh 4516261Sjkh /* disable receiver (leave transmitter enabled) */ 4526261Sjkh infop->channel_control = (1 << 4) | (1 << 3) | 1; 4536261Sjkh cd1400_channel_cmd(base, infop->channel_control); 4546261Sjkh splx(s); 4556261Sjkh 4566261Sjkh ttyclose(tp); 4576261Sjkh#ifdef broken /* session holds a ref to the tty; can't deallocate */ 4586261Sjkh ttyfree(tp); 4596261Sjkh infop->tty = cy_tty[unit] = (struct tty *)NULL; 4606261Sjkh#endif 4616261Sjkh 4626261Sjkh if (infop->dtrwait) { 4636261Sjkh int error; 4646261Sjkh 4656261Sjkh timeout(cyclose_wakeup, (caddr_t)&infop->dtrwait, infop->dtrwait); 4666261Sjkh do { 4676261Sjkh error = tsleep((caddr_t)&infop->dtrwait, 4686261Sjkh TTIPRI|PCATCH, "cyclose", 0); 4696261Sjkh } while (error == ERESTART); 4706261Sjkh } 4716261Sjkh 4726261Sjkh return 0; 4736261Sjkh} /* end of cyclose() */ 4746261Sjkh 4756261Sjkh 4766261Sjkhint 4776261Sjkhcyread(dev_t dev, struct uio *uio, int flag) 4786261Sjkh{ 4796261Sjkh u_int unit = UNIT(dev); 4806261Sjkh struct tty *tp = info[unit]->tty; 4816261Sjkh 4826261Sjkh return (*linesw[(u_char)tp->t_line].l_read)(tp, uio, flag); 4836261Sjkh} /* end of cyread() */ 4846261Sjkh 4856261Sjkh 4866261Sjkhint 4876261Sjkhcywrite(dev_t dev, struct uio *uio, int flag) 4886261Sjkh{ 4896261Sjkh u_int unit = UNIT(dev); 4906261Sjkh struct tty *tp = info[unit]->tty; 4916261Sjkh 4926261Sjkh#ifdef Smarts 4936261Sjkh /* XXX duplicate ttwrite(), but without so much output processing on 4946261Sjkh * CR & LF chars. Hardly worth the effort, given that high-throughput 4956261Sjkh * sessions are raw anyhow. 4966261Sjkh */ 4976261Sjkh#else 4986261Sjkh return (*linesw[(u_char)tp->t_line].l_write)(tp, uio, flag); 4996261Sjkh#endif 5006261Sjkh} /* end of cywrite() */ 5016261Sjkh 5026261Sjkh 5036261Sjkh#ifdef Smarts 5046261Sjkh/* standard line discipline input routine */ 5056261Sjkhint 5066261Sjkhcyinput(int c, struct tty *tp) 5076261Sjkh{ 5086261Sjkh /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK 5096261Sjkh * bits, as they are done by the CD1400. Hardly worth the effort, 5106261Sjkh * given that high-throughput sessions are raw anyhow. 5116261Sjkh */ 5126261Sjkh} /* end of cyinput() */ 5136261Sjkh#endif /* Smarts */ 5146261Sjkh 5156261Sjkh 5166261Sjkhinline static void 5176261Sjkhservice_upper_rx(int unit) 5186261Sjkh{ 5196261Sjkh struct cy *ip = info[unit]; 5206261Sjkh struct tty *tp = ip->tty; 5216261Sjkh struct cy_buf *buf; 5226261Sjkh int i; 5236261Sjkh u_char *ch; 5246261Sjkh 5256261Sjkh buf = ip->rx_buf; 5266261Sjkh 5276261Sjkh /* give service_rx() a new one */ 5286261Sjkh disable_intr(); /* faster than spltty() */ 5296261Sjkh ip->rx_buf = buf->next_buf; 5306261Sjkh enable_intr(); 5316261Sjkh 5326261Sjkh if (tp->t_state & TS_ISOPEN) { 5336261Sjkh ch = buf->buf; 5346261Sjkh i = buf->next_char - buf->buf; 5356261Sjkh 5366261Sjkh#ifdef FastRawInput 5376261Sjkh /* try to avoid calling the line discipline stuff if we can */ 5386261Sjkh if ((tp->t_line == 0) && 5396261Sjkh !(tp->t_iflag & (ICRNL | IMAXBEL | INLCR)) && 5406261Sjkh !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | 5416261Sjkh ISIG | PENDIN)) && 5426261Sjkh !(tp->t_state & (TS_CNTTB | TS_LNCH))) { 5436261Sjkh 5446261Sjkh i = b_to_q(ch, i, &tp->t_rawq); 5456261Sjkh if (i) { 5466261Sjkh /* 5476261Sjkh * we have no RTS flow control support on cy-8 5486261Sjkh * boards, so this is really just tough luck 5496261Sjkh */ 5506261Sjkh 5516261Sjkh log(LOG_WARNING, "cy%d: tty input queue overflow\n", 5526261Sjkh unit); 5536261Sjkh } 5546261Sjkh 5556261Sjkh ttwakeup(tp); /* notify any readers */ 5566261Sjkh } 5576261Sjkh else 5586261Sjkh#endif /* FastRawInput */ 5596261Sjkh { 5606261Sjkh while (i--) 5616261Sjkh (*linesw[(u_char)tp->t_line].l_rint)((int)*ch++, tp); 5626261Sjkh } 5636261Sjkh } 5646261Sjkh 5656261Sjkh /* clear the buffer we've just processed */ 5666261Sjkh buf->next_char = buf->buf; 5676261Sjkh buf->free = CY_RX_BUF_SIZE; 5686261Sjkh} /* end of service_upper_rx() */ 5696261Sjkh 5706261Sjkh 5716261Sjkh#ifdef TxBuffer 5726261Sjkhstatic void 5736261Sjkhservice_upper_tx(int unit) 5746261Sjkh{ 5756261Sjkh struct cy *ip = info[unit]; 5766261Sjkh struct tty *tp = ip->tty; 5776261Sjkh 5786261Sjkh tp->t_state &=~ (TS_BUSY|TS_FLUSH); 5796261Sjkh 5806261Sjkh if (tp->t_outq.c_cc <= tp->t_lowat) { 5816261Sjkh if (tp->t_state&TS_ASLEEP) { 5826261Sjkh tp->t_state &= ~TS_ASLEEP; 5836261Sjkh wakeup((caddr_t)&tp->t_outq); 5846261Sjkh } 5856261Sjkh selwakeup(&tp->t_wsel); 5866261Sjkh } 5876261Sjkh 5886261Sjkh if (tp->t_outq.c_cc > 0) { 5896261Sjkh struct cy_ring *txq = &ip->tx_buf; 5906261Sjkh int free_count = CY_TX_BUF_SIZE - ip->tx_buf.used; 5916261Sjkh u_char *cp = txq->tail; 5926261Sjkh int count; 5936261Sjkh int chars_done; 5946261Sjkh 5956261Sjkh tp->t_state |= TS_BUSY; 5966261Sjkh 5976261Sjkh /* find the largest contig. copy we can do */ 5986261Sjkh count = ((txq->endish - cp) > free_count) ? 5996261Sjkh free_count : txq->endish - cp; 6006261Sjkh 6016261Sjkh count = ((cp + free_count) > txq->endish) ? 6026261Sjkh txq->endish - cp : free_count; 6036261Sjkh 6046261Sjkh /* copy the first slab */ 6056261Sjkh chars_done = q_to_b(&tp->t_outq, cp, count); 6066261Sjkh 6076261Sjkh /* check for wrap-around time */ 6086261Sjkh cp += chars_done; 6096261Sjkh if (cp == txq->endish) 6106261Sjkh cp = txq->buf; /* back to the start */ 6116261Sjkh 6126261Sjkh /* copy anything else, after we've wrapped around */ 6136261Sjkh if ((chars_done == count) && (count != free_count)) { 6146261Sjkh /* copy the second slab */ 6156261Sjkh count = q_to_b(&tp->t_outq, cp, free_count - count); 6166261Sjkh cp += count; 6176261Sjkh chars_done += count; 6186261Sjkh } 6196261Sjkh 6206261Sjkh /* 6216261Sjkh * update queue, protecting ourselves from any rampant 6226261Sjkh * lower-layers 6236261Sjkh */ 6246261Sjkh disable_intr(); 6256261Sjkh txq->tail = cp; 6266261Sjkh txq->used += chars_done; 6276261Sjkh enable_intr(); 6286261Sjkh } 6296261Sjkh 6306261Sjkh if (!tp->t_outq.c_cc) 6316261Sjkh tp->t_state &=~ TS_BUSY; 6326261Sjkh} /* end of service_upper_tx() */ 6336261Sjkh#endif /* TxBuffer */ 6346261Sjkh 6356261Sjkh 6366261Sjkhinline static void 6376261Sjkhservice_upper_mdm(int unit) 6386261Sjkh{ 6396261Sjkh struct cy *ip = info[unit]; 6406261Sjkh 6416261Sjkh if (ip->carrier_delta) { 6426261Sjkh int carrier = ip->modem_sig & CD1400_MSVR_CD; 6436261Sjkh struct tty *tp = ip->tty; 6446261Sjkh 6456261Sjkh if (!(*linesw[(u_char)tp->t_line].l_modem)(tp, carrier)) { 6466261Sjkh cy_addr base = ip->base_addr; 6476261Sjkh 6486261Sjkh /* clear DTR */ 6496261Sjkh disable_intr(); 6506261Sjkh *(base + CD1400_CAR) = (u_char)(unit & 0x03); 6516261Sjkh *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; 6526261Sjkh ip->modem_sig &= ~CD1400_MSVR_DTR; 6536261Sjkh ip->carrier_delta = 0; 6546261Sjkh enable_intr(); 6556261Sjkh } 6566261Sjkh else { 6576261Sjkh disable_intr(); 6586261Sjkh ip->carrier_delta = 0; 6596261Sjkh enable_intr(); 6606261Sjkh } 6616261Sjkh } 6626261Sjkh} /* end of service_upper_mdm() */ 6636261Sjkh 6646261Sjkh 6656261Sjkh/* upper level character processing routine */ 6666261Sjkhvoid 6676261Sjkhcytimeout(caddr_t ptr) 6686261Sjkh{ 6696261Sjkh int unit; 6706261Sjkh 6716261Sjkh timeout_scheduled = 0; 6726261Sjkh 6736261Sjkh#ifdef CyDebug 6746261Sjkh cy_timeouts++; 6756261Sjkh#endif 6766261Sjkh 6776261Sjkh /* check each port in turn */ 6786261Sjkh for (unit = 0; unit < NCY*PORTS_PER_CYCLOM; unit++) { 6796261Sjkh struct cy *ip = info[unit]; 6806261Sjkh#ifndef TxBuffer 6816261Sjkh struct tty *tp = ip->tty; 6826261Sjkh#endif 6836261Sjkh 6846261Sjkh /* ignore anything that is not open */ 6856261Sjkh if (!ip->tty) 6866261Sjkh continue; 6876261Sjkh 6886261Sjkh /* 6896261Sjkh * any received chars to handle? (doesn't matter if intr routine 6906261Sjkh * kicks in while we're testing this) 6916261Sjkh */ 6926261Sjkh if (ip->rx_buf->free != CY_RX_BUF_SIZE) 6936261Sjkh service_upper_rx(unit); 6946261Sjkh 6956261Sjkh#ifdef TxBuffer 6966261Sjkh /* anything to add to the transmit buffer (low-water mark)? */ 6976261Sjkh if (ip->tx_buf.used < CY_TX_BUF_SIZE/2) 6986261Sjkh service_upper_tx(unit); 6996261Sjkh#else 7006261Sjkh if (tp->t_outq.c_cc <= tp->t_lowat) { 7016261Sjkh if (tp->t_state&TS_ASLEEP) { 7026261Sjkh tp->t_state &= ~TS_ASLEEP; 7036261Sjkh wakeup((caddr_t)&tp->t_outq); 7046261Sjkh } 7056261Sjkh selwakeup(&tp->t_wsel); 7066261Sjkh } 7076261Sjkh#endif 7086261Sjkh 7096261Sjkh /* anything modem signals altered? */ 7106261Sjkh service_upper_mdm(unit); 7116261Sjkh 7126261Sjkh /* any overruns to log? */ 7136261Sjkh#ifdef LogOverruns 7146261Sjkh if (ip->fifo_overrun) { 7156261Sjkh /* 7166261Sjkh * turn off the alarm - not important enough to bother 7176261Sjkh * with interrupt protection. 7186261Sjkh */ 7196261Sjkh ip->fifo_overrun = 0; 7206261Sjkh 7216261Sjkh log(LOG_WARNING, "cy%d: receive fifo overrun\n", unit); 7226261Sjkh } 7236261Sjkh#endif 7246261Sjkh if (ip->rx_buf_overrun) { 7256261Sjkh /* 7266261Sjkh * turn off the alarm - not important enough to bother 7276261Sjkh * with interrupt protection. 7286261Sjkh */ 7296261Sjkh ip->rx_buf_overrun = 0; 7306261Sjkh 7316261Sjkh log(LOG_WARNING, "cy%d: receive buffer full\n", unit); 7326261Sjkh } 7336261Sjkh } 7346261Sjkh} /* cytimeout() */ 7356261Sjkh 7366261Sjkh 7376261Sjkhinline static void 7386261Sjkhschedule_upper_service(void) 7396261Sjkh{ 7406261Sjkh#ifdef CyDebug 7416261Sjkh cy_timeout_req++; 7426261Sjkh#endif 7436261Sjkh 7446261Sjkh if (!timeout_scheduled) { 7456261Sjkh timeout(cytimeout, (caddr_t)0, 1); /* call next tick */ 7466261Sjkh timeout_scheduled = 1; 7476261Sjkh } 7486261Sjkh} /* end of schedule_upper_service() */ 7496261Sjkh 7506261Sjkh 7516261Sjkh/* initialise a channel on the cyclom board */ 7526261Sjkh 7536261Sjkhstatic void 7546261Sjkhcy_channel_init(dev_t dev, int reset) 7556261Sjkh{ 7566261Sjkh u_int unit = UNIT(dev); 7576261Sjkh int carrier_mode = CARRIER_MODE(dev); 7586261Sjkh struct cy *ip = info[unit]; 7596261Sjkh cy_addr base = ip->base_addr; 7606261Sjkh struct tty *tp = ip->tty; 7616261Sjkh struct cy_buf *buf, *next_buf; 7626261Sjkh int i; 7636261Sjkh#ifndef PollMode 7646261Sjkh u_char cd1400_unit; 7656261Sjkh#endif 7666261Sjkh 7676261Sjkh /* clear the structure and refill it */ 7686261Sjkh bzero(ip, sizeof(struct cy)); 7696261Sjkh ip->base_addr = base; 7706261Sjkh ip->tty = tp; 7716261Sjkh ip->carrier_mode = carrier_mode; 7726261Sjkh 7736261Sjkh /* select channel of the CD1400 */ 7746261Sjkh *(base + CD1400_CAR) = (u_char)(unit & 0x03); 7756261Sjkh 7766261Sjkh if (reset) 7776261Sjkh cd1400_channel_cmd(base, 0x80); /* reset the channel */ 7786261Sjkh 7796261Sjkh /* set LIVR to 0 - intr routines depend on this */ 7806261Sjkh *(base + CD1400_LIVR) = 0; 7816261Sjkh 7826261Sjkh#ifndef PollMode 7836261Sjkh /* set top four bits of {R,T,M}ICR to the cd1400 7846261Sjkh * number, cd1400_unit 7856261Sjkh */ 7866261Sjkh cd1400_unit = unit / CD1400_NO_OF_CHANNELS; 7876261Sjkh *(base + CD1400_RICR) = (u_char)(cd1400_unit << 4); 7886261Sjkh *(base + CD1400_TICR) = (u_char)(cd1400_unit << 4); 7896261Sjkh *(base + CD1400_MICR) = (u_char)(cd1400_unit << 4); 7906261Sjkh#endif 7916261Sjkh 7926261Sjkh ip->dtrwait = hz/4; /* quarter of a second */ 7936261Sjkh 7946261Sjkh /* setup low-level buffers */ 7956261Sjkh i = CY_RX_BUFS; 7966261Sjkh ip->rx_buf = next_buf = &ip->rx_buf_pool[0]; 7976261Sjkh while (i--) { 7986261Sjkh buf = &ip->rx_buf_pool[i]; 7996261Sjkh 8006261Sjkh buf->next_char = buf->buf; /* first char to use */ 8016261Sjkh buf->free = CY_RX_BUF_SIZE; /* i.e. empty */ 8026261Sjkh buf->next_buf = next_buf; /* where to go next */ 8036261Sjkh next_buf = buf; 8046261Sjkh } 8056261Sjkh 8066261Sjkh#ifdef TxBuffer 8076261Sjkh ip->tx_buf.endish = ip->tx_buf.buf + CY_TX_BUF_SIZE; 8086261Sjkh 8096261Sjkh /* clear the low-level tx buffer */ 8106261Sjkh ip->tx_buf.head = ip->tx_buf.tail = ip->tx_buf.buf; 8116261Sjkh ip->tx_buf.used = 0; 8126261Sjkh#endif 8136261Sjkh 8146261Sjkh /* clear the low-level rx buffer */ 8156261Sjkh ip->rx_buf->next_char = ip->rx_buf->buf; /* first char to use */ 8166261Sjkh ip->rx_buf->free = CY_RX_BUF_SIZE; /* completely empty */ 8176261Sjkh} /* end of cy_channel_init() */ 8186261Sjkh 8196261Sjkh 8206261Sjkh/* service a receive interrupt */ 8216261Sjkhinline static void 8226261Sjkhservice_rx(int cd, caddr_t base) 8236261Sjkh{ 8246261Sjkh struct cy *infop; 8256261Sjkh unsigned count; 8266261Sjkh int ch; 8276261Sjkh u_char serv_type, channel; 8286261Sjkh#ifdef PollMode 8296261Sjkh u_char save_rir, save_car; 8306261Sjkh#endif 8316261Sjkh 8326261Sjkh /* setup */ 8336261Sjkh#ifdef PollMode 8346261Sjkh save_rir = *(base + CD1400_RIR); 8356261Sjkh channel = cd * CD1400_NO_OF_CHANNELS + (save_rir & 0x3); 8366261Sjkh save_car = *(base + CD1400_CAR); 8376261Sjkh *(base + CD1400_CAR) = save_rir; /* enter modem service */ 8386261Sjkh serv_type = *(base + CD1400_RIVR); 8396261Sjkh#else 8406261Sjkh serv_type = *(base + CD1400_SVCACKR); /* ack receive service */ 8416261Sjkh channel = ((u_char)*(base + CD1400_RICR)) >> 2; /* get cyclom channel # */ 8426261Sjkh 8436261Sjkh#ifdef CyDebug 8446261Sjkh if (channel >= PORTS_PER_CYCLOM) { 8456261Sjkh printf("cy: service_rx - channel %02x\n", channel); 8466261Sjkh panic("cy: service_rx - bad channel"); 8476261Sjkh } 8486261Sjkh#endif 8496261Sjkh#endif 8506261Sjkh 8516261Sjkh infop = info[channel]; 8526261Sjkh 8536261Sjkh /* read those chars */ 8546261Sjkh if (serv_type & CD1400_RIVR_EXCEPTION) { 8556261Sjkh /* read the exception status */ 8566261Sjkh u_char status = *(base + CD1400_RDSR); 8576261Sjkh 8586261Sjkh /* XXX is it a break? Do something if it is! */ 8596261Sjkh 8606261Sjkh /* XXX is IGNPAR not set? Store a null in the buffer. */ 8616261Sjkh 8626261Sjkh#ifdef LogOverruns 8636261Sjkh if (status & CD1400_RDSR_OVERRUN) { 8646261Sjkh#if 0 8656261Sjkh ch |= TTY_PE; /* for SLIP */ 8666261Sjkh#endif 8676261Sjkh infop->fifo_overrun++; 8686261Sjkh } 8696261Sjkh#endif 8706261Sjkh infop->recv_exception++; 8716261Sjkh } 8726261Sjkh else { 8736261Sjkh struct cy_buf *buf = infop->rx_buf; 8746261Sjkh 8756261Sjkh count = (u_char)*(base + CD1400_RDCR); /* how many to read? */ 8766261Sjkh infop->recv_normal += count; 8776261Sjkh if (buf->free < count) { 8786261Sjkh infop->rx_buf_overrun += count; 8796261Sjkh 8806261Sjkh /* read & discard everything */ 8816261Sjkh while (count--) 8826261Sjkh ch = (u_char)*(base + CD1400_RDSR); 8836261Sjkh } 8846261Sjkh else { 8856261Sjkh /* slurp it into our low-level buffer */ 8866261Sjkh buf->free -= count; 8876261Sjkh while (count--) { 8886261Sjkh ch = (u_char)*(base + CD1400_RDSR); /* read the char */ 8896261Sjkh *(buf->next_char++) = ch; 8906261Sjkh } 8916261Sjkh } 8926261Sjkh } 8936261Sjkh 8946261Sjkh#ifdef PollMode 8956261Sjkh *(base + CD1400_RIR) = (u_char)(save_rir & 0x3f); /* terminate service context */ 8966261Sjkh#else 8976261Sjkh *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ 8986261Sjkh#endif 8996261Sjkh} /* end of service_rx */ 9006261Sjkh 9016261Sjkh 9026261Sjkh/* service a transmit interrupt */ 9036261Sjkhinline static void 9046261Sjkhservice_tx(int cd, caddr_t base) 9056261Sjkh{ 9066261Sjkh struct cy *ip; 9076261Sjkh#ifdef TxBuffer 9086261Sjkh struct cy_ring *txq; 9096261Sjkh#else 9106261Sjkh struct tty *tp; 9116261Sjkh#endif 9126261Sjkh u_char channel; 9136261Sjkh#ifdef PollMode 9146261Sjkh u_char save_tir, save_car; 9156261Sjkh#else 9166261Sjkh u_char vector; 9176261Sjkh#endif 9186261Sjkh 9196261Sjkh /* setup */ 9206261Sjkh#ifdef PollMode 9216261Sjkh save_tir = *(base + CD1400_TIR); 9226261Sjkh channel = cd * CD1400_NO_OF_CHANNELS + (save_tir & 0x3); 9236261Sjkh save_car = *(base + CD1400_CAR); 9246261Sjkh *(base + CD1400_CAR) = save_tir; /* enter tx service */ 9256261Sjkh#else 9266261Sjkh vector = *(base + CD1400_SVCACKT); /* ack transmit service */ 9276261Sjkh channel = ((u_char)*(base + CD1400_TICR)) >> 2; /* get cyclom channel # */ 9286261Sjkh 9296261Sjkh#ifdef CyDebug 9306261Sjkh if (channel >= PORTS_PER_CYCLOM) { 9316261Sjkh printf("cy: service_tx - channel %02x\n", channel); 9326261Sjkh panic("cy: service_tx - bad channel"); 9336261Sjkh } 9346261Sjkh#endif 9356261Sjkh#endif 9366261Sjkh 9376261Sjkh ip = info[channel]; 9386261Sjkh#ifdef TxBuffer 9396261Sjkh txq = &ip->tx_buf; 9406261Sjkh 9416261Sjkh if (txq->used > 0) { 9426261Sjkh cy_addr base = ip->base_addr; 9436261Sjkh int count = min(CD1400_FIFOSIZE, txq->used); 9446261Sjkh int chars_done = count; 9456261Sjkh u_char *cp = txq->head; 9466261Sjkh u_char *buf_end = txq->endish; 9476261Sjkh 9486261Sjkh /* ip->state |= CY_BUSY; */ 9496261Sjkh while (count--) { 9506261Sjkh *(base + CD1400_TDR) = *cp++; 9516261Sjkh if (cp >= buf_end) 9526261Sjkh cp = txq->buf; 9536261Sjkh }; 9546261Sjkh txq->head = cp; 9556261Sjkh txq->used -= chars_done; /* important that this is atomic */ 9566261Sjkh ip->xmit += chars_done; 9576261Sjkh } 9586261Sjkh 9596261Sjkh /* 9606261Sjkh * disable tx intrs if no more chars to send. we re-enable 9616261Sjkh * them in cystart() 9626261Sjkh */ 9636261Sjkh if (!txq->used) { 9646261Sjkh ip->intr_enable &=~ (1 << 2); 9656261Sjkh *(base + CD1400_SRER) = ip->intr_enable; 9666261Sjkh /* ip->state &= ~CY_BUSY; */ 9676261Sjkh } 9686261Sjkh#else 9696261Sjkh tp = ip->tty; 9706261Sjkh 9716261Sjkh if (!(tp->t_state & TS_TTSTOP) && (tp->t_outq.c_cc > 0)) { 9726261Sjkh cy_addr base = ip->base_addr; 9736261Sjkh int count = min(CD1400_FIFOSIZE, tp->t_outq.c_cc); 9746261Sjkh 9756261Sjkh ip->xmit += count; 9766261Sjkh tp->t_state |= TS_BUSY; 9776261Sjkh while (count--) 9786261Sjkh *(base + CD1400_TDR) = getc(&tp->t_outq); 9796261Sjkh } 9806261Sjkh 9816261Sjkh /* 9826261Sjkh * disable tx intrs if no more chars to send. we re-enable them 9836261Sjkh * in cystart() 9846261Sjkh */ 9856261Sjkh if (!tp->t_outq.c_cc) { 9866261Sjkh ip->intr_enable &=~ (1 << 2); 9876261Sjkh *(base + CD1400_SRER) = ip->intr_enable; 9886261Sjkh tp->t_state &= ~TS_BUSY; 9896261Sjkh } 9906261Sjkh#endif 9916261Sjkh 9926261Sjkh#ifdef PollMode 9936261Sjkh *(base + CD1400_TIR) = (u_char)(save_tir & 0x3f); /* terminate service context */ 9946261Sjkh#else 9956261Sjkh *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ 9966261Sjkh#endif 9976261Sjkh} /* end of service_tx */ 9986261Sjkh 9996261Sjkh 10006261Sjkh/* service a modem status interrupt */ 10016261Sjkhinline static void 10026261Sjkhservice_mdm(int cd, caddr_t base) 10036261Sjkh{ 10046261Sjkh struct cy *infop; 10056261Sjkh u_char channel, deltas; 10066261Sjkh#ifdef PollMode 10076261Sjkh u_char save_mir, save_car; 10086261Sjkh#else 10096261Sjkh u_char vector; 10106261Sjkh#endif 10116261Sjkh 10126261Sjkh /* setup */ 10136261Sjkh#ifdef PollMode 10146261Sjkh save_mir = *(base + CD1400_MIR); 10156261Sjkh channel = cd * CD1400_NO_OF_CHANNELS + (save_mir & 0x3); 10166261Sjkh save_car = *(base + CD1400_CAR); 10176261Sjkh *(base + CD1400_CAR) = save_mir; /* enter modem service */ 10186261Sjkh#else 10196261Sjkh vector = *(base + CD1400_SVCACKM); /* ack modem service */ 10206261Sjkh channel = ((u_char)*(base + CD1400_MICR)) >> 2; /* get cyclom channel # */ 10216261Sjkh 10226261Sjkh#ifdef CyDebug 10236261Sjkh if (channel >= PORTS_PER_CYCLOM) { 10246261Sjkh printf("cy: service_mdm - channel %02x\n", channel); 10256261Sjkh panic("cy: service_mdm - bad channel"); 10266261Sjkh } 10276261Sjkh#endif 10286261Sjkh#endif 10296261Sjkh 10306261Sjkh infop = info[channel]; 10316261Sjkh 10326261Sjkh /* read the siggies and see what's changed */ 10336261Sjkh infop->modem_sig = (u_char)*(base + CD1400_MSVR); 10346261Sjkh deltas = (u_char)*(base + CD1400_MISR); 10356261Sjkh 10366261Sjkh if ((infop->carrier_mode <= 2) && (deltas & CD1400_MISR_CDd)) 10376261Sjkh /* something for the upper layer to deal with */ 10386261Sjkh infop->carrier_delta = 1; 10396261Sjkh 10406261Sjkh infop->mdm++; 10416261Sjkh 10426261Sjkh /* terminate service context */ 10436261Sjkh#ifdef PollMode 10446261Sjkh *(base + CD1400_MIR) = (u_char)(save_mir & 0x3f); 10456261Sjkh#else 10466261Sjkh *(base + CD1400_EOSRR) = (u_char)0; 10476261Sjkh#endif 10486261Sjkh} /* end of service_mdm */ 10496261Sjkh 10506261Sjkh 10516261Sjkhint 10526261Sjkhcyintr(int unit) 10536261Sjkh{ 10546261Sjkh int cd; 10556261Sjkh u_char status; 10566261Sjkh 10576261Sjkh /* check each CD1400 in turn */ 10586261Sjkh for (cd = 0; cd < CD1400s_PER_CYCLOM; cd++) { 10596261Sjkh cy_addr base = cyclom_base + cd*CD1400_MEMSIZE; 10606261Sjkh 10616261Sjkh /* poll to see if it has any work */ 10626261Sjkh while (status = (u_char)*(base + CD1400_SVRR)) { 10636261Sjkh#ifdef CyDebug 10646261Sjkh cy_svrr_probes++; 10656261Sjkh#endif 10666261Sjkh /* service requests as appropriate, giving priority to RX */ 10676261Sjkh if (status & CD1400_SVRR_RX) 10686261Sjkh service_rx(cd, base); 10696261Sjkh if (status & CD1400_SVRR_TX) 10706261Sjkh service_tx(cd, base); 10716261Sjkh if (status & CD1400_SVRR_MDM) 10726261Sjkh service_mdm(cd, base); 10736261Sjkh } 10746261Sjkh } 10756261Sjkh 10766261Sjkh /* request upper level service to deal with whatever happened */ 10776261Sjkh schedule_upper_service(); 10786261Sjkh 10796261Sjkh /* re-enable interrupts on the cyclom */ 10806261Sjkh *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; 10816261Sjkh 10826261Sjkh return 1; 10836261Sjkh} 10846261Sjkh 10856261Sjkh 10866261Sjkhint 10876261Sjkhcyioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) 10886261Sjkh{ 10896261Sjkh int unit = UNIT(dev); 10906261Sjkh struct cy *infop = info[unit]; 10916261Sjkh struct tty *tp = infop->tty; 10926261Sjkh int error; 10936261Sjkh 10946261Sjkh error = (*linesw[(u_char)tp->t_line].l_ioctl)(tp, cmd, data, flag, p); 10956261Sjkh if (error >= 0) 10966261Sjkh return (error); 10976261Sjkh error = ttioctl(tp, cmd, data, flag 10986261Sjkh#ifdef NetBSD 10996261Sjkh , p 11006261Sjkh#endif 11016261Sjkh ); 11026261Sjkh if (error >= 0) 11036261Sjkh return (error); 11046261Sjkh 11056261Sjkh switch (cmd) { 11066261Sjkh#ifdef notyet /* sigh - more junk to do XXX */ 11076261Sjkh case TIOCSBRK: 11086261Sjkh break; 11096261Sjkh case TIOCCBRK: 11106261Sjkh break; 11116261Sjkh case TIOCSDTR: 11126261Sjkh break; 11136261Sjkh case TIOCCDTR: 11146261Sjkh break; 11156261Sjkh 11166261Sjkh case TIOCMSET: 11176261Sjkh break; 11186261Sjkh case TIOCMBIS: 11196261Sjkh break; 11206261Sjkh case TIOCMBIC: 11216261Sjkh break; 11226261Sjkh#endif /* notyet */ 11236261Sjkh 11246261Sjkh case TIOCMGET: { 11256261Sjkh int bits = 0; 11266261Sjkh u_char status = infop->modem_sig; 11276261Sjkh 11286261Sjkh if (status & CD1400_MSVR_DTR) bits |= TIOCM_DTR | TIOCM_RTS; 11296261Sjkh if (status & CD1400_MSVR_CD) bits |= TIOCM_CD; 11306261Sjkh if (status & CD1400_MSVR_CTS) bits |= TIOCM_CTS; 11316261Sjkh if (status & CD1400_MSVR_DSR) bits |= TIOCM_DSR; 11326261Sjkh#ifdef CYCLOM_16 11336261Sjkh if (status & CD1400_MSVR_RI) bits |= TIOCM_RI; 11346261Sjkh#endif 11356261Sjkh if (infop->channel_control & 0x02) bits |= TIOCM_LE; 11366261Sjkh *(int *)data = bits; 11376261Sjkh break; 11386261Sjkh } 11396261Sjkh 11406261Sjkh#ifdef TIOCMSBIDIR 11416261Sjkh case TIOCMSBIDIR: 11426261Sjkh return (ENOTTY); 11436261Sjkh#endif /* TIOCMSBIDIR */ 11446261Sjkh 11456261Sjkh#ifdef TIOCMGBIDIR 11466261Sjkh case TIOCMGBIDIR: 11476261Sjkh return (ENOTTY); 11486261Sjkh#endif /* TIOCMGBIDIR */ 11496261Sjkh 11506261Sjkh#ifdef TIOCMSDTRWAIT 11516261Sjkh case TIOCMSDTRWAIT: 11526261Sjkh /* must be root to set dtr delay */ 11536261Sjkh if (p->p_ucred->cr_uid != 0) 11546261Sjkh return(EPERM); 11556261Sjkh 11566261Sjkh infop->dtrwait = *(u_int *)data; 11576261Sjkh break; 11586261Sjkh#endif /* TIOCMSDTRWAIT */ 11596261Sjkh 11606261Sjkh#ifdef TIOCMGDTRWAIT 11616261Sjkh case TIOCMGDTRWAIT: 11626261Sjkh *(u_int *)data = infop->dtrwait; 11636261Sjkh break; 11646261Sjkh#endif /* TIOCMGDTRWAIT */ 11656261Sjkh 11666261Sjkh default: 11676261Sjkh return (ENOTTY); 11686261Sjkh } 11696261Sjkh 11706261Sjkh return 0; 11716261Sjkh} /* end of cyioctl() */ 11726261Sjkh 11736261Sjkh 11746261Sjkhint 11756261Sjkhcyparam(struct tty *tp, struct termios *t) 11766261Sjkh{ 11776261Sjkh u_char unit = UNIT(tp->t_dev); 11786261Sjkh struct cy *infop = info[unit]; 11796261Sjkh cy_addr base = infop->base_addr; 11806261Sjkh int cflag = t->c_cflag; 11816261Sjkh int iflag = t->c_iflag; 11826261Sjkh int ispeed, ospeed; 11836261Sjkh int itimeout; 11846261Sjkh int iprescaler, oprescaler; 11856261Sjkh int s; 11866261Sjkh u_char cor_change = 0; 11876261Sjkh u_char opt; 11886261Sjkh 11896261Sjkh if (!t->c_ispeed) 11906261Sjkh t->c_ispeed = t->c_ospeed; 11916261Sjkh 11926261Sjkh s = spltty(); 11936261Sjkh 11946261Sjkh /* select the appropriate channel on the CD1400 */ 11956261Sjkh *(base + CD1400_CAR) = unit & 0x03; 11966261Sjkh 11976261Sjkh /* handle DTR drop on speed == 0 trick */ 11986261Sjkh if (t->c_ospeed == 0) { 11996261Sjkh *(base + CD1400_DTR) = CD1400_DTR_CLEAR; 12006261Sjkh infop->modem_sig &= ~CD1400_MSVR_DTR; 12016261Sjkh } 12026261Sjkh else { 12036261Sjkh *(base + CD1400_DTR) = CD1400_DTR_SET; 12046261Sjkh infop->modem_sig |= CD1400_MSVR_DTR; 12056261Sjkh } 12066261Sjkh 12076261Sjkh /* set baud rates if they've changed from last time */ 12086261Sjkh 12096261Sjkh if ((ospeed = cyspeed(t->c_ospeed, &oprescaler)) < 0) 12106261Sjkh return EINVAL; 12116261Sjkh *(base + CD1400_TBPR) = (u_char)ospeed; 12126261Sjkh *(base + CD1400_TCOR) = (u_char)oprescaler; 12136261Sjkh 12146261Sjkh if ((ispeed = cyspeed(t->c_ispeed, &iprescaler)) < 0) 12156261Sjkh return EINVAL; 12166261Sjkh *(base + CD1400_RBPR) = (u_char)ispeed; 12176261Sjkh *(base + CD1400_RCOR) = (u_char)iprescaler; 12186261Sjkh 12196261Sjkh /* 12206261Sjkh * set receive time-out period 12216261Sjkh * generate a rx interrupt if no new chars are received in 12226261Sjkh * this many ticks 12236261Sjkh * don't bother comparing old & new VMIN, VTIME and ispeed - it 12246261Sjkh * can't be much worse just to calculate and set it each time! 12256261Sjkh * certainly less hassle. :-) 12266261Sjkh */ 12276261Sjkh 12286261Sjkh /* 12296261Sjkh * calculate minimum timeout period: 12306261Sjkh * 5 ms or the time it takes to receive 1 char, rounded up to the 12316261Sjkh * next ms, whichever is greater 12326261Sjkh */ 12336261Sjkh if (t->c_ispeed > 0) { 12346261Sjkh itimeout = (t->c_ispeed > 2200) ? 5 : (10000/t->c_ispeed + 1); 12356261Sjkh 12366261Sjkh /* if we're using VTIME as an inter-char timeout, and it is set to 12376261Sjkh * be longer than the minimum calculated above, go for it 12386261Sjkh */ 12396261Sjkh if (t->c_cc[VMIN] && t->c_cc[VTIME] && t->c_cc[VTIME]*10 > itimeout) 12406261Sjkh itimeout = t->c_cc[VTIME]*10; 12416261Sjkh 12426261Sjkh /* store it, taking care not to overflow the byte-sized register */ 12436261Sjkh *(base + CD1400_RTPR) = (u_char)((itimeout <= 255) ? itimeout : 255); 12446261Sjkh } 12456261Sjkh 12466261Sjkh 12476261Sjkh /* 12486261Sjkh * channel control 12496261Sjkh * receiver enable 12506261Sjkh * transmitter enable (always set) 12516261Sjkh */ 12526261Sjkh opt = (1 << 4) | (1 << 3) | ((cflag & CREAD) ? (1 << 1) : 1); 12536261Sjkh if (opt != infop->channel_control) { 12546261Sjkh infop->channel_control = opt; 12556261Sjkh cd1400_channel_cmd(base, opt); 12566261Sjkh } 12576261Sjkh 12586261Sjkh#ifdef Smarts 12596261Sjkh /* set special chars */ 12606261Sjkh if (t->c_cc[VSTOP] != _POSIX_VDISABLE && 12616261Sjkh (t->c_cc[VSTOP] != infop->spec_char[0])) { 12626261Sjkh *(base + CD1400_SCHR1) = infop->spec_char[0] = t->c_cc[VSTOP]; 12636261Sjkh } 12646261Sjkh if (t->c_cc[VSTART] != _POSIX_VDISABLE && 12656261Sjkh (t->c_cc[VSTART] != infop->spec_char[1])) { 12666261Sjkh *(base + CD1400_SCHR2) = infop->spec_char[0] = t->c_cc[VSTART]; 12676261Sjkh } 12686261Sjkh if (t->c_cc[VINTR] != _POSIX_VDISABLE && 12696261Sjkh (t->c_cc[VINTR] != infop->spec_char[2])) { 12706261Sjkh *(base + CD1400_SCHR3) = infop->spec_char[0] = t->c_cc[VINTR]; 12716261Sjkh } 12726261Sjkh if (t->c_cc[VSUSP] != _POSIX_VDISABLE && 12736261Sjkh (t->c_cc[VSUSP] != infop->spec_char[3])) { 12746261Sjkh *(base + CD1400_SCHR4) = infop->spec_char[0] = t->c_cc[VSUSP]; 12756261Sjkh } 12766261Sjkh#endif 12776261Sjkh 12786261Sjkh /* 12796261Sjkh * set channel option register 1 - 12806261Sjkh * parity mode 12816261Sjkh * stop bits 12826261Sjkh * char length 12836261Sjkh */ 12846261Sjkh opt = 0; 12856261Sjkh /* parity */ 12866261Sjkh if (cflag & PARENB) { 12876261Sjkh if (cflag & PARODD) 12886261Sjkh opt |= 1 << 7; 12896261Sjkh opt |= 2 << 5; /* normal parity mode */ 12906261Sjkh } 12916261Sjkh if (!(iflag & INPCK)) 12926261Sjkh opt |= 1 << 4; /* ignore parity */ 12936261Sjkh /* stop bits */ 12946261Sjkh if (cflag & CSTOPB) 12956261Sjkh opt |= 2 << 2; 12966261Sjkh /* char length */ 12976261Sjkh opt |= (cflag & CSIZE) >> 8; /* nasty, but fast */ 12986261Sjkh if (opt != infop->cor[0]) { 12996261Sjkh cor_change |= 1 << 1; 13006261Sjkh *(base + CD1400_COR1) = opt; 13016261Sjkh } 13026261Sjkh 13036261Sjkh /* 13046261Sjkh * set channel option register 2 - 13056261Sjkh * flow control 13066261Sjkh */ 13076261Sjkh opt = 0; 13086261Sjkh#ifdef Smarts 13096261Sjkh if (iflag & IXANY) 13106261Sjkh opt |= 1 << 7; /* auto output restart on any char after XOFF */ 13116261Sjkh if (iflag & IXOFF) 13126261Sjkh opt |= 1 << 6; /* auto XOFF output flow-control */ 13136261Sjkh#endif 13146261Sjkh#ifndef ALWAYS_RTS_CTS 13156261Sjkh if (cflag & CCTS_OFLOW) 13166261Sjkh#endif 13176261Sjkh opt |= 1 << 1; /* auto CTS flow-control */ 13186261Sjkh 13196261Sjkh if (opt != infop->cor[1]) { 13206261Sjkh cor_change |= 1 << 2; 13216261Sjkh *(base + CD1400_COR2) = opt; 13226261Sjkh } 13236261Sjkh 13246261Sjkh /* 13256261Sjkh * set channel option register 3 - 13266261Sjkh * receiver FIFO interrupt threshold 13276261Sjkh * flow control 13286261Sjkh */ 13296261Sjkh opt = RxFifoThreshold; /* rx fifo threshold */ 13306261Sjkh#ifdef Smarts 13316261Sjkh if (t->c_lflag & ICANON) 13326261Sjkh opt |= 1 << 6; /* detect INTR & SUSP chars */ 13336261Sjkh if (iflag & IXOFF) 13346261Sjkh opt |= (1 << 5) | (1 << 4); /* transparent in-band flow control */ 13356261Sjkh#endif 13366261Sjkh if (opt != infop->cor[2]) { 13376261Sjkh cor_change |= 1 << 3; 13386261Sjkh *(base + CD1400_COR3) = opt; 13396261Sjkh } 13406261Sjkh 13416261Sjkh 13426261Sjkh /* notify the CD1400 if COR1-3 have changed */ 13436261Sjkh if (cor_change) { 13446261Sjkh cor_change |= 1 << 6; /* COR change flag */ 13456261Sjkh cd1400_channel_cmd(base, cor_change); 13466261Sjkh } 13476261Sjkh 13486261Sjkh /* 13496261Sjkh * set channel option register 4 - 13506261Sjkh * CR/NL processing 13516261Sjkh * break processing 13526261Sjkh * received exception processing 13536261Sjkh */ 13546261Sjkh opt = 0; 13556261Sjkh if (iflag & IGNCR) 13566261Sjkh opt |= 1 << 7; 13576261Sjkh#ifdef Smarts 13586261Sjkh /* 13596261Sjkh * we need a new ttyinput() for this, as we don't want to 13606261Sjkh * have ICRNL && INLCR being done in both layers, or to have 13616261Sjkh * synchronisation problems 13626261Sjkh */ 13636261Sjkh if (iflag & ICRNL) 13646261Sjkh opt |= 1 << 6; 13656261Sjkh if (iflag & INLCR) 13666261Sjkh opt |= 1 << 5; 13676261Sjkh#endif 13686261Sjkh if (iflag & IGNBRK) 13696261Sjkh opt |= 1 << 4; 13706261Sjkh if (!(iflag & BRKINT)) 13716261Sjkh opt |= 1 << 3; 13726261Sjkh if (iflag & IGNPAR) 13736261Sjkh#ifdef LogOverruns 13746261Sjkh opt |= 0; /* broken chars cause receive exceptions */ 13756261Sjkh#else 13766261Sjkh opt |= 2; /* discard broken chars */ 13776261Sjkh#endif 13786261Sjkh else { 13796261Sjkh if (iflag & PARMRK) 13806261Sjkh opt |= 4; /* precede broken chars with 0xff 0x0 */ 13816261Sjkh else 13826261Sjkh#ifdef LogOverruns 13836261Sjkh opt |= 0; /* broken chars cause receive exceptions */ 13846261Sjkh#else 13856261Sjkh opt |= 3; /* convert framing/parity errs to nulls */ 13866261Sjkh#endif 13876261Sjkh } 13886261Sjkh *(base + CD1400_COR4) = opt; 13896261Sjkh 13906261Sjkh /* 13916261Sjkh * set channel option register 5 - 13926261Sjkh */ 13936261Sjkh opt = 0; 13946261Sjkh if (iflag & ISTRIP) 13956261Sjkh opt |= 1 << 7; 13966261Sjkh if (t->c_iflag & IEXTEN) { 13976261Sjkh opt |= 1 << 6; /* enable LNEXT (e.g. ctrl-v quoting) handling */ 13986261Sjkh } 13996261Sjkh#ifdef Smarts 14006261Sjkh if (t->c_oflag & ONLCR) 14016261Sjkh opt |= 1 << 1; 14026261Sjkh if (t->c_oflag & OCRNL) 14036261Sjkh opt |= 1; 14046261Sjkh#endif 14056261Sjkh *(base + CD1400_COR5) = opt; 14066261Sjkh 14076261Sjkh /* 14086261Sjkh * set modem change option register 1 14096261Sjkh * generate modem interrupts on which 1 -> 0 input transitions 14106261Sjkh * also controls auto-DTR output flow-control, which we don't use 14116261Sjkh */ 14126261Sjkh opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ 14136261Sjkh *(base + CD1400_MCOR1) = opt; 14146261Sjkh 14156261Sjkh /* 14166261Sjkh * set modem change option register 2 14176261Sjkh * generate modem interrupts on specific 0 -> 1 input transitions 14186261Sjkh */ 14196261Sjkh opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ 14206261Sjkh *(base + CD1400_MCOR2) = opt; 14216261Sjkh 14226261Sjkh splx(s); 14236261Sjkh 14246261Sjkh return 0; 14256261Sjkh} /* end of cyparam */ 14266261Sjkh 14276261Sjkh 14286261Sjkhvoid 14296261Sjkhcystart(struct tty *tp) 14306261Sjkh{ 14316261Sjkh u_char unit = UNIT(tp->t_dev); 14326261Sjkh struct cy *infop = info[unit]; 14336261Sjkh cy_addr base = infop->base_addr; 14346261Sjkh int s; 14356261Sjkh 14366261Sjkh#ifdef CyDebug 14376261Sjkh infop->start_count++; 14386261Sjkh#endif 14396261Sjkh 14406261Sjkh /* check the flow-control situation */ 14416261Sjkh if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) 14426261Sjkh return; 14436261Sjkh 14446261Sjkh if (tp->t_outq.c_cc <= tp->t_lowat) { 14456261Sjkh if (tp->t_state&TS_ASLEEP) { 14466261Sjkh tp->t_state &= ~TS_ASLEEP; 14476261Sjkh wakeup((caddr_t)&tp->t_outq); 14486261Sjkh } 14496261Sjkh selwakeup(&tp->t_wsel); 14506261Sjkh } 14516261Sjkh 14526261Sjkh#ifdef TxBuffer 14536261Sjkh service_upper_tx(unit); /* feed the monster */ 14546261Sjkh#endif 14556261Sjkh 14566261Sjkh s = spltty(); 14576261Sjkh 14586261Sjkh if (!(infop->intr_enable & (1 << 2))) { 14596261Sjkh /* select the channel */ 14606261Sjkh *(base + CD1400_CAR) = unit & (u_char)3; 14616261Sjkh 14626261Sjkh /* (re)enable interrupts to set things in motion */ 14636261Sjkh infop->intr_enable |= (1 << 2); 14646261Sjkh *(base + CD1400_SRER) = infop->intr_enable; 14656261Sjkh 14666261Sjkh infop->start_real++; 14676261Sjkh } 14686261Sjkh 14696261Sjkh splx(s); 14706261Sjkh} /* end of cystart() */ 14716261Sjkh 14726261Sjkh 14736261Sjkhint 14746261Sjkhcystop(struct tty *tp, int flag) 14756261Sjkh{ 14766261Sjkh u_char unit = UNIT(tp->t_dev); 14776261Sjkh struct cy *ip = info[unit]; 14786261Sjkh cy_addr base = ip->base_addr; 14796261Sjkh int s; 14806261Sjkh 14816261Sjkh s = spltty(); 14826261Sjkh 14836261Sjkh /* select the channel */ 14846261Sjkh *(base + CD1400_CAR) = unit & 3; 14856261Sjkh 14866261Sjkh /* halt output by disabling transmit interrupts */ 14876261Sjkh ip->intr_enable &=~ (1 << 2); 14886261Sjkh *(base + CD1400_SRER) = ip->intr_enable; 14896261Sjkh 14906261Sjkh splx(s); 14916261Sjkh 14926261Sjkh return 0; 14936261Sjkh} 14946261Sjkh 14956261Sjkh 14966261Sjkhint 14976261Sjkhcyselect(dev_t dev, int rw, struct proc *p) 14986261Sjkh{ 14996261Sjkh struct tty *tp = info[UNIT(dev)]->tty; 15006261Sjkh int s = spltty(); 15016261Sjkh int nread; 15026261Sjkh 15036261Sjkh switch (rw) { 15046261Sjkh 15056261Sjkh case FREAD: 15066261Sjkh nread = ttnread(tp); 15076261Sjkh if (nread > 0 || 15086261Sjkh ((tp->t_cflag&CLOCAL) == 0 && (tp->t_state&TS_CARR_ON) == 0)) 15096261Sjkh goto win; 15106261Sjkh selrecord(p, &tp->t_rsel); 15116261Sjkh break; 15126261Sjkh 15136261Sjkh case FWRITE: 15146261Sjkh if (tp->t_outq.c_cc <= tp->t_lowat) 15156261Sjkh goto win; 15166261Sjkh selrecord(p, &tp->t_wsel); 15176261Sjkh break; 15186261Sjkh } 15196261Sjkh splx(s); 15206261Sjkh return (0); 15216261Sjkh win: 15226261Sjkh splx(s); 15236261Sjkh return (1); 15246261Sjkh} /* end of cyselect() */ 15256261Sjkh 15266261Sjkh 15276261Sjkhint 15286261Sjkhcyspeed(int speed, int *prescaler_io) 15296261Sjkh{ 15306261Sjkh int actual; 15316261Sjkh int error; 15326261Sjkh int divider; 15336261Sjkh int prescaler; 15346261Sjkh int prescaler_unit; 15356261Sjkh 15366261Sjkh if (speed == 0) 15376261Sjkh return 0; 15386261Sjkh 15396261Sjkh if (speed < 0 || speed > 150000) 15406261Sjkh return -1; 15416261Sjkh 15426261Sjkh /* determine which prescaler to use */ 15436261Sjkh for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; 15446261Sjkh prescaler_unit--, prescaler >>= 2) { 15456261Sjkh if (CYCLOM_CLOCK/prescaler/speed > 63) 15466261Sjkh break; 15476261Sjkh } 15486261Sjkh 15496261Sjkh divider = (CYCLOM_CLOCK/prescaler*2/speed + 1)/2; /* round off */ 15506261Sjkh if (divider > 255) 15516261Sjkh divider = 255; 15526261Sjkh actual = CYCLOM_CLOCK/prescaler/divider; 15536261Sjkh error = ((actual-speed)*2000/speed +1)/2; /* percentage */ 15546261Sjkh 15556261Sjkh /* 3.0% max error tolerance */ 15566261Sjkh if (error < -30 || error > 30) 15576261Sjkh return -1; 15586261Sjkh 15596261Sjkh#if 0 15606261Sjkh printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); 15616261Sjkh printf("divider = %d (%x)\n", divider, divider); 15626261Sjkh printf("actual = %d\n", actual); 15636261Sjkh printf("error = %d\n", error); 15646261Sjkh#endif 15656261Sjkh 15666261Sjkh *prescaler_io = prescaler_unit; 15676261Sjkh return divider; 15686261Sjkh} /* end of cyspeed() */ 15696261Sjkh 15706261Sjkh 15716261Sjkhstatic void 15726261Sjkhcd1400_channel_cmd(cy_addr base, u_char cmd) 15736261Sjkh{ 15746261Sjkh /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, 15756261Sjkh as the card is probed every round? Replaced delaycount with 8k. 15766261Sjkh Either delaycount has to be implemented in FreeBSD or more sensible 15776261Sjkh way of doing these should be implemented. DELAY isn't enough here. 15786261Sjkh */ 15796261Sjkh unsigned maxwait = 5 * 8 * 1024; /* approx. 5 ms */ 15806261Sjkh 15816261Sjkh /* wait for processing of previous command to complete */ 15826261Sjkh while (*(base + CD1400_CCR) && maxwait--) 15836261Sjkh ; 15846261Sjkh 15856261Sjkh if (!maxwait) 15866261Sjkh log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n", 15876261Sjkh 5 * 8 * 1024); 15886261Sjkh 15896261Sjkh *(base + CD1400_CCR) = cmd; 15906261Sjkh} /* end of cd1400_channel_cmd() */ 15916261Sjkh 15926261Sjkh 15936261Sjkh#ifdef CyDebug 15946261Sjkh/* useful in ddb */ 15956261Sjkhvoid 15966261Sjkhcyclear(void) 15976261Sjkh{ 15986261Sjkh /* clear the timeout request */ 15996261Sjkh disable_intr(); 16006261Sjkh timeout_scheduled = 0; 16016261Sjkh enable_intr(); 16026261Sjkh} 16036261Sjkh 16046261Sjkhvoid 16056261Sjkhcyclearintr(void) 16066261Sjkh{ 16076261Sjkh /* clear interrupts */ 16086261Sjkh *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; 16096261Sjkh} 16106261Sjkh 16116261Sjkhint 16126261Sjkhcyparam_dummy(struct tty *tp, struct termios *t) 16136261Sjkh{ 16146261Sjkh return 0; 16156261Sjkh} 16166261Sjkh 16176261Sjkhvoid 16186261Sjkhcyset(int unit, int active) 16196261Sjkh{ 16206261Sjkh if (unit < 0 || unit > PORTS_PER_CYCLOM) { 16216261Sjkh printf("bad unit number %d\n", unit); 16226261Sjkh return; 16236261Sjkh } 16246261Sjkh cy_tty[unit]->t_param = active ? cyparam : cyparam_dummy; 16256261Sjkh} 16266261Sjkh 16276261Sjkh 16286261Sjkh/* useful in ddb */ 16296261Sjkhvoid 16306261Sjkhcystatus(int unit) 16316261Sjkh{ 16326261Sjkh struct cy *infop = info[unit]; 16336261Sjkh struct tty *tp = infop->tty; 16346261Sjkh cy_addr base = infop->base_addr; 16356261Sjkh 16366261Sjkh printf("info for channel %d\n", unit); 16376261Sjkh printf("------------------\n"); 16386261Sjkh 16396261Sjkh printf("cd1400 base address:\t0x%x\n", (int)infop->base_addr); 16406261Sjkh 16416261Sjkh /* select the port */ 16426261Sjkh *(base + CD1400_CAR) = (u_char)unit; 16436261Sjkh 16446261Sjkh printf("saved channel_control:\t%02x\n", infop->channel_control); 16456261Sjkh printf("saved cor1:\t\t%02x\n", infop->cor[0]); 16466261Sjkh printf("service request enable reg:\t%02x (%02x cached)\n", 16476261Sjkh (u_char)*(base + CD1400_SRER), infop->intr_enable); 16486261Sjkh printf("service request register:\t%02x\n", 16496261Sjkh (u_char)*(base + CD1400_SVRR)); 16506261Sjkh printf("\n"); 16516261Sjkh printf("modem status:\t\t\t%02x (%02x cached)\n", 16526261Sjkh (u_char)*(base + CD1400_MSVR), infop->modem_sig); 16536261Sjkh printf("rx/tx/mdm interrupt registers:\t%02x %02x %02x\n", 16546261Sjkh (u_char)*(base + CD1400_RIR), (u_char)*(base + CD1400_TIR), 16556261Sjkh (u_char)*(base + CD1400_MIR)); 16566261Sjkh printf("\n"); 16576261Sjkh if (tp) { 16586261Sjkh printf("tty state:\t\t\t%04x\n", tp->t_state); 16596261Sjkh printf("upper layer queue lengths:\t%d raw, %d canon, %d output\n", 16606261Sjkh tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); 16616261Sjkh } 16626261Sjkh else 16636261Sjkh printf("tty state:\t\t\tclosed\n"); 16646261Sjkh printf("\n"); 16656261Sjkh 16666261Sjkh printf("calls to cystart():\t\t%d (%d useful)\n", 16676261Sjkh infop->start_count, infop->start_real); 16686261Sjkh printf("\n"); 16696261Sjkh printf("total cyclom service probes:\t%d\n", cy_svrr_probes); 16706261Sjkh printf("calls to upper layer:\t\t%d\n", cy_timeouts); 16716261Sjkh printf("rx buffer chars free:\t\t%d\n", infop->rx_buf->free); 16726261Sjkh#ifdef TxBuffer 16736261Sjkh printf("tx buffer chars used:\t\t%d\n", infop->tx_buf.used); 16746261Sjkh#endif 16756261Sjkh printf("received chars:\t\t\t%d good, %d exception\n", 16766261Sjkh infop->recv_normal, infop->recv_exception); 16776261Sjkh printf("transmitted chars:\t\t%d\n", infop->xmit); 16786261Sjkh printf("modem signal deltas:\t\t%d\n", infop->mdm); 16796261Sjkh printf("\n"); 16806261Sjkh} /* end of cystatus() */ 16816261Sjkh#endif 16826261Sjkh#endif /* NCY > 0 */ 1683