Index: head/sys/dev/cy/cy.c =================================================================== --- head/sys/dev/cy/cy.c (revision 6781) +++ head/sys/dev/cy/cy.c (revision 6782) @@ -1,1683 +1,1673 @@ /* * cyclades cyclom-y serial driver * Andrew Herbert , 17 August 1993 * * Copyright (c) 1993 Andrew Herbert. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name Andrew Herbert may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: cy.c,v 1.2 1995/02/15 18:41:41 bde Exp $ + * $Id: cy.c,v 1.3 1995/02/25 20:09:12 pst Exp $ */ /* * Device minor number encoding: * * c c x x u u u u - bits in the minor device number * * bits meaning * ---- ------- * uuuu physical serial line (i.e. unit) to use * 0-7 on a cyclom-8Y, 0-15 on a cyclom-16Y * xx unused * cc carrier control mode * 00 complete hardware carrier control of the tty. * DCD must be high for the open(2) to complete. * 01 dialin pseudo-device (not yet implemented) * 10 carrier ignored until a high->low transition * 11 carrier completed ignored */ /* * Known deficiencies: * * * no BREAK handling - breaks are ignored, and can't be sent either * * no support for bad-char reporting, except via PARMRK * * no support for dialin + dialout devices */ #include "cy.h" #if NCY > 0 /* This disgusing hack because we actually have 16 units on one controller */ #if NCY < 2 #undef NCY #define NCY (16) #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NetBSD #include #endif #include #include #include #define RxFifoThreshold 3 /* 3 characters (out of 12) in the receive * FIFO before an interrupt is generated */ #define FastRawInput /* bypass the regular char-by-char canonical input * processing whenever possible */ #define PollMode /* use polling-based irq service routine, not the * hardware svcack lines. Must be defined for * cyclom-16y boards. * * XXX cyclom-8y doesn't work without this defined * either (!) */ #define LogOverruns /* log receive fifo overruns */ #undef TxBuffer /* buffer driver output, to be slightly more * efficient * * XXX presently buggy */ #undef Smarts /* enable slightly more CD1400 intelligence. Mainly * the output CR/LF processing, plus we can avoid a * few checks usually done in ttyinput(). * * XXX not yet implemented, and not particularly * worthwhile either. */ #define CyDebug /* include debugging code (minimal effect on * performance) */ #define CY_RX_BUFS 2 /* two receive buffers per port */ #define CY_RX_BUF_SIZE 256 /* bytes per receive buffer */ #define CY_TX_BUF_SIZE 512 /* bytes per transmit buffer */ /* #define CD1400s_PER_CYCLOM 1 */ /* cyclom-4y */ /* #define CD1400s_PER_CYCLOM 2 */ /* cyclom-8y */ #define CD1400s_PER_CYCLOM 4 /* cyclom-16y */ /* FreeBSD's getty doesn't know option for setting RTS/CTS handshake. Its getty, like a lot of other old cruft, should be replaced with something which used POSIX tty interfaces which at least allow enabling it. In the meantime, use the force. */ #define ALWAYS_RTS_CTS 1 #if CD1400s_PER_CYCLOM < 4 #define CD1400_MEMSIZE 0x400 /* 4*256 bytes per chip: cyclom-[48]y */ #else #define CD1400_MEMSIZE 0x100 /* 256 bytes per chip: cyclom-16y */ /* XXX or is it 0x400 like the rest? */ #define CYCLOM_16 1 /* This is a cyclom-16Y */ #endif #define PORTS_PER_CYCLOM (CD1400_NO_OF_CHANNELS * CD1400s_PER_CYCLOM) #define CYCLOM_RESET_16 0x1400 /* cyclom-16y reset */ #define CYCLOM_CLEAR_INTR 0x1800 /* intr ack address */ #define CYCLOM_CLOCK 25000000 /* baud rate clock */ #define CY_UNITMASK 0x0f #define CY_CARRIERMASK 0xC0 #define CY_CARRIERSHIFT 6 #define UNIT(x) (minor(x) & CY_UNITMASK) #define CARRIER_MODE(x) ((minor(x) & CY_CARRIERMASK) >> CY_CARRIERSHIFT) typedef u_char * volatile cy_addr; int cyprobe(struct isa_device *dev); int cyattach(struct isa_device *isdp); void cystart(struct tty *tp); int cyparam(struct tty *tp, struct termios *t); int cyspeed(int speed, int *prescaler_io); static void cy_channel_init(dev_t dev, int reset); static void cd1400_channel_cmd(cy_addr base, u_char cmd); /* hsu@clinet.fi: sigh */ #ifdef __NetBSD__ #define DELAY(foo) delay(foo) void delay(int delay); #endif /* Better get rid of this until the core people agree on kernel interfaces. At least it will then compile on both WhichBSDs. */ #if 0 extern unsigned int delaycount; /* calibrated 1 ms cpu-spin delay */ #endif struct isa_driver cydriver = { cyprobe, cyattach, "cy" }; /* low-level ping-pong buffer structure */ struct cy_buf { u_char *next_char; /* location of next char to write */ u_int free; /* free chars remaining in buffer */ struct cy_buf *next_buf; /* circular, you know */ u_char buf[CY_RX_BUF_SIZE]; /* start of the buffer */ }; /* low-level ring buffer */ #ifdef TxBuffer struct cy_ring { u_char buf[CY_TX_BUF_SIZE]; u_char *head; u_char *tail; /* next pos. to insert char */ u_char *endish; /* physical end of buf */ u_int used; /* no. of chars in queue */ }; #endif /* * define a structure to keep track of each serial line */ struct cy { cy_addr base_addr; /* base address of this port's cd1400 */ struct tty *tty; u_int dtrwait; /* time (in ticks) to hold dtr low after close */ u_int recv_exception; /* exception chars received */ u_int recv_normal; /* normal chars received */ u_int xmit; /* chars transmitted */ u_int mdm; /* modem signal changes */ #ifdef CyDebug u_int start_count; /* no. of calls to cystart() */ u_int start_real; /* no. of calls that did something */ #endif u_char carrier_mode; /* hardware carrier handling mode */ /* * 0 = always use * 1 = always use (dialin port) * 2 = ignore during open, then use it * 3 = ignore completely */ u_char carrier_delta; /* true if carrier has changed state */ u_char fifo_overrun; /* true if cd1400 receive fifo has... */ u_char rx_buf_overrun; /* true if low-level buf overflow */ u_char intr_enable; /* CD1400 SRER shadow */ u_char modem_sig; /* CD1400 modem signal shadow */ u_char channel_control;/* CD1400 CCR control command shadow */ u_char cor[3]; /* CD1400 COR1-3 shadows */ #ifdef Smarts u_char spec_char[4]; /* CD1400 SCHR1-4 shadows */ #endif struct cy_buf *rx_buf; /* current receive buffer */ struct cy_buf rx_buf_pool[CY_RX_BUFS];/* receive ping-pong buffers */ #ifdef TxBuffer struct cy_ring tx_buf; /* transmit buffer */ #endif }; int cydefaultrate = TTYDEF_SPEED; cy_addr cyclom_base; /* base address of the card */ static struct cy *info[NCY*PORTS_PER_CYCLOM]; #ifdef __FreeBSD__ /* XXX actually only temporarily for 2.1-Development */ struct tty cy_tty[NCY*PORTS_PER_CYCLOM]; #else struct tty *cy_tty[NCY*PORTS_PER_CYCLOM]; #endif static volatile u_char timeout_scheduled = 0; /* true if a timeout has been scheduled */ #ifdef CyDebug u_int cy_svrr_probes = 0; /* debugging */ u_int cy_timeouts = 0; u_int cy_timeout_req = 0; #endif /**********************************************************************/ int cyprobe(struct isa_device *dev) { int i, j; u_char version = 0; /* firmware version */ /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ i = *(cy_addr)(dev->id_maddr + CYCLOM_RESET_16); DELAY(500); /* wait for the board to get its act together (500 us) */ for (i = 0; i < CD1400s_PER_CYCLOM; i++) { cy_addr base = dev->id_maddr + i * CD1400_MEMSIZE; /* wait for chip to become ready for new command */ for (j = 0; j < 100; j += 50) { DELAY(50); /* wait 50 us */ if (!*(base + CD1400_CCR)) break; } /* clear the GFRCR register */ *(base + CD1400_GFRCR) = 0; /* issue a reset command */ *(base + CD1400_CCR) = CD1400_CMD_RESET; /* wait for the CD1400 to initialise itself */ for (j = 0; j < 1000; j += 50) { DELAY(50); /* wait 50 us */ /* retrieve firmware version */ version = *(base + CD1400_GFRCR); if (version) break; } /* anything in the 40-4f range is fine */ if ((version & 0xf0) != 0x40) { return 0; } } return 1; /* found */ } int cyattach(struct isa_device *isdp) { /* u_char unit = UNIT(isdp->id_unit); */ int i, j, k; /* global variable used various routines */ cyclom_base = (cy_addr)isdp->id_maddr; for (i = 0, k = 0; i < CD1400s_PER_CYCLOM; i++) { cy_addr base = cyclom_base + i * CD1400_MEMSIZE; /* setup a 1ms clock tick */ *(base + CD1400_PPR) = CD1400_CLOCK_25_1MS; for (j = 0; j < CD1400_NO_OF_CHANNELS; j++, k++) { struct cy *ip; /* * grab some space. it'd be more polite to do this in cyopen(), * but hey. */ info[k] = ip = malloc(sizeof(struct cy), M_DEVBUF, M_WAITOK); /* clear all sorts of junk */ bzero(ip, sizeof(struct cy)); ip->base_addr = base; /* initialise the channel, without resetting it first */ cy_channel_init(k, 0); } } /* clear interrupts */ *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; return 1; } int cyopen(dev_t dev, int flag, int mode, struct proc *p) { u_int unit = UNIT(dev); struct cy *infop; cy_addr base; struct tty *tp; int error = 0; u_char carrier; if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) return (ENXIO); infop = info[unit]; base = infop->base_addr; #ifdef __FreeBSD__ infop->tty = &cy_tty[unit]; #else if (!cy_tty[unit]) infop->tty = cy_tty[unit] = ttymalloc(); #endif tp = infop->tty; tp->t_oproc = cystart; tp->t_param = cyparam; tp->t_dev = dev; if (!(tp->t_state & TS_ISOPEN)) { tp->t_state |= TS_WOPEN; ttychars(tp); if (tp->t_ispeed == 0) { tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = cydefaultrate; } (void) spltty(); cy_channel_init(unit, 1); /* reset the hardware */ /* * raise dtr and generally set things up correctly. this * has the side-effect of selecting the appropriate cd1400 * channel, to help us with subsequent channel control stuff */ cyparam(tp, &tp->t_termios); /* check carrier, and set t_state's TS_CARR_ON flag accordingly */ infop->modem_sig = *(base + CD1400_MSVR); carrier = infop->modem_sig & CD1400_MSVR_CD; if (carrier || (infop->carrier_mode >= 2)) tp->t_state |= TS_CARR_ON; else tp->t_state &=~ TS_CARR_ON; /* * enable modem & rx interrupts - relies on cyparam() * having selected the appropriate cd1400 channel */ infop->intr_enable = (1 << 7) | (1 << 4); *(base + CD1400_SRER) = infop->intr_enable; ttsetwater(tp); } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) return (EBUSY); if (!(flag & O_NONBLOCK)) while (!(tp->t_cflag & CLOCAL) && !(tp->t_state & TS_CARR_ON) && !error) error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI|PCATCH, ttopen, 0); (void) spl0(); if (!error) error = (*linesw[(u_char)tp->t_line].l_open)(dev, tp); return (error); } /* end of cyopen() */ void cyclose_wakeup(void *arg) { wakeup(arg); } /* end of cyclose_wakeup() */ int cyclose(dev_t dev, int flag, int mode, struct proc *p) { u_int unit = UNIT(dev); struct cy *infop = info[unit]; struct tty *tp = infop->tty; cy_addr base = infop->base_addr; int s; (*linesw[(u_char)tp->t_line].l_close)(tp, flag); s = spltty(); /* select the appropriate channel on the CD1400 */ *(base + CD1400_CAR) = (u_char)(unit & 0x03); /* disable this channel and lower DTR */ infop->intr_enable = 0; *(base + CD1400_SRER) = (u_char)0; /* no intrs */ *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; /* no DTR */ infop->modem_sig &= ~CD1400_MSVR_DTR; /* disable receiver (leave transmitter enabled) */ infop->channel_control = (1 << 4) | (1 << 3) | 1; cd1400_channel_cmd(base, infop->channel_control); splx(s); ttyclose(tp); #ifdef broken /* session holds a ref to the tty; can't deallocate */ ttyfree(tp); infop->tty = cy_tty[unit] = (struct tty *)NULL; #endif if (infop->dtrwait) { int error; timeout(cyclose_wakeup, (caddr_t)&infop->dtrwait, infop->dtrwait); do { error = tsleep((caddr_t)&infop->dtrwait, TTIPRI|PCATCH, "cyclose", 0); } while (error == ERESTART); } return 0; } /* end of cyclose() */ int cyread(dev_t dev, struct uio *uio, int flag) { u_int unit = UNIT(dev); struct tty *tp = info[unit]->tty; return (*linesw[(u_char)tp->t_line].l_read)(tp, uio, flag); } /* end of cyread() */ int cywrite(dev_t dev, struct uio *uio, int flag) { u_int unit = UNIT(dev); struct tty *tp = info[unit]->tty; #ifdef Smarts /* XXX duplicate ttwrite(), but without so much output processing on * CR & LF chars. Hardly worth the effort, given that high-throughput * sessions are raw anyhow. */ #else return (*linesw[(u_char)tp->t_line].l_write)(tp, uio, flag); #endif } /* end of cywrite() */ #ifdef Smarts /* standard line discipline input routine */ int cyinput(int c, struct tty *tp) { /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK * bits, as they are done by the CD1400. Hardly worth the effort, * given that high-throughput sessions are raw anyhow. */ } /* end of cyinput() */ #endif /* Smarts */ inline static void service_upper_rx(int unit) { struct cy *ip = info[unit]; struct tty *tp = ip->tty; struct cy_buf *buf; int i; u_char *ch; buf = ip->rx_buf; /* give service_rx() a new one */ disable_intr(); /* faster than spltty() */ ip->rx_buf = buf->next_buf; enable_intr(); if (tp->t_state & TS_ISOPEN) { ch = buf->buf; i = buf->next_char - buf->buf; #ifdef FastRawInput /* try to avoid calling the line discipline stuff if we can */ if ((tp->t_line == 0) && !(tp->t_iflag & (ICRNL | IMAXBEL | INLCR)) && !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG | PENDIN)) && !(tp->t_state & (TS_CNTTB | TS_LNCH))) { i = b_to_q(ch, i, &tp->t_rawq); if (i) { /* * we have no RTS flow control support on cy-8 * boards, so this is really just tough luck */ log(LOG_WARNING, "cy%d: tty input queue overflow\n", unit); } ttwakeup(tp); /* notify any readers */ } else #endif /* FastRawInput */ { while (i--) (*linesw[(u_char)tp->t_line].l_rint)((int)*ch++, tp); } } /* clear the buffer we've just processed */ buf->next_char = buf->buf; buf->free = CY_RX_BUF_SIZE; } /* end of service_upper_rx() */ #ifdef TxBuffer static void service_upper_tx(int unit) { struct cy *ip = info[unit]; struct tty *tp = ip->tty; tp->t_state &=~ (TS_BUSY|TS_FLUSH); if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } selwakeup(&tp->t_wsel); } if (tp->t_outq.c_cc > 0) { struct cy_ring *txq = &ip->tx_buf; int free_count = CY_TX_BUF_SIZE - ip->tx_buf.used; u_char *cp = txq->tail; int count; int chars_done; tp->t_state |= TS_BUSY; /* find the largest contig. copy we can do */ count = ((txq->endish - cp) > free_count) ? free_count : txq->endish - cp; count = ((cp + free_count) > txq->endish) ? txq->endish - cp : free_count; /* copy the first slab */ chars_done = q_to_b(&tp->t_outq, cp, count); /* check for wrap-around time */ cp += chars_done; if (cp == txq->endish) cp = txq->buf; /* back to the start */ /* copy anything else, after we've wrapped around */ if ((chars_done == count) && (count != free_count)) { /* copy the second slab */ count = q_to_b(&tp->t_outq, cp, free_count - count); cp += count; chars_done += count; } /* * update queue, protecting ourselves from any rampant * lower-layers */ disable_intr(); txq->tail = cp; txq->used += chars_done; enable_intr(); } if (!tp->t_outq.c_cc) tp->t_state &=~ TS_BUSY; } /* end of service_upper_tx() */ #endif /* TxBuffer */ inline static void service_upper_mdm(int unit) { struct cy *ip = info[unit]; if (ip->carrier_delta) { int carrier = ip->modem_sig & CD1400_MSVR_CD; struct tty *tp = ip->tty; if (!(*linesw[(u_char)tp->t_line].l_modem)(tp, carrier)) { cy_addr base = ip->base_addr; /* clear DTR */ disable_intr(); *(base + CD1400_CAR) = (u_char)(unit & 0x03); *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; ip->modem_sig &= ~CD1400_MSVR_DTR; ip->carrier_delta = 0; enable_intr(); } else { disable_intr(); ip->carrier_delta = 0; enable_intr(); } } } /* end of service_upper_mdm() */ /* upper level character processing routine */ void cytimeout(void *ptr) { int unit; timeout_scheduled = 0; #ifdef CyDebug cy_timeouts++; #endif /* check each port in turn */ for (unit = 0; unit < NCY*PORTS_PER_CYCLOM; unit++) { struct cy *ip = info[unit]; #ifndef TxBuffer struct tty *tp = ip->tty; #endif /* ignore anything that is not open */ if (!ip->tty) continue; /* * any received chars to handle? (doesn't matter if intr routine * kicks in while we're testing this) */ if (ip->rx_buf->free != CY_RX_BUF_SIZE) service_upper_rx(unit); #ifdef TxBuffer /* anything to add to the transmit buffer (low-water mark)? */ if (ip->tx_buf.used < CY_TX_BUF_SIZE/2) service_upper_tx(unit); #else if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } selwakeup(&tp->t_wsel); } #endif /* anything modem signals altered? */ service_upper_mdm(unit); /* any overruns to log? */ #ifdef LogOverruns if (ip->fifo_overrun) { /* * turn off the alarm - not important enough to bother * with interrupt protection. */ ip->fifo_overrun = 0; log(LOG_WARNING, "cy%d: receive fifo overrun\n", unit); } #endif if (ip->rx_buf_overrun) { /* * turn off the alarm - not important enough to bother * with interrupt protection. */ ip->rx_buf_overrun = 0; log(LOG_WARNING, "cy%d: receive buffer full\n", unit); } } } /* cytimeout() */ inline static void schedule_upper_service(void) { #ifdef CyDebug cy_timeout_req++; #endif if (!timeout_scheduled) { timeout(cytimeout, (caddr_t)0, 1); /* call next tick */ timeout_scheduled = 1; } } /* end of schedule_upper_service() */ /* initialise a channel on the cyclom board */ static void cy_channel_init(dev_t dev, int reset) { u_int unit = UNIT(dev); int carrier_mode = CARRIER_MODE(dev); struct cy *ip = info[unit]; cy_addr base = ip->base_addr; struct tty *tp = ip->tty; struct cy_buf *buf, *next_buf; int i; #ifndef PollMode u_char cd1400_unit; #endif /* clear the structure and refill it */ bzero(ip, sizeof(struct cy)); ip->base_addr = base; ip->tty = tp; ip->carrier_mode = carrier_mode; /* select channel of the CD1400 */ *(base + CD1400_CAR) = (u_char)(unit & 0x03); if (reset) cd1400_channel_cmd(base, 0x80); /* reset the channel */ /* set LIVR to 0 - intr routines depend on this */ *(base + CD1400_LIVR) = 0; #ifndef PollMode /* set top four bits of {R,T,M}ICR to the cd1400 * number, cd1400_unit */ cd1400_unit = unit / CD1400_NO_OF_CHANNELS; *(base + CD1400_RICR) = (u_char)(cd1400_unit << 4); *(base + CD1400_TICR) = (u_char)(cd1400_unit << 4); *(base + CD1400_MICR) = (u_char)(cd1400_unit << 4); #endif ip->dtrwait = hz/4; /* quarter of a second */ /* setup low-level buffers */ i = CY_RX_BUFS; ip->rx_buf = next_buf = &ip->rx_buf_pool[0]; while (i--) { buf = &ip->rx_buf_pool[i]; buf->next_char = buf->buf; /* first char to use */ buf->free = CY_RX_BUF_SIZE; /* i.e. empty */ buf->next_buf = next_buf; /* where to go next */ next_buf = buf; } #ifdef TxBuffer ip->tx_buf.endish = ip->tx_buf.buf + CY_TX_BUF_SIZE; /* clear the low-level tx buffer */ ip->tx_buf.head = ip->tx_buf.tail = ip->tx_buf.buf; ip->tx_buf.used = 0; #endif /* clear the low-level rx buffer */ ip->rx_buf->next_char = ip->rx_buf->buf; /* first char to use */ ip->rx_buf->free = CY_RX_BUF_SIZE; /* completely empty */ } /* end of cy_channel_init() */ /* service a receive interrupt */ inline static void service_rx(int cd, caddr_t base) { struct cy *infop; unsigned count; int ch; u_char serv_type, channel; #ifdef PollMode u_char save_rir, save_car; #endif /* setup */ #ifdef PollMode save_rir = *(base + CD1400_RIR); channel = cd * CD1400_NO_OF_CHANNELS + (save_rir & 0x3); save_car = *(base + CD1400_CAR); *(base + CD1400_CAR) = save_rir; /* enter modem service */ serv_type = *(base + CD1400_RIVR); #else serv_type = *(base + CD1400_SVCACKR); /* ack receive service */ channel = ((u_char)*(base + CD1400_RICR)) >> 2; /* get cyclom channel # */ #ifdef CyDebug if (channel >= PORTS_PER_CYCLOM) { printf("cy: service_rx - channel %02x\n", channel); panic("cy: service_rx - bad channel"); } #endif #endif infop = info[channel]; /* read those chars */ if (serv_type & CD1400_RIVR_EXCEPTION) { /* read the exception status */ u_char status = *(base + CD1400_RDSR); /* XXX is it a break? Do something if it is! */ /* XXX is IGNPAR not set? Store a null in the buffer. */ #ifdef LogOverruns if (status & CD1400_RDSR_OVERRUN) { #if 0 ch |= TTY_PE; /* for SLIP */ #endif infop->fifo_overrun++; } #endif infop->recv_exception++; } else { struct cy_buf *buf = infop->rx_buf; count = (u_char)*(base + CD1400_RDCR); /* how many to read? */ infop->recv_normal += count; if (buf->free < count) { infop->rx_buf_overrun += count; /* read & discard everything */ while (count--) ch = (u_char)*(base + CD1400_RDSR); } else { /* slurp it into our low-level buffer */ buf->free -= count; while (count--) { ch = (u_char)*(base + CD1400_RDSR); /* read the char */ *(buf->next_char++) = ch; } } } #ifdef PollMode *(base + CD1400_RIR) = (u_char)(save_rir & 0x3f); /* terminate service context */ #else *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ #endif } /* end of service_rx */ /* service a transmit interrupt */ inline static void service_tx(int cd, caddr_t base) { struct cy *ip; #ifdef TxBuffer struct cy_ring *txq; #else struct tty *tp; #endif u_char channel; #ifdef PollMode u_char save_tir, save_car; #else u_char vector; #endif /* setup */ #ifdef PollMode save_tir = *(base + CD1400_TIR); channel = cd * CD1400_NO_OF_CHANNELS + (save_tir & 0x3); save_car = *(base + CD1400_CAR); *(base + CD1400_CAR) = save_tir; /* enter tx service */ #else vector = *(base + CD1400_SVCACKT); /* ack transmit service */ channel = ((u_char)*(base + CD1400_TICR)) >> 2; /* get cyclom channel # */ #ifdef CyDebug if (channel >= PORTS_PER_CYCLOM) { printf("cy: service_tx - channel %02x\n", channel); panic("cy: service_tx - bad channel"); } #endif #endif ip = info[channel]; #ifdef TxBuffer txq = &ip->tx_buf; if (txq->used > 0) { cy_addr base = ip->base_addr; int count = min(CD1400_FIFOSIZE, txq->used); int chars_done = count; u_char *cp = txq->head; u_char *buf_end = txq->endish; /* ip->state |= CY_BUSY; */ while (count--) { *(base + CD1400_TDR) = *cp++; if (cp >= buf_end) cp = txq->buf; }; txq->head = cp; txq->used -= chars_done; /* important that this is atomic */ ip->xmit += chars_done; } /* * disable tx intrs if no more chars to send. we re-enable * them in cystart() */ if (!txq->used) { ip->intr_enable &=~ (1 << 2); *(base + CD1400_SRER) = ip->intr_enable; /* ip->state &= ~CY_BUSY; */ } #else tp = ip->tty; if (!(tp->t_state & TS_TTSTOP) && (tp->t_outq.c_cc > 0)) { cy_addr base = ip->base_addr; int count = min(CD1400_FIFOSIZE, tp->t_outq.c_cc); ip->xmit += count; tp->t_state |= TS_BUSY; while (count--) *(base + CD1400_TDR) = getc(&tp->t_outq); } /* * disable tx intrs if no more chars to send. we re-enable them * in cystart() */ if (!tp->t_outq.c_cc) { ip->intr_enable &=~ (1 << 2); *(base + CD1400_SRER) = ip->intr_enable; tp->t_state &= ~TS_BUSY; } #endif #ifdef PollMode *(base + CD1400_TIR) = (u_char)(save_tir & 0x3f); /* terminate service context */ #else *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ #endif } /* end of service_tx */ /* service a modem status interrupt */ inline static void service_mdm(int cd, caddr_t base) { struct cy *infop; u_char channel, deltas; #ifdef PollMode u_char save_mir, save_car; #else u_char vector; #endif /* setup */ #ifdef PollMode save_mir = *(base + CD1400_MIR); channel = cd * CD1400_NO_OF_CHANNELS + (save_mir & 0x3); save_car = *(base + CD1400_CAR); *(base + CD1400_CAR) = save_mir; /* enter modem service */ #else vector = *(base + CD1400_SVCACKM); /* ack modem service */ channel = ((u_char)*(base + CD1400_MICR)) >> 2; /* get cyclom channel # */ #ifdef CyDebug if (channel >= PORTS_PER_CYCLOM) { printf("cy: service_mdm - channel %02x\n", channel); panic("cy: service_mdm - bad channel"); } #endif #endif infop = info[channel]; /* read the siggies and see what's changed */ infop->modem_sig = (u_char)*(base + CD1400_MSVR); deltas = (u_char)*(base + CD1400_MISR); if ((infop->carrier_mode <= 2) && (deltas & CD1400_MISR_CDd)) /* something for the upper layer to deal with */ infop->carrier_delta = 1; infop->mdm++; /* terminate service context */ #ifdef PollMode *(base + CD1400_MIR) = (u_char)(save_mir & 0x3f); #else *(base + CD1400_EOSRR) = (u_char)0; #endif } /* end of service_mdm */ int cyintr(int unit) { int cd; u_char status; /* check each CD1400 in turn */ for (cd = 0; cd < CD1400s_PER_CYCLOM; cd++) { cy_addr base = cyclom_base + cd*CD1400_MEMSIZE; /* poll to see if it has any work */ while (status = (u_char)*(base + CD1400_SVRR)) { #ifdef CyDebug cy_svrr_probes++; #endif /* service requests as appropriate, giving priority to RX */ if (status & CD1400_SVRR_RX) service_rx(cd, base); if (status & CD1400_SVRR_TX) service_tx(cd, base); if (status & CD1400_SVRR_MDM) service_mdm(cd, base); } } /* request upper level service to deal with whatever happened */ schedule_upper_service(); /* re-enable interrupts on the cyclom */ *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; return 1; } int cyioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { int unit = UNIT(dev); struct cy *infop = info[unit]; struct tty *tp = infop->tty; int error; error = (*linesw[(u_char)tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); error = ttioctl(tp, cmd, data, flag #ifdef NetBSD , p #endif ); if (error >= 0) return (error); switch (cmd) { #ifdef notyet /* sigh - more junk to do XXX */ case TIOCSBRK: break; case TIOCCBRK: break; case TIOCSDTR: break; case TIOCCDTR: break; case TIOCMSET: break; case TIOCMBIS: break; case TIOCMBIC: break; #endif /* notyet */ case TIOCMGET: { int bits = 0; u_char status = infop->modem_sig; if (status & CD1400_MSVR_DTR) bits |= TIOCM_DTR | TIOCM_RTS; if (status & CD1400_MSVR_CD) bits |= TIOCM_CD; if (status & CD1400_MSVR_CTS) bits |= TIOCM_CTS; if (status & CD1400_MSVR_DSR) bits |= TIOCM_DSR; #ifdef CYCLOM_16 if (status & CD1400_MSVR_RI) bits |= TIOCM_RI; #endif if (infop->channel_control & 0x02) bits |= TIOCM_LE; *(int *)data = bits; break; } #ifdef TIOCMSBIDIR case TIOCMSBIDIR: return (ENOTTY); #endif /* TIOCMSBIDIR */ #ifdef TIOCMGBIDIR case TIOCMGBIDIR: return (ENOTTY); #endif /* TIOCMGBIDIR */ #ifdef TIOCMSDTRWAIT case TIOCMSDTRWAIT: /* must be root to set dtr delay */ if (p->p_ucred->cr_uid != 0) return(EPERM); infop->dtrwait = *(u_int *)data; break; #endif /* TIOCMSDTRWAIT */ #ifdef TIOCMGDTRWAIT case TIOCMGDTRWAIT: *(u_int *)data = infop->dtrwait; break; #endif /* TIOCMGDTRWAIT */ default: return (ENOTTY); } return 0; } /* end of cyioctl() */ int cyparam(struct tty *tp, struct termios *t) { u_char unit = UNIT(tp->t_dev); struct cy *infop = info[unit]; cy_addr base = infop->base_addr; int cflag = t->c_cflag; int iflag = t->c_iflag; int ispeed, ospeed; int itimeout; int iprescaler, oprescaler; int s; u_char cor_change = 0; u_char opt; if (!t->c_ispeed) t->c_ispeed = t->c_ospeed; s = spltty(); /* select the appropriate channel on the CD1400 */ *(base + CD1400_CAR) = unit & 0x03; /* handle DTR drop on speed == 0 trick */ if (t->c_ospeed == 0) { *(base + CD1400_DTR) = CD1400_DTR_CLEAR; infop->modem_sig &= ~CD1400_MSVR_DTR; } else { *(base + CD1400_DTR) = CD1400_DTR_SET; infop->modem_sig |= CD1400_MSVR_DTR; } /* set baud rates if they've changed from last time */ if ((ospeed = cyspeed(t->c_ospeed, &oprescaler)) < 0) return EINVAL; *(base + CD1400_TBPR) = (u_char)ospeed; *(base + CD1400_TCOR) = (u_char)oprescaler; if ((ispeed = cyspeed(t->c_ispeed, &iprescaler)) < 0) return EINVAL; *(base + CD1400_RBPR) = (u_char)ispeed; *(base + CD1400_RCOR) = (u_char)iprescaler; /* * set receive time-out period * generate a rx interrupt if no new chars are received in * this many ticks * don't bother comparing old & new VMIN, VTIME and ispeed - it * can't be much worse just to calculate and set it each time! * certainly less hassle. :-) */ /* * calculate minimum timeout period: * 5 ms or the time it takes to receive 1 char, rounded up to the * next ms, whichever is greater */ if (t->c_ispeed > 0) { itimeout = (t->c_ispeed > 2200) ? 5 : (10000/t->c_ispeed + 1); /* if we're using VTIME as an inter-char timeout, and it is set to * be longer than the minimum calculated above, go for it */ if (t->c_cc[VMIN] && t->c_cc[VTIME] && t->c_cc[VTIME]*10 > itimeout) itimeout = t->c_cc[VTIME]*10; /* store it, taking care not to overflow the byte-sized register */ *(base + CD1400_RTPR) = (u_char)((itimeout <= 255) ? itimeout : 255); } /* * channel control * receiver enable * transmitter enable (always set) */ opt = (1 << 4) | (1 << 3) | ((cflag & CREAD) ? (1 << 1) : 1); if (opt != infop->channel_control) { infop->channel_control = opt; cd1400_channel_cmd(base, opt); } #ifdef Smarts /* set special chars */ if (t->c_cc[VSTOP] != _POSIX_VDISABLE && (t->c_cc[VSTOP] != infop->spec_char[0])) { *(base + CD1400_SCHR1) = infop->spec_char[0] = t->c_cc[VSTOP]; } if (t->c_cc[VSTART] != _POSIX_VDISABLE && (t->c_cc[VSTART] != infop->spec_char[1])) { *(base + CD1400_SCHR2) = infop->spec_char[0] = t->c_cc[VSTART]; } if (t->c_cc[VINTR] != _POSIX_VDISABLE && (t->c_cc[VINTR] != infop->spec_char[2])) { *(base + CD1400_SCHR3) = infop->spec_char[0] = t->c_cc[VINTR]; } if (t->c_cc[VSUSP] != _POSIX_VDISABLE && (t->c_cc[VSUSP] != infop->spec_char[3])) { *(base + CD1400_SCHR4) = infop->spec_char[0] = t->c_cc[VSUSP]; } #endif /* * set channel option register 1 - * parity mode * stop bits * char length */ opt = 0; /* parity */ if (cflag & PARENB) { if (cflag & PARODD) opt |= 1 << 7; opt |= 2 << 5; /* normal parity mode */ } if (!(iflag & INPCK)) opt |= 1 << 4; /* ignore parity */ /* stop bits */ if (cflag & CSTOPB) opt |= 2 << 2; /* char length */ opt |= (cflag & CSIZE) >> 8; /* nasty, but fast */ if (opt != infop->cor[0]) { cor_change |= 1 << 1; *(base + CD1400_COR1) = opt; } /* * set channel option register 2 - * flow control */ opt = 0; #ifdef Smarts if (iflag & IXANY) opt |= 1 << 7; /* auto output restart on any char after XOFF */ if (iflag & IXOFF) opt |= 1 << 6; /* auto XOFF output flow-control */ #endif #ifndef ALWAYS_RTS_CTS if (cflag & CCTS_OFLOW) #endif opt |= 1 << 1; /* auto CTS flow-control */ if (opt != infop->cor[1]) { cor_change |= 1 << 2; *(base + CD1400_COR2) = opt; } /* * set channel option register 3 - * receiver FIFO interrupt threshold * flow control */ opt = RxFifoThreshold; /* rx fifo threshold */ #ifdef Smarts if (t->c_lflag & ICANON) opt |= 1 << 6; /* detect INTR & SUSP chars */ if (iflag & IXOFF) opt |= (1 << 5) | (1 << 4); /* transparent in-band flow control */ #endif if (opt != infop->cor[2]) { cor_change |= 1 << 3; *(base + CD1400_COR3) = opt; } /* notify the CD1400 if COR1-3 have changed */ if (cor_change) { cor_change |= 1 << 6; /* COR change flag */ cd1400_channel_cmd(base, cor_change); } /* * set channel option register 4 - * CR/NL processing * break processing * received exception processing */ opt = 0; if (iflag & IGNCR) opt |= 1 << 7; #ifdef Smarts /* * we need a new ttyinput() for this, as we don't want to * have ICRNL && INLCR being done in both layers, or to have * synchronisation problems */ if (iflag & ICRNL) opt |= 1 << 6; if (iflag & INLCR) opt |= 1 << 5; #endif if (iflag & IGNBRK) opt |= 1 << 4; if (!(iflag & BRKINT)) opt |= 1 << 3; if (iflag & IGNPAR) #ifdef LogOverruns opt |= 0; /* broken chars cause receive exceptions */ #else opt |= 2; /* discard broken chars */ #endif else { if (iflag & PARMRK) opt |= 4; /* precede broken chars with 0xff 0x0 */ else #ifdef LogOverruns opt |= 0; /* broken chars cause receive exceptions */ #else opt |= 3; /* convert framing/parity errs to nulls */ #endif } *(base + CD1400_COR4) = opt; /* * set channel option register 5 - */ opt = 0; if (iflag & ISTRIP) opt |= 1 << 7; if (t->c_iflag & IEXTEN) { opt |= 1 << 6; /* enable LNEXT (e.g. ctrl-v quoting) handling */ } #ifdef Smarts if (t->c_oflag & ONLCR) opt |= 1 << 1; if (t->c_oflag & OCRNL) opt |= 1; #endif *(base + CD1400_COR5) = opt; /* * set modem change option register 1 * generate modem interrupts on which 1 -> 0 input transitions * also controls auto-DTR output flow-control, which we don't use */ opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ *(base + CD1400_MCOR1) = opt; /* * set modem change option register 2 * generate modem interrupts on specific 0 -> 1 input transitions */ opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ *(base + CD1400_MCOR2) = opt; splx(s); return 0; } /* end of cyparam */ void cystart(struct tty *tp) { u_char unit = UNIT(tp->t_dev); struct cy *infop = info[unit]; cy_addr base = infop->base_addr; int s; #ifdef CyDebug infop->start_count++; #endif /* check the flow-control situation */ if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) return; if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } selwakeup(&tp->t_wsel); } #ifdef TxBuffer service_upper_tx(unit); /* feed the monster */ #endif s = spltty(); if (!(infop->intr_enable & (1 << 2))) { /* select the channel */ *(base + CD1400_CAR) = unit & (u_char)3; /* (re)enable interrupts to set things in motion */ infop->intr_enable |= (1 << 2); *(base + CD1400_SRER) = infop->intr_enable; infop->start_real++; } splx(s); } /* end of cystart() */ int cystop(struct tty *tp, int flag) { u_char unit = UNIT(tp->t_dev); struct cy *ip = info[unit]; cy_addr base = ip->base_addr; int s; s = spltty(); /* select the channel */ *(base + CD1400_CAR) = unit & 3; /* halt output by disabling transmit interrupts */ ip->intr_enable &=~ (1 << 2); *(base + CD1400_SRER) = ip->intr_enable; splx(s); return 0; } struct tty * cydevtotty(dev_t dev) { u_char unit = UNIT(dev); if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) return NULL; return info[unit]->tty; -} - -int -cyselect(dev_t dev, int rw, struct proc *p) -{ - u_char unit = UNIT(dev); - if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) - return (ENXIO); - - return (ttyselect(info[unit]->tty, rw, p)); } int cyspeed(int speed, int *prescaler_io) { int actual; int error; int divider; int prescaler; int prescaler_unit; if (speed == 0) return 0; if (speed < 0 || speed > 150000) return -1; /* determine which prescaler to use */ for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; prescaler_unit--, prescaler >>= 2) { if (CYCLOM_CLOCK/prescaler/speed > 63) break; } divider = (CYCLOM_CLOCK/prescaler*2/speed + 1)/2; /* round off */ if (divider > 255) divider = 255; actual = CYCLOM_CLOCK/prescaler/divider; error = ((actual-speed)*2000/speed +1)/2; /* percentage */ /* 3.0% max error tolerance */ if (error < -30 || error > 30) return -1; #if 0 printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); printf("divider = %d (%x)\n", divider, divider); printf("actual = %d\n", actual); printf("error = %d\n", error); #endif *prescaler_io = prescaler_unit; return divider; } /* end of cyspeed() */ static void cd1400_channel_cmd(cy_addr base, u_char cmd) { /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, as the card is probed every round? Replaced delaycount with 8k. Either delaycount has to be implemented in FreeBSD or more sensible way of doing these should be implemented. DELAY isn't enough here. */ unsigned maxwait = 5 * 8 * 1024; /* approx. 5 ms */ /* wait for processing of previous command to complete */ while (*(base + CD1400_CCR) && maxwait--) ; if (!maxwait) log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n", 5 * 8 * 1024); *(base + CD1400_CCR) = cmd; } /* end of cd1400_channel_cmd() */ #ifdef CyDebug /* useful in ddb */ void cyclear(void) { /* clear the timeout request */ disable_intr(); timeout_scheduled = 0; enable_intr(); } void cyclearintr(void) { /* clear interrupts */ *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; } int cyparam_dummy(struct tty *tp, struct termios *t) { return 0; } void cyset(int unit, int active) { if (unit < 0 || unit >= /* NCY *? */ PORTS_PER_CYCLOM) { printf("bad unit number %d\n", unit); return; } #ifdef __FreeBSD__ cy_tty[unit].t_param = active ? cyparam : cyparam_dummy; #else cy_tty[unit]->t_param = active ? cyparam : cyparam_dummy; #endif } /* useful in ddb */ void cystatus(int unit) { struct cy *infop = info[unit]; struct tty *tp = infop->tty; cy_addr base = infop->base_addr; printf("info for channel %d\n", unit); printf("------------------\n"); printf("cd1400 base address:\t0x%x\n", (int)infop->base_addr); /* select the port */ *(base + CD1400_CAR) = (u_char)unit; printf("saved channel_control:\t%02x\n", infop->channel_control); printf("saved cor1:\t\t%02x\n", infop->cor[0]); printf("service request enable reg:\t%02x (%02x cached)\n", (u_char)*(base + CD1400_SRER), infop->intr_enable); printf("service request register:\t%02x\n", (u_char)*(base + CD1400_SVRR)); printf("\n"); printf("modem status:\t\t\t%02x (%02x cached)\n", (u_char)*(base + CD1400_MSVR), infop->modem_sig); printf("rx/tx/mdm interrupt registers:\t%02x %02x %02x\n", (u_char)*(base + CD1400_RIR), (u_char)*(base + CD1400_TIR), (u_char)*(base + CD1400_MIR)); printf("\n"); if (tp) { printf("tty state:\t\t\t%04x\n", tp->t_state); printf("upper layer queue lengths:\t%d raw, %d canon, %d output\n", tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); } else printf("tty state:\t\t\tclosed\n"); printf("\n"); printf("calls to cystart():\t\t%d (%d useful)\n", infop->start_count, infop->start_real); printf("\n"); printf("total cyclom service probes:\t%d\n", cy_svrr_probes); printf("calls to upper layer:\t\t%d\n", cy_timeouts); printf("rx buffer chars free:\t\t%d\n", infop->rx_buf->free); #ifdef TxBuffer printf("tx buffer chars used:\t\t%d\n", infop->tx_buf.used); #endif printf("received chars:\t\t\t%d good, %d exception\n", infop->recv_normal, infop->recv_exception); printf("transmitted chars:\t\t%d\n", infop->xmit); printf("modem signal deltas:\t\t%d\n", infop->mdm); printf("\n"); } /* end of cystatus() */ #endif #endif /* NCY > 0 */ Index: head/sys/dev/cy/cy_isa.c =================================================================== --- head/sys/dev/cy/cy_isa.c (revision 6781) +++ head/sys/dev/cy/cy_isa.c (revision 6782) @@ -1,1683 +1,1673 @@ /* * cyclades cyclom-y serial driver * Andrew Herbert , 17 August 1993 * * Copyright (c) 1993 Andrew Herbert. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name Andrew Herbert may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: cy.c,v 1.2 1995/02/15 18:41:41 bde Exp $ + * $Id: cy.c,v 1.3 1995/02/25 20:09:12 pst Exp $ */ /* * Device minor number encoding: * * c c x x u u u u - bits in the minor device number * * bits meaning * ---- ------- * uuuu physical serial line (i.e. unit) to use * 0-7 on a cyclom-8Y, 0-15 on a cyclom-16Y * xx unused * cc carrier control mode * 00 complete hardware carrier control of the tty. * DCD must be high for the open(2) to complete. * 01 dialin pseudo-device (not yet implemented) * 10 carrier ignored until a high->low transition * 11 carrier completed ignored */ /* * Known deficiencies: * * * no BREAK handling - breaks are ignored, and can't be sent either * * no support for bad-char reporting, except via PARMRK * * no support for dialin + dialout devices */ #include "cy.h" #if NCY > 0 /* This disgusing hack because we actually have 16 units on one controller */ #if NCY < 2 #undef NCY #define NCY (16) #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NetBSD #include #endif #include #include #include #define RxFifoThreshold 3 /* 3 characters (out of 12) in the receive * FIFO before an interrupt is generated */ #define FastRawInput /* bypass the regular char-by-char canonical input * processing whenever possible */ #define PollMode /* use polling-based irq service routine, not the * hardware svcack lines. Must be defined for * cyclom-16y boards. * * XXX cyclom-8y doesn't work without this defined * either (!) */ #define LogOverruns /* log receive fifo overruns */ #undef TxBuffer /* buffer driver output, to be slightly more * efficient * * XXX presently buggy */ #undef Smarts /* enable slightly more CD1400 intelligence. Mainly * the output CR/LF processing, plus we can avoid a * few checks usually done in ttyinput(). * * XXX not yet implemented, and not particularly * worthwhile either. */ #define CyDebug /* include debugging code (minimal effect on * performance) */ #define CY_RX_BUFS 2 /* two receive buffers per port */ #define CY_RX_BUF_SIZE 256 /* bytes per receive buffer */ #define CY_TX_BUF_SIZE 512 /* bytes per transmit buffer */ /* #define CD1400s_PER_CYCLOM 1 */ /* cyclom-4y */ /* #define CD1400s_PER_CYCLOM 2 */ /* cyclom-8y */ #define CD1400s_PER_CYCLOM 4 /* cyclom-16y */ /* FreeBSD's getty doesn't know option for setting RTS/CTS handshake. Its getty, like a lot of other old cruft, should be replaced with something which used POSIX tty interfaces which at least allow enabling it. In the meantime, use the force. */ #define ALWAYS_RTS_CTS 1 #if CD1400s_PER_CYCLOM < 4 #define CD1400_MEMSIZE 0x400 /* 4*256 bytes per chip: cyclom-[48]y */ #else #define CD1400_MEMSIZE 0x100 /* 256 bytes per chip: cyclom-16y */ /* XXX or is it 0x400 like the rest? */ #define CYCLOM_16 1 /* This is a cyclom-16Y */ #endif #define PORTS_PER_CYCLOM (CD1400_NO_OF_CHANNELS * CD1400s_PER_CYCLOM) #define CYCLOM_RESET_16 0x1400 /* cyclom-16y reset */ #define CYCLOM_CLEAR_INTR 0x1800 /* intr ack address */ #define CYCLOM_CLOCK 25000000 /* baud rate clock */ #define CY_UNITMASK 0x0f #define CY_CARRIERMASK 0xC0 #define CY_CARRIERSHIFT 6 #define UNIT(x) (minor(x) & CY_UNITMASK) #define CARRIER_MODE(x) ((minor(x) & CY_CARRIERMASK) >> CY_CARRIERSHIFT) typedef u_char * volatile cy_addr; int cyprobe(struct isa_device *dev); int cyattach(struct isa_device *isdp); void cystart(struct tty *tp); int cyparam(struct tty *tp, struct termios *t); int cyspeed(int speed, int *prescaler_io); static void cy_channel_init(dev_t dev, int reset); static void cd1400_channel_cmd(cy_addr base, u_char cmd); /* hsu@clinet.fi: sigh */ #ifdef __NetBSD__ #define DELAY(foo) delay(foo) void delay(int delay); #endif /* Better get rid of this until the core people agree on kernel interfaces. At least it will then compile on both WhichBSDs. */ #if 0 extern unsigned int delaycount; /* calibrated 1 ms cpu-spin delay */ #endif struct isa_driver cydriver = { cyprobe, cyattach, "cy" }; /* low-level ping-pong buffer structure */ struct cy_buf { u_char *next_char; /* location of next char to write */ u_int free; /* free chars remaining in buffer */ struct cy_buf *next_buf; /* circular, you know */ u_char buf[CY_RX_BUF_SIZE]; /* start of the buffer */ }; /* low-level ring buffer */ #ifdef TxBuffer struct cy_ring { u_char buf[CY_TX_BUF_SIZE]; u_char *head; u_char *tail; /* next pos. to insert char */ u_char *endish; /* physical end of buf */ u_int used; /* no. of chars in queue */ }; #endif /* * define a structure to keep track of each serial line */ struct cy { cy_addr base_addr; /* base address of this port's cd1400 */ struct tty *tty; u_int dtrwait; /* time (in ticks) to hold dtr low after close */ u_int recv_exception; /* exception chars received */ u_int recv_normal; /* normal chars received */ u_int xmit; /* chars transmitted */ u_int mdm; /* modem signal changes */ #ifdef CyDebug u_int start_count; /* no. of calls to cystart() */ u_int start_real; /* no. of calls that did something */ #endif u_char carrier_mode; /* hardware carrier handling mode */ /* * 0 = always use * 1 = always use (dialin port) * 2 = ignore during open, then use it * 3 = ignore completely */ u_char carrier_delta; /* true if carrier has changed state */ u_char fifo_overrun; /* true if cd1400 receive fifo has... */ u_char rx_buf_overrun; /* true if low-level buf overflow */ u_char intr_enable; /* CD1400 SRER shadow */ u_char modem_sig; /* CD1400 modem signal shadow */ u_char channel_control;/* CD1400 CCR control command shadow */ u_char cor[3]; /* CD1400 COR1-3 shadows */ #ifdef Smarts u_char spec_char[4]; /* CD1400 SCHR1-4 shadows */ #endif struct cy_buf *rx_buf; /* current receive buffer */ struct cy_buf rx_buf_pool[CY_RX_BUFS];/* receive ping-pong buffers */ #ifdef TxBuffer struct cy_ring tx_buf; /* transmit buffer */ #endif }; int cydefaultrate = TTYDEF_SPEED; cy_addr cyclom_base; /* base address of the card */ static struct cy *info[NCY*PORTS_PER_CYCLOM]; #ifdef __FreeBSD__ /* XXX actually only temporarily for 2.1-Development */ struct tty cy_tty[NCY*PORTS_PER_CYCLOM]; #else struct tty *cy_tty[NCY*PORTS_PER_CYCLOM]; #endif static volatile u_char timeout_scheduled = 0; /* true if a timeout has been scheduled */ #ifdef CyDebug u_int cy_svrr_probes = 0; /* debugging */ u_int cy_timeouts = 0; u_int cy_timeout_req = 0; #endif /**********************************************************************/ int cyprobe(struct isa_device *dev) { int i, j; u_char version = 0; /* firmware version */ /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ i = *(cy_addr)(dev->id_maddr + CYCLOM_RESET_16); DELAY(500); /* wait for the board to get its act together (500 us) */ for (i = 0; i < CD1400s_PER_CYCLOM; i++) { cy_addr base = dev->id_maddr + i * CD1400_MEMSIZE; /* wait for chip to become ready for new command */ for (j = 0; j < 100; j += 50) { DELAY(50); /* wait 50 us */ if (!*(base + CD1400_CCR)) break; } /* clear the GFRCR register */ *(base + CD1400_GFRCR) = 0; /* issue a reset command */ *(base + CD1400_CCR) = CD1400_CMD_RESET; /* wait for the CD1400 to initialise itself */ for (j = 0; j < 1000; j += 50) { DELAY(50); /* wait 50 us */ /* retrieve firmware version */ version = *(base + CD1400_GFRCR); if (version) break; } /* anything in the 40-4f range is fine */ if ((version & 0xf0) != 0x40) { return 0; } } return 1; /* found */ } int cyattach(struct isa_device *isdp) { /* u_char unit = UNIT(isdp->id_unit); */ int i, j, k; /* global variable used various routines */ cyclom_base = (cy_addr)isdp->id_maddr; for (i = 0, k = 0; i < CD1400s_PER_CYCLOM; i++) { cy_addr base = cyclom_base + i * CD1400_MEMSIZE; /* setup a 1ms clock tick */ *(base + CD1400_PPR) = CD1400_CLOCK_25_1MS; for (j = 0; j < CD1400_NO_OF_CHANNELS; j++, k++) { struct cy *ip; /* * grab some space. it'd be more polite to do this in cyopen(), * but hey. */ info[k] = ip = malloc(sizeof(struct cy), M_DEVBUF, M_WAITOK); /* clear all sorts of junk */ bzero(ip, sizeof(struct cy)); ip->base_addr = base; /* initialise the channel, without resetting it first */ cy_channel_init(k, 0); } } /* clear interrupts */ *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; return 1; } int cyopen(dev_t dev, int flag, int mode, struct proc *p) { u_int unit = UNIT(dev); struct cy *infop; cy_addr base; struct tty *tp; int error = 0; u_char carrier; if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) return (ENXIO); infop = info[unit]; base = infop->base_addr; #ifdef __FreeBSD__ infop->tty = &cy_tty[unit]; #else if (!cy_tty[unit]) infop->tty = cy_tty[unit] = ttymalloc(); #endif tp = infop->tty; tp->t_oproc = cystart; tp->t_param = cyparam; tp->t_dev = dev; if (!(tp->t_state & TS_ISOPEN)) { tp->t_state |= TS_WOPEN; ttychars(tp); if (tp->t_ispeed == 0) { tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = cydefaultrate; } (void) spltty(); cy_channel_init(unit, 1); /* reset the hardware */ /* * raise dtr and generally set things up correctly. this * has the side-effect of selecting the appropriate cd1400 * channel, to help us with subsequent channel control stuff */ cyparam(tp, &tp->t_termios); /* check carrier, and set t_state's TS_CARR_ON flag accordingly */ infop->modem_sig = *(base + CD1400_MSVR); carrier = infop->modem_sig & CD1400_MSVR_CD; if (carrier || (infop->carrier_mode >= 2)) tp->t_state |= TS_CARR_ON; else tp->t_state &=~ TS_CARR_ON; /* * enable modem & rx interrupts - relies on cyparam() * having selected the appropriate cd1400 channel */ infop->intr_enable = (1 << 7) | (1 << 4); *(base + CD1400_SRER) = infop->intr_enable; ttsetwater(tp); } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) return (EBUSY); if (!(flag & O_NONBLOCK)) while (!(tp->t_cflag & CLOCAL) && !(tp->t_state & TS_CARR_ON) && !error) error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI|PCATCH, ttopen, 0); (void) spl0(); if (!error) error = (*linesw[(u_char)tp->t_line].l_open)(dev, tp); return (error); } /* end of cyopen() */ void cyclose_wakeup(void *arg) { wakeup(arg); } /* end of cyclose_wakeup() */ int cyclose(dev_t dev, int flag, int mode, struct proc *p) { u_int unit = UNIT(dev); struct cy *infop = info[unit]; struct tty *tp = infop->tty; cy_addr base = infop->base_addr; int s; (*linesw[(u_char)tp->t_line].l_close)(tp, flag); s = spltty(); /* select the appropriate channel on the CD1400 */ *(base + CD1400_CAR) = (u_char)(unit & 0x03); /* disable this channel and lower DTR */ infop->intr_enable = 0; *(base + CD1400_SRER) = (u_char)0; /* no intrs */ *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; /* no DTR */ infop->modem_sig &= ~CD1400_MSVR_DTR; /* disable receiver (leave transmitter enabled) */ infop->channel_control = (1 << 4) | (1 << 3) | 1; cd1400_channel_cmd(base, infop->channel_control); splx(s); ttyclose(tp); #ifdef broken /* session holds a ref to the tty; can't deallocate */ ttyfree(tp); infop->tty = cy_tty[unit] = (struct tty *)NULL; #endif if (infop->dtrwait) { int error; timeout(cyclose_wakeup, (caddr_t)&infop->dtrwait, infop->dtrwait); do { error = tsleep((caddr_t)&infop->dtrwait, TTIPRI|PCATCH, "cyclose", 0); } while (error == ERESTART); } return 0; } /* end of cyclose() */ int cyread(dev_t dev, struct uio *uio, int flag) { u_int unit = UNIT(dev); struct tty *tp = info[unit]->tty; return (*linesw[(u_char)tp->t_line].l_read)(tp, uio, flag); } /* end of cyread() */ int cywrite(dev_t dev, struct uio *uio, int flag) { u_int unit = UNIT(dev); struct tty *tp = info[unit]->tty; #ifdef Smarts /* XXX duplicate ttwrite(), but without so much output processing on * CR & LF chars. Hardly worth the effort, given that high-throughput * sessions are raw anyhow. */ #else return (*linesw[(u_char)tp->t_line].l_write)(tp, uio, flag); #endif } /* end of cywrite() */ #ifdef Smarts /* standard line discipline input routine */ int cyinput(int c, struct tty *tp) { /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK * bits, as they are done by the CD1400. Hardly worth the effort, * given that high-throughput sessions are raw anyhow. */ } /* end of cyinput() */ #endif /* Smarts */ inline static void service_upper_rx(int unit) { struct cy *ip = info[unit]; struct tty *tp = ip->tty; struct cy_buf *buf; int i; u_char *ch; buf = ip->rx_buf; /* give service_rx() a new one */ disable_intr(); /* faster than spltty() */ ip->rx_buf = buf->next_buf; enable_intr(); if (tp->t_state & TS_ISOPEN) { ch = buf->buf; i = buf->next_char - buf->buf; #ifdef FastRawInput /* try to avoid calling the line discipline stuff if we can */ if ((tp->t_line == 0) && !(tp->t_iflag & (ICRNL | IMAXBEL | INLCR)) && !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG | PENDIN)) && !(tp->t_state & (TS_CNTTB | TS_LNCH))) { i = b_to_q(ch, i, &tp->t_rawq); if (i) { /* * we have no RTS flow control support on cy-8 * boards, so this is really just tough luck */ log(LOG_WARNING, "cy%d: tty input queue overflow\n", unit); } ttwakeup(tp); /* notify any readers */ } else #endif /* FastRawInput */ { while (i--) (*linesw[(u_char)tp->t_line].l_rint)((int)*ch++, tp); } } /* clear the buffer we've just processed */ buf->next_char = buf->buf; buf->free = CY_RX_BUF_SIZE; } /* end of service_upper_rx() */ #ifdef TxBuffer static void service_upper_tx(int unit) { struct cy *ip = info[unit]; struct tty *tp = ip->tty; tp->t_state &=~ (TS_BUSY|TS_FLUSH); if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } selwakeup(&tp->t_wsel); } if (tp->t_outq.c_cc > 0) { struct cy_ring *txq = &ip->tx_buf; int free_count = CY_TX_BUF_SIZE - ip->tx_buf.used; u_char *cp = txq->tail; int count; int chars_done; tp->t_state |= TS_BUSY; /* find the largest contig. copy we can do */ count = ((txq->endish - cp) > free_count) ? free_count : txq->endish - cp; count = ((cp + free_count) > txq->endish) ? txq->endish - cp : free_count; /* copy the first slab */ chars_done = q_to_b(&tp->t_outq, cp, count); /* check for wrap-around time */ cp += chars_done; if (cp == txq->endish) cp = txq->buf; /* back to the start */ /* copy anything else, after we've wrapped around */ if ((chars_done == count) && (count != free_count)) { /* copy the second slab */ count = q_to_b(&tp->t_outq, cp, free_count - count); cp += count; chars_done += count; } /* * update queue, protecting ourselves from any rampant * lower-layers */ disable_intr(); txq->tail = cp; txq->used += chars_done; enable_intr(); } if (!tp->t_outq.c_cc) tp->t_state &=~ TS_BUSY; } /* end of service_upper_tx() */ #endif /* TxBuffer */ inline static void service_upper_mdm(int unit) { struct cy *ip = info[unit]; if (ip->carrier_delta) { int carrier = ip->modem_sig & CD1400_MSVR_CD; struct tty *tp = ip->tty; if (!(*linesw[(u_char)tp->t_line].l_modem)(tp, carrier)) { cy_addr base = ip->base_addr; /* clear DTR */ disable_intr(); *(base + CD1400_CAR) = (u_char)(unit & 0x03); *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; ip->modem_sig &= ~CD1400_MSVR_DTR; ip->carrier_delta = 0; enable_intr(); } else { disable_intr(); ip->carrier_delta = 0; enable_intr(); } } } /* end of service_upper_mdm() */ /* upper level character processing routine */ void cytimeout(void *ptr) { int unit; timeout_scheduled = 0; #ifdef CyDebug cy_timeouts++; #endif /* check each port in turn */ for (unit = 0; unit < NCY*PORTS_PER_CYCLOM; unit++) { struct cy *ip = info[unit]; #ifndef TxBuffer struct tty *tp = ip->tty; #endif /* ignore anything that is not open */ if (!ip->tty) continue; /* * any received chars to handle? (doesn't matter if intr routine * kicks in while we're testing this) */ if (ip->rx_buf->free != CY_RX_BUF_SIZE) service_upper_rx(unit); #ifdef TxBuffer /* anything to add to the transmit buffer (low-water mark)? */ if (ip->tx_buf.used < CY_TX_BUF_SIZE/2) service_upper_tx(unit); #else if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } selwakeup(&tp->t_wsel); } #endif /* anything modem signals altered? */ service_upper_mdm(unit); /* any overruns to log? */ #ifdef LogOverruns if (ip->fifo_overrun) { /* * turn off the alarm - not important enough to bother * with interrupt protection. */ ip->fifo_overrun = 0; log(LOG_WARNING, "cy%d: receive fifo overrun\n", unit); } #endif if (ip->rx_buf_overrun) { /* * turn off the alarm - not important enough to bother * with interrupt protection. */ ip->rx_buf_overrun = 0; log(LOG_WARNING, "cy%d: receive buffer full\n", unit); } } } /* cytimeout() */ inline static void schedule_upper_service(void) { #ifdef CyDebug cy_timeout_req++; #endif if (!timeout_scheduled) { timeout(cytimeout, (caddr_t)0, 1); /* call next tick */ timeout_scheduled = 1; } } /* end of schedule_upper_service() */ /* initialise a channel on the cyclom board */ static void cy_channel_init(dev_t dev, int reset) { u_int unit = UNIT(dev); int carrier_mode = CARRIER_MODE(dev); struct cy *ip = info[unit]; cy_addr base = ip->base_addr; struct tty *tp = ip->tty; struct cy_buf *buf, *next_buf; int i; #ifndef PollMode u_char cd1400_unit; #endif /* clear the structure and refill it */ bzero(ip, sizeof(struct cy)); ip->base_addr = base; ip->tty = tp; ip->carrier_mode = carrier_mode; /* select channel of the CD1400 */ *(base + CD1400_CAR) = (u_char)(unit & 0x03); if (reset) cd1400_channel_cmd(base, 0x80); /* reset the channel */ /* set LIVR to 0 - intr routines depend on this */ *(base + CD1400_LIVR) = 0; #ifndef PollMode /* set top four bits of {R,T,M}ICR to the cd1400 * number, cd1400_unit */ cd1400_unit = unit / CD1400_NO_OF_CHANNELS; *(base + CD1400_RICR) = (u_char)(cd1400_unit << 4); *(base + CD1400_TICR) = (u_char)(cd1400_unit << 4); *(base + CD1400_MICR) = (u_char)(cd1400_unit << 4); #endif ip->dtrwait = hz/4; /* quarter of a second */ /* setup low-level buffers */ i = CY_RX_BUFS; ip->rx_buf = next_buf = &ip->rx_buf_pool[0]; while (i--) { buf = &ip->rx_buf_pool[i]; buf->next_char = buf->buf; /* first char to use */ buf->free = CY_RX_BUF_SIZE; /* i.e. empty */ buf->next_buf = next_buf; /* where to go next */ next_buf = buf; } #ifdef TxBuffer ip->tx_buf.endish = ip->tx_buf.buf + CY_TX_BUF_SIZE; /* clear the low-level tx buffer */ ip->tx_buf.head = ip->tx_buf.tail = ip->tx_buf.buf; ip->tx_buf.used = 0; #endif /* clear the low-level rx buffer */ ip->rx_buf->next_char = ip->rx_buf->buf; /* first char to use */ ip->rx_buf->free = CY_RX_BUF_SIZE; /* completely empty */ } /* end of cy_channel_init() */ /* service a receive interrupt */ inline static void service_rx(int cd, caddr_t base) { struct cy *infop; unsigned count; int ch; u_char serv_type, channel; #ifdef PollMode u_char save_rir, save_car; #endif /* setup */ #ifdef PollMode save_rir = *(base + CD1400_RIR); channel = cd * CD1400_NO_OF_CHANNELS + (save_rir & 0x3); save_car = *(base + CD1400_CAR); *(base + CD1400_CAR) = save_rir; /* enter modem service */ serv_type = *(base + CD1400_RIVR); #else serv_type = *(base + CD1400_SVCACKR); /* ack receive service */ channel = ((u_char)*(base + CD1400_RICR)) >> 2; /* get cyclom channel # */ #ifdef CyDebug if (channel >= PORTS_PER_CYCLOM) { printf("cy: service_rx - channel %02x\n", channel); panic("cy: service_rx - bad channel"); } #endif #endif infop = info[channel]; /* read those chars */ if (serv_type & CD1400_RIVR_EXCEPTION) { /* read the exception status */ u_char status = *(base + CD1400_RDSR); /* XXX is it a break? Do something if it is! */ /* XXX is IGNPAR not set? Store a null in the buffer. */ #ifdef LogOverruns if (status & CD1400_RDSR_OVERRUN) { #if 0 ch |= TTY_PE; /* for SLIP */ #endif infop->fifo_overrun++; } #endif infop->recv_exception++; } else { struct cy_buf *buf = infop->rx_buf; count = (u_char)*(base + CD1400_RDCR); /* how many to read? */ infop->recv_normal += count; if (buf->free < count) { infop->rx_buf_overrun += count; /* read & discard everything */ while (count--) ch = (u_char)*(base + CD1400_RDSR); } else { /* slurp it into our low-level buffer */ buf->free -= count; while (count--) { ch = (u_char)*(base + CD1400_RDSR); /* read the char */ *(buf->next_char++) = ch; } } } #ifdef PollMode *(base + CD1400_RIR) = (u_char)(save_rir & 0x3f); /* terminate service context */ #else *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ #endif } /* end of service_rx */ /* service a transmit interrupt */ inline static void service_tx(int cd, caddr_t base) { struct cy *ip; #ifdef TxBuffer struct cy_ring *txq; #else struct tty *tp; #endif u_char channel; #ifdef PollMode u_char save_tir, save_car; #else u_char vector; #endif /* setup */ #ifdef PollMode save_tir = *(base + CD1400_TIR); channel = cd * CD1400_NO_OF_CHANNELS + (save_tir & 0x3); save_car = *(base + CD1400_CAR); *(base + CD1400_CAR) = save_tir; /* enter tx service */ #else vector = *(base + CD1400_SVCACKT); /* ack transmit service */ channel = ((u_char)*(base + CD1400_TICR)) >> 2; /* get cyclom channel # */ #ifdef CyDebug if (channel >= PORTS_PER_CYCLOM) { printf("cy: service_tx - channel %02x\n", channel); panic("cy: service_tx - bad channel"); } #endif #endif ip = info[channel]; #ifdef TxBuffer txq = &ip->tx_buf; if (txq->used > 0) { cy_addr base = ip->base_addr; int count = min(CD1400_FIFOSIZE, txq->used); int chars_done = count; u_char *cp = txq->head; u_char *buf_end = txq->endish; /* ip->state |= CY_BUSY; */ while (count--) { *(base + CD1400_TDR) = *cp++; if (cp >= buf_end) cp = txq->buf; }; txq->head = cp; txq->used -= chars_done; /* important that this is atomic */ ip->xmit += chars_done; } /* * disable tx intrs if no more chars to send. we re-enable * them in cystart() */ if (!txq->used) { ip->intr_enable &=~ (1 << 2); *(base + CD1400_SRER) = ip->intr_enable; /* ip->state &= ~CY_BUSY; */ } #else tp = ip->tty; if (!(tp->t_state & TS_TTSTOP) && (tp->t_outq.c_cc > 0)) { cy_addr base = ip->base_addr; int count = min(CD1400_FIFOSIZE, tp->t_outq.c_cc); ip->xmit += count; tp->t_state |= TS_BUSY; while (count--) *(base + CD1400_TDR) = getc(&tp->t_outq); } /* * disable tx intrs if no more chars to send. we re-enable them * in cystart() */ if (!tp->t_outq.c_cc) { ip->intr_enable &=~ (1 << 2); *(base + CD1400_SRER) = ip->intr_enable; tp->t_state &= ~TS_BUSY; } #endif #ifdef PollMode *(base + CD1400_TIR) = (u_char)(save_tir & 0x3f); /* terminate service context */ #else *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ #endif } /* end of service_tx */ /* service a modem status interrupt */ inline static void service_mdm(int cd, caddr_t base) { struct cy *infop; u_char channel, deltas; #ifdef PollMode u_char save_mir, save_car; #else u_char vector; #endif /* setup */ #ifdef PollMode save_mir = *(base + CD1400_MIR); channel = cd * CD1400_NO_OF_CHANNELS + (save_mir & 0x3); save_car = *(base + CD1400_CAR); *(base + CD1400_CAR) = save_mir; /* enter modem service */ #else vector = *(base + CD1400_SVCACKM); /* ack modem service */ channel = ((u_char)*(base + CD1400_MICR)) >> 2; /* get cyclom channel # */ #ifdef CyDebug if (channel >= PORTS_PER_CYCLOM) { printf("cy: service_mdm - channel %02x\n", channel); panic("cy: service_mdm - bad channel"); } #endif #endif infop = info[channel]; /* read the siggies and see what's changed */ infop->modem_sig = (u_char)*(base + CD1400_MSVR); deltas = (u_char)*(base + CD1400_MISR); if ((infop->carrier_mode <= 2) && (deltas & CD1400_MISR_CDd)) /* something for the upper layer to deal with */ infop->carrier_delta = 1; infop->mdm++; /* terminate service context */ #ifdef PollMode *(base + CD1400_MIR) = (u_char)(save_mir & 0x3f); #else *(base + CD1400_EOSRR) = (u_char)0; #endif } /* end of service_mdm */ int cyintr(int unit) { int cd; u_char status; /* check each CD1400 in turn */ for (cd = 0; cd < CD1400s_PER_CYCLOM; cd++) { cy_addr base = cyclom_base + cd*CD1400_MEMSIZE; /* poll to see if it has any work */ while (status = (u_char)*(base + CD1400_SVRR)) { #ifdef CyDebug cy_svrr_probes++; #endif /* service requests as appropriate, giving priority to RX */ if (status & CD1400_SVRR_RX) service_rx(cd, base); if (status & CD1400_SVRR_TX) service_tx(cd, base); if (status & CD1400_SVRR_MDM) service_mdm(cd, base); } } /* request upper level service to deal with whatever happened */ schedule_upper_service(); /* re-enable interrupts on the cyclom */ *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; return 1; } int cyioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { int unit = UNIT(dev); struct cy *infop = info[unit]; struct tty *tp = infop->tty; int error; error = (*linesw[(u_char)tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); error = ttioctl(tp, cmd, data, flag #ifdef NetBSD , p #endif ); if (error >= 0) return (error); switch (cmd) { #ifdef notyet /* sigh - more junk to do XXX */ case TIOCSBRK: break; case TIOCCBRK: break; case TIOCSDTR: break; case TIOCCDTR: break; case TIOCMSET: break; case TIOCMBIS: break; case TIOCMBIC: break; #endif /* notyet */ case TIOCMGET: { int bits = 0; u_char status = infop->modem_sig; if (status & CD1400_MSVR_DTR) bits |= TIOCM_DTR | TIOCM_RTS; if (status & CD1400_MSVR_CD) bits |= TIOCM_CD; if (status & CD1400_MSVR_CTS) bits |= TIOCM_CTS; if (status & CD1400_MSVR_DSR) bits |= TIOCM_DSR; #ifdef CYCLOM_16 if (status & CD1400_MSVR_RI) bits |= TIOCM_RI; #endif if (infop->channel_control & 0x02) bits |= TIOCM_LE; *(int *)data = bits; break; } #ifdef TIOCMSBIDIR case TIOCMSBIDIR: return (ENOTTY); #endif /* TIOCMSBIDIR */ #ifdef TIOCMGBIDIR case TIOCMGBIDIR: return (ENOTTY); #endif /* TIOCMGBIDIR */ #ifdef TIOCMSDTRWAIT case TIOCMSDTRWAIT: /* must be root to set dtr delay */ if (p->p_ucred->cr_uid != 0) return(EPERM); infop->dtrwait = *(u_int *)data; break; #endif /* TIOCMSDTRWAIT */ #ifdef TIOCMGDTRWAIT case TIOCMGDTRWAIT: *(u_int *)data = infop->dtrwait; break; #endif /* TIOCMGDTRWAIT */ default: return (ENOTTY); } return 0; } /* end of cyioctl() */ int cyparam(struct tty *tp, struct termios *t) { u_char unit = UNIT(tp->t_dev); struct cy *infop = info[unit]; cy_addr base = infop->base_addr; int cflag = t->c_cflag; int iflag = t->c_iflag; int ispeed, ospeed; int itimeout; int iprescaler, oprescaler; int s; u_char cor_change = 0; u_char opt; if (!t->c_ispeed) t->c_ispeed = t->c_ospeed; s = spltty(); /* select the appropriate channel on the CD1400 */ *(base + CD1400_CAR) = unit & 0x03; /* handle DTR drop on speed == 0 trick */ if (t->c_ospeed == 0) { *(base + CD1400_DTR) = CD1400_DTR_CLEAR; infop->modem_sig &= ~CD1400_MSVR_DTR; } else { *(base + CD1400_DTR) = CD1400_DTR_SET; infop->modem_sig |= CD1400_MSVR_DTR; } /* set baud rates if they've changed from last time */ if ((ospeed = cyspeed(t->c_ospeed, &oprescaler)) < 0) return EINVAL; *(base + CD1400_TBPR) = (u_char)ospeed; *(base + CD1400_TCOR) = (u_char)oprescaler; if ((ispeed = cyspeed(t->c_ispeed, &iprescaler)) < 0) return EINVAL; *(base + CD1400_RBPR) = (u_char)ispeed; *(base + CD1400_RCOR) = (u_char)iprescaler; /* * set receive time-out period * generate a rx interrupt if no new chars are received in * this many ticks * don't bother comparing old & new VMIN, VTIME and ispeed - it * can't be much worse just to calculate and set it each time! * certainly less hassle. :-) */ /* * calculate minimum timeout period: * 5 ms or the time it takes to receive 1 char, rounded up to the * next ms, whichever is greater */ if (t->c_ispeed > 0) { itimeout = (t->c_ispeed > 2200) ? 5 : (10000/t->c_ispeed + 1); /* if we're using VTIME as an inter-char timeout, and it is set to * be longer than the minimum calculated above, go for it */ if (t->c_cc[VMIN] && t->c_cc[VTIME] && t->c_cc[VTIME]*10 > itimeout) itimeout = t->c_cc[VTIME]*10; /* store it, taking care not to overflow the byte-sized register */ *(base + CD1400_RTPR) = (u_char)((itimeout <= 255) ? itimeout : 255); } /* * channel control * receiver enable * transmitter enable (always set) */ opt = (1 << 4) | (1 << 3) | ((cflag & CREAD) ? (1 << 1) : 1); if (opt != infop->channel_control) { infop->channel_control = opt; cd1400_channel_cmd(base, opt); } #ifdef Smarts /* set special chars */ if (t->c_cc[VSTOP] != _POSIX_VDISABLE && (t->c_cc[VSTOP] != infop->spec_char[0])) { *(base + CD1400_SCHR1) = infop->spec_char[0] = t->c_cc[VSTOP]; } if (t->c_cc[VSTART] != _POSIX_VDISABLE && (t->c_cc[VSTART] != infop->spec_char[1])) { *(base + CD1400_SCHR2) = infop->spec_char[0] = t->c_cc[VSTART]; } if (t->c_cc[VINTR] != _POSIX_VDISABLE && (t->c_cc[VINTR] != infop->spec_char[2])) { *(base + CD1400_SCHR3) = infop->spec_char[0] = t->c_cc[VINTR]; } if (t->c_cc[VSUSP] != _POSIX_VDISABLE && (t->c_cc[VSUSP] != infop->spec_char[3])) { *(base + CD1400_SCHR4) = infop->spec_char[0] = t->c_cc[VSUSP]; } #endif /* * set channel option register 1 - * parity mode * stop bits * char length */ opt = 0; /* parity */ if (cflag & PARENB) { if (cflag & PARODD) opt |= 1 << 7; opt |= 2 << 5; /* normal parity mode */ } if (!(iflag & INPCK)) opt |= 1 << 4; /* ignore parity */ /* stop bits */ if (cflag & CSTOPB) opt |= 2 << 2; /* char length */ opt |= (cflag & CSIZE) >> 8; /* nasty, but fast */ if (opt != infop->cor[0]) { cor_change |= 1 << 1; *(base + CD1400_COR1) = opt; } /* * set channel option register 2 - * flow control */ opt = 0; #ifdef Smarts if (iflag & IXANY) opt |= 1 << 7; /* auto output restart on any char after XOFF */ if (iflag & IXOFF) opt |= 1 << 6; /* auto XOFF output flow-control */ #endif #ifndef ALWAYS_RTS_CTS if (cflag & CCTS_OFLOW) #endif opt |= 1 << 1; /* auto CTS flow-control */ if (opt != infop->cor[1]) { cor_change |= 1 << 2; *(base + CD1400_COR2) = opt; } /* * set channel option register 3 - * receiver FIFO interrupt threshold * flow control */ opt = RxFifoThreshold; /* rx fifo threshold */ #ifdef Smarts if (t->c_lflag & ICANON) opt |= 1 << 6; /* detect INTR & SUSP chars */ if (iflag & IXOFF) opt |= (1 << 5) | (1 << 4); /* transparent in-band flow control */ #endif if (opt != infop->cor[2]) { cor_change |= 1 << 3; *(base + CD1400_COR3) = opt; } /* notify the CD1400 if COR1-3 have changed */ if (cor_change) { cor_change |= 1 << 6; /* COR change flag */ cd1400_channel_cmd(base, cor_change); } /* * set channel option register 4 - * CR/NL processing * break processing * received exception processing */ opt = 0; if (iflag & IGNCR) opt |= 1 << 7; #ifdef Smarts /* * we need a new ttyinput() for this, as we don't want to * have ICRNL && INLCR being done in both layers, or to have * synchronisation problems */ if (iflag & ICRNL) opt |= 1 << 6; if (iflag & INLCR) opt |= 1 << 5; #endif if (iflag & IGNBRK) opt |= 1 << 4; if (!(iflag & BRKINT)) opt |= 1 << 3; if (iflag & IGNPAR) #ifdef LogOverruns opt |= 0; /* broken chars cause receive exceptions */ #else opt |= 2; /* discard broken chars */ #endif else { if (iflag & PARMRK) opt |= 4; /* precede broken chars with 0xff 0x0 */ else #ifdef LogOverruns opt |= 0; /* broken chars cause receive exceptions */ #else opt |= 3; /* convert framing/parity errs to nulls */ #endif } *(base + CD1400_COR4) = opt; /* * set channel option register 5 - */ opt = 0; if (iflag & ISTRIP) opt |= 1 << 7; if (t->c_iflag & IEXTEN) { opt |= 1 << 6; /* enable LNEXT (e.g. ctrl-v quoting) handling */ } #ifdef Smarts if (t->c_oflag & ONLCR) opt |= 1 << 1; if (t->c_oflag & OCRNL) opt |= 1; #endif *(base + CD1400_COR5) = opt; /* * set modem change option register 1 * generate modem interrupts on which 1 -> 0 input transitions * also controls auto-DTR output flow-control, which we don't use */ opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ *(base + CD1400_MCOR1) = opt; /* * set modem change option register 2 * generate modem interrupts on specific 0 -> 1 input transitions */ opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ *(base + CD1400_MCOR2) = opt; splx(s); return 0; } /* end of cyparam */ void cystart(struct tty *tp) { u_char unit = UNIT(tp->t_dev); struct cy *infop = info[unit]; cy_addr base = infop->base_addr; int s; #ifdef CyDebug infop->start_count++; #endif /* check the flow-control situation */ if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) return; if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } selwakeup(&tp->t_wsel); } #ifdef TxBuffer service_upper_tx(unit); /* feed the monster */ #endif s = spltty(); if (!(infop->intr_enable & (1 << 2))) { /* select the channel */ *(base + CD1400_CAR) = unit & (u_char)3; /* (re)enable interrupts to set things in motion */ infop->intr_enable |= (1 << 2); *(base + CD1400_SRER) = infop->intr_enable; infop->start_real++; } splx(s); } /* end of cystart() */ int cystop(struct tty *tp, int flag) { u_char unit = UNIT(tp->t_dev); struct cy *ip = info[unit]; cy_addr base = ip->base_addr; int s; s = spltty(); /* select the channel */ *(base + CD1400_CAR) = unit & 3; /* halt output by disabling transmit interrupts */ ip->intr_enable &=~ (1 << 2); *(base + CD1400_SRER) = ip->intr_enable; splx(s); return 0; } struct tty * cydevtotty(dev_t dev) { u_char unit = UNIT(dev); if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) return NULL; return info[unit]->tty; -} - -int -cyselect(dev_t dev, int rw, struct proc *p) -{ - u_char unit = UNIT(dev); - if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) - return (ENXIO); - - return (ttyselect(info[unit]->tty, rw, p)); } int cyspeed(int speed, int *prescaler_io) { int actual; int error; int divider; int prescaler; int prescaler_unit; if (speed == 0) return 0; if (speed < 0 || speed > 150000) return -1; /* determine which prescaler to use */ for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; prescaler_unit--, prescaler >>= 2) { if (CYCLOM_CLOCK/prescaler/speed > 63) break; } divider = (CYCLOM_CLOCK/prescaler*2/speed + 1)/2; /* round off */ if (divider > 255) divider = 255; actual = CYCLOM_CLOCK/prescaler/divider; error = ((actual-speed)*2000/speed +1)/2; /* percentage */ /* 3.0% max error tolerance */ if (error < -30 || error > 30) return -1; #if 0 printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); printf("divider = %d (%x)\n", divider, divider); printf("actual = %d\n", actual); printf("error = %d\n", error); #endif *prescaler_io = prescaler_unit; return divider; } /* end of cyspeed() */ static void cd1400_channel_cmd(cy_addr base, u_char cmd) { /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, as the card is probed every round? Replaced delaycount with 8k. Either delaycount has to be implemented in FreeBSD or more sensible way of doing these should be implemented. DELAY isn't enough here. */ unsigned maxwait = 5 * 8 * 1024; /* approx. 5 ms */ /* wait for processing of previous command to complete */ while (*(base + CD1400_CCR) && maxwait--) ; if (!maxwait) log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n", 5 * 8 * 1024); *(base + CD1400_CCR) = cmd; } /* end of cd1400_channel_cmd() */ #ifdef CyDebug /* useful in ddb */ void cyclear(void) { /* clear the timeout request */ disable_intr(); timeout_scheduled = 0; enable_intr(); } void cyclearintr(void) { /* clear interrupts */ *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; } int cyparam_dummy(struct tty *tp, struct termios *t) { return 0; } void cyset(int unit, int active) { if (unit < 0 || unit >= /* NCY *? */ PORTS_PER_CYCLOM) { printf("bad unit number %d\n", unit); return; } #ifdef __FreeBSD__ cy_tty[unit].t_param = active ? cyparam : cyparam_dummy; #else cy_tty[unit]->t_param = active ? cyparam : cyparam_dummy; #endif } /* useful in ddb */ void cystatus(int unit) { struct cy *infop = info[unit]; struct tty *tp = infop->tty; cy_addr base = infop->base_addr; printf("info for channel %d\n", unit); printf("------------------\n"); printf("cd1400 base address:\t0x%x\n", (int)infop->base_addr); /* select the port */ *(base + CD1400_CAR) = (u_char)unit; printf("saved channel_control:\t%02x\n", infop->channel_control); printf("saved cor1:\t\t%02x\n", infop->cor[0]); printf("service request enable reg:\t%02x (%02x cached)\n", (u_char)*(base + CD1400_SRER), infop->intr_enable); printf("service request register:\t%02x\n", (u_char)*(base + CD1400_SVRR)); printf("\n"); printf("modem status:\t\t\t%02x (%02x cached)\n", (u_char)*(base + CD1400_MSVR), infop->modem_sig); printf("rx/tx/mdm interrupt registers:\t%02x %02x %02x\n", (u_char)*(base + CD1400_RIR), (u_char)*(base + CD1400_TIR), (u_char)*(base + CD1400_MIR)); printf("\n"); if (tp) { printf("tty state:\t\t\t%04x\n", tp->t_state); printf("upper layer queue lengths:\t%d raw, %d canon, %d output\n", tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); } else printf("tty state:\t\t\tclosed\n"); printf("\n"); printf("calls to cystart():\t\t%d (%d useful)\n", infop->start_count, infop->start_real); printf("\n"); printf("total cyclom service probes:\t%d\n", cy_svrr_probes); printf("calls to upper layer:\t\t%d\n", cy_timeouts); printf("rx buffer chars free:\t\t%d\n", infop->rx_buf->free); #ifdef TxBuffer printf("tx buffer chars used:\t\t%d\n", infop->tx_buf.used); #endif printf("received chars:\t\t\t%d good, %d exception\n", infop->recv_normal, infop->recv_exception); printf("transmitted chars:\t\t%d\n", infop->xmit); printf("modem signal deltas:\t\t%d\n", infop->mdm); printf("\n"); } /* end of cystatus() */ #endif #endif /* NCY > 0 */ Index: head/sys/dev/sio/sio.c =================================================================== --- head/sys/dev/sio/sio.c (revision 6781) +++ head/sys/dev/sio/sio.c (revision 6782) @@ -1,2268 +1,2259 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.67 1995/02/25 20:09:14 pst Exp $ + * $Id: sio.c,v 1.68 1995/02/26 02:30:18 bde Exp $ */ #include "sio.h" #if NSIO > 0 /* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. */ #include #include #include #include #define TTYDEFCHARS /* XXX TK2.0 */ #include #undef TTYDEFCHARS #include #include #include #include #include #include #include #include #include #include #include #include /* XXX just to get at `imen' */ #include #include #include #include /* * XXX temporary kludges for 2.0 (XXX TK2.0). */ #define TSA_CARR_ON(tp) ((void *)&(tp)->t_rawq) #define TSA_OCOMPLETE(tp) ((void *)&(tp)->t_outq) #define TSA_OLOWAT(tp) ((void *)&(tp)->t_outq) void termioschars(t) struct termios *t; { bcopy(ttydefchars, t->c_cc, sizeof t->c_cc); } #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE) #define RS_IBUFSIZE 256 #define TTY_BI TTY_FE /* XXX */ #define TTY_OE TTY_PE /* XXX */ #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ #define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) #define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) #define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) #endif /* COM_MULTIPORT */ #define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) #define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */ #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart() and comflush()) * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_int Port_t; /* hardware port */ typedef u_char bool_t; /* boolean */ /* com device structure */ struct com_s { u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ u_char ftl; /* current rx fifo trigger level */ u_char ftl_init; /* ftl_max for next open() */ u_char ftl_max; /* maximum ftl for curent open() */ bool_t hasfifo; /* nonzero for 16550 UARTs */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t poll; /* nonzero if polling is required */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ u_char *obufend; /* end of output buffer */ u_char *optr; /* next char to output */ Port_t data_port; /* i/o ports */ Port_t int_id_port; Port_t iobase; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; struct timeval timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */ #define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Output buffer. Someday we should avoid copying. Twice. */ u_char obuf[256]; }; /* * The public functions in the com module ought to be declared in a com-driver * system header. */ /* Interrupt handling entry points. */ void siointr __P((int unit)); void siopoll __P((void)); /* Device switch entry points. */ int sioopen __P((dev_t dev, int oflags, int devtype, struct proc *p)); int sioclose __P((dev_t dev, int fflag, int devtype, struct proc *p)); int sioread __P((dev_t dev, struct uio *uio, int ioflag)); int siowrite __P((dev_t dev, struct uio *uio, int ioflag)); int sioioctl __P((dev_t dev, int cmd, caddr_t data, int fflag, struct proc *p)); void siostop __P((struct tty *tp, int rw)); #define sioreset noreset int sioselect __P((dev_t dev, int rw, struct proc *p)); #define siommap nommap #define siostrategy nostrategy /* Console device entry points. */ int siocncheckc __P((dev_t dev)); int siocngetc __P((dev_t dev)); struct consdev; void siocninit __P((struct consdev *cp)); void siocnprobe __P((struct consdev *cp)); void siocnputc __P((dev_t dev, int c)); static int sioattach __P((struct isa_device *dev)); static timeout_t siodtrwakeup; static void comflush __P((struct com_s *com)); static void comhardclose __P((struct com_s *com)); static void siointr1 __P((struct com_s *com)); static void commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static int sioprobe __P((struct isa_device *dev)); static void sioregisterdev __P((struct isa_device *id)); static void comstart __P((struct tty *tp)); static timeout_t comwakeup; static int tiocm_xxx2mcr __P((int tiocm_xxx)); #ifdef DSI_SOFT_MODEM static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr)); #endif /* DSI_SOFT_MODEM */ /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) static struct timeval intr_timestamp; struct isa_driver siodriver = { sioprobe, sioattach, "sio" }; #ifdef COMCONSOLE #undef COMCONSOLE #define COMCONSOLE 1 #else #define COMCONSOLE 0 #endif static int comconsole = CONUNIT; static speed_t comdefaultrate = TTYDEF_SPEED; static u_int com_events; /* input chars + weighted output completions */ static int commajor; #if 0 /* XXX TK2.0 */ struct tty *sio_tty[NSIO]; #else struct tty sio_tty[NSIO]; #endif extern struct tty *constty; /* XXX */ #ifdef KGDB #include "machine/remote-sl.h" extern int kgdb_dev; extern int kgdb_rate; extern int kgdb_debug_init; #endif static struct speedtab comspeedtab[] = { 0, 0, 50, COMBRD(50), 75, COMBRD(75), 110, COMBRD(110), 134, COMBRD(134), 150, COMBRD(150), 200, COMBRD(200), 300, COMBRD(300), 600, COMBRD(600), 1200, COMBRD(1200), 1800, COMBRD(1800), 2400, COMBRD(2400), 4800, COMBRD(4800), 9600, COMBRD(9600), 19200, COMBRD(19200), 38400, COMBRD(38400), 57600, COMBRD(57600), 115200, COMBRD(115200), -1, -1 }; /* XXX - configure this list */ static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static int sioprobe(dev) struct isa_device *dev; { static bool_t already_init; Port_t *com_ptr; bool_t failures[10]; int fn; struct isa_device *idev; Port_t iobase; u_char mcr_image; int result; if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ for (com_ptr = likely_com_ports; com_ptr < &likely_com_ports[sizeof likely_com_ports / sizeof likely_com_ports[0]]; ++com_ptr) outb(*com_ptr + com_mcr, 0); already_init = TRUE; } /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(dev)) { idev = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(dev)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", dev->id_unit, COM_MPMASTER(dev)); return (0); } if (!COM_NOTAST4(dev)) { outb(idev->id_iobase + com_scr, idev->id_irq ? 0x80 : 0); mcr_image = 0; } } #endif /* COM_MULTIPORT */ if (idev->id_irq == 0) mcr_image = 0; bzero(failures, sizeof failures); iobase = dev->id_iobase; /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ disable_intr(); /* EXTRA DELAY? */ /* * XXX DELAY() reenables CPU interrupts. This is a problem for * shared interrupts after the first device using one has been * successfully probed - config_isadev() has enabled the interrupt * in the ICU. */ outb(IO_ICU1 + 1, 0xff); /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ outb(iobase + com_cfcr, CFCR_DLAB); outb(iobase + com_dlbl, COMBRD(9600) & 0xff); outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8); outb(iobase + com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (9600 / 10)); /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); outb(iobase + com_ier, 0); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ outb(iobase + com_ier, IER_ETXRDY); /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); DELAY((1 + 2) * 1000000 / (9600 / 10)); /* * Turn off loopback mode so that the interrupt gate works again * (MCR_IENABLE was hidden). This should leave the device driving * an interrupt line high. It doesn't matter if the interrupt * line oscillates while we are not looking at it, since interrupts * are disabled. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ /* EXTRA DELAY? */ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; failures[1] = inb(iobase + com_ier) - IER_ETXRDY; failures[2] = inb(iobase + com_mcr) - mcr_image; if (idev->id_irq != 0) failures[3] = isa_irq_pending(idev) ? 0 : 1; failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; if (idev->id_irq != 0) failures[5] = isa_irq_pending(idev) ? 1 : 0; failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; /* * Turn off all device interrupts and check that they go off properly. * Leave MCR_IENABLE alone. For ports without a master port, it gates * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving at) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ outb(iobase + com_ier, 0); outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = inb(iobase + com_ier); if (idev->id_irq != 0) failures[8] = isa_irq_pending(idev) ? 1 : 0; failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; outb(IO_ICU1 + 1, imen); /* XXX */ enable_intr(); result = IO_COMSIZE; for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) { outb(iobase + com_mcr, 0); result = 0; if (COM_VERBOSE(dev)) printf("sio%d: probe test %d failed\n", dev->id_unit, fn); } return (result); } static struct kern_devconf kdc_sio[NSIO] = { { 0, 0, 0, /* filled in by dev_attach */ "sio", 0, { MDDT_ISA, 0, "tty" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ 0, /* parentdata */ DC_UNCONFIGURED, "RS-232 serial port" } }; static void sioregisterdev(id) struct isa_device *id; { int unit; unit = id->id_unit; if (unit != 0) kdc_sio[unit] = kdc_sio[0]; kdc_sio[unit].kdc_unit = unit; kdc_sio[unit].kdc_isa = id; kdc_sio[unit].kdc_state = DC_IDLE; dev_attach(&kdc_sio[unit]); } static int sioattach(isdp) struct isa_device *isdp; { struct com_s *com; static bool_t comwakeup_started = FALSE; Port_t iobase; int s; int unit; isdp->id_ri_flags |= RI_FAST; iobase = isdp->id_iobase; unit = isdp->id_unit; com = malloc(sizeof *com, M_TTYS, M_NOWAIT); if (com == NULL) return (0); /* * sioprobe() has initialized the device registers as follows: * o cfcr = CFCR_8BITS. * It is most important that CFCR_DLAB is off, so that the * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->no_irq = isdp->id_irq == 0; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->iobase = iobase; com->data_port = iobase + com_data; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; } termioschars(&com->it_in); com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); #ifdef DSI_SOFT_MODEM if((inb(iobase+7) ^ inb(iobase+7)) & 0x80) { printf(" Digicom Systems, Inc. SoftModem"); goto determined_type; } #endif /* DSI_SOFT_MODEM */ #ifdef COM_MULTIPORT if (!COM_ISMULTIPORT(isdp)) #endif { u_char scr; u_char scr1; u_char scr2; scr = inb(iobase + com_scr); outb(iobase + com_scr, 0xa5); scr1 = inb(iobase + com_scr); outb(iobase + com_scr, 0x5a); scr2 = inb(iobase + com_scr); outb(iobase + com_scr, scr); if (scr1 != 0xa5 || scr2 != 0x5a) { printf(" 8250"); goto determined_type; } } outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14); DELAY(100); switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_TRIGGER_1: printf(" 16450"); break; case FIFO_TRIGGER_4: printf(" 16450?"); break; case FIFO_TRIGGER_8: printf(" 16550?"); break; case FIFO_TRIGGER_14: printf(" 16550A"); if (COM_NOFIFO(isdp)) printf(" fifo disabled"); else { com->hasfifo = TRUE; com->ftl_init = FIFO_TRIGGER_14; com->tx_fifo_size = 16; } break; } outb(iobase + com_fifo, 0); determined_type: ; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(isdp)) { com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(isdp)) printf(" master"); printf(")"); com->no_irq = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(isdp))->id_irq == 0; } #endif /* COM_MULTIPORT */ printf("\n"); sioregisterdev(isdp); #ifdef KGDB if (kgdb_dev == makedev(commajor, unit)) { if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) kgdb_dev = -1; /* can't debug over console port */ else { int divisor; /* * XXX now unfinished and broken. Need to do * something more like a full open(). There's no * suitable interrupt handler so don't enable device * interrupts. Watch out for null tp's. */ outb(iobase + com_cfcr, CFCR_DLAB); divisor = ttspeedtab(kgdb_rate, comspeedtab); outb(iobase + com_dlbl, divisor & 0xFF); outb(iobase + com_dlbh, (u_int) divisor >> 8); outb(iobase + com_cfcr, CFCR_8BITS); outb(com->modem_status_port, com->mcr_image |= MCR_DTR | MCR_RTS); if (kgdb_debug_init) { /* * Print prefix of device name, * let kgdb_connect print the rest. */ printf("sio%d: ", unit); kgdb_connect(1); } else printf("sio%d: kgdb enabled\n", unit); } } #endif s = spltty(); com_addr(unit) = com; splx(s); if (!comwakeup_started) { comwakeup((void *)NULL); comwakeup_started = TRUE; } return (1); } /* ARGSUSED */ int sioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); if (mynor & CONTROL_MASK) return (0); #if 0 /* XXX TK2.0 */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else tp = com->tp = &sio_tty[unit]; #endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); if (error != 0) goto out; } kdc_sio[unit].kdc_state = DC_BUSY; if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "siobi", 0); if (error != 0) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; commctl(com, MCR_DTR | MCR_RTS, DMSET); com->ftl_max = com->ftl_init; com->poll = com->no_irq; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; /* * XXX we should goto open_top if comparam() slept. */ ttsetwater(tp); iobase = com->iobase; if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | FIFO_ENABLE | com->ftl); DELAY(100); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(100); (void) inb(com->data_port); } } disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); enable_intr(); /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "siobi" * instead of "siodcd". */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); --com->wopeners; if (error != 0) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } /*ARGSUSED*/ int sioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); splx(s); return (0); } static void comhardclose(com) struct com_s *com; { Port_t iobase; int s; struct tty *tp; int unit; unit = DEV_TO_UNIT(com->tp->t_dev); iobase = com->iobase; s = spltty(); com->poll = FALSE; com->do_timestamp = 0; outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); #ifdef KGDB /* do not disable interrupts or hang up if debugging */ if (kgdb_dev != makedev(commajor, unit)) #endif { outb(iobase + com_ier, 0); tp = com->tp; if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { commctl(com, MCR_RTS, DMSET); if (com->dtr_wait != 0) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ if (!(com->state & CS_DTR_OFF)) kdc_sio[unit].kdc_state = DC_IDLE; splx(s); } int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); tp = com_addr(MINOR_TO_UNIT(mynor))->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty && unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) constty = NULL; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; kdc_sio[DEV_TO_UNIT(com->tp->t_dev)].kdc_state = DC_IDLE; wakeup(&com->dtr_wait); } /* Interrupt routine for timekeeping purposes */ void siointrts(unit) int unit; { /* * XXX microtime() reenables CPU interrupts. We can't afford to * be interrupted and don't want to slow down microtime(), so lock * out interrupts in another way. */ outb(IO_ICU1 + 1, 0xff); microtime(&intr_timestamp); disable_intr(); outb(IO_ICU1 + 1, imen); siointr(unit); } void siointr(unit) int unit; { #ifndef COM_MULTIPORT siointr1(com_addr(unit)); #else /* COM_MULTIPORT */ struct com_s *com; bool_t possibly_more_intrs; /* * Loop until there is no activity on any port. This is necessary * to get an interrupt edge more than to avoid another interrupt. * If the IRQ signal is just an OR of the IRQ signals from several * devices, then the edge from one may be lost because another is * on. */ do { possibly_more_intrs = FALSE; for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && (inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { siointr1(com); possibly_more_intrs = TRUE; } } } while (possibly_more_intrs); #endif /* COM_MULTIPORT */ } static void siointr1(com) struct com_s *com; { u_char line_status; u_char modem_status; u_char *ioptr; u_char recv_data; if (com->do_timestamp) /* XXX a little bloat here... */ com->timestamp = intr_timestamp; while (TRUE) { line_status = inb(com->line_status_port); /* input event? (check first to help avoid overruns) */ while (line_status & LSR_RCV_MASK) { /* break/unnattached error bits or real input? */ if (!(line_status & LSR_RXRDY)) recv_data = 0; else recv_data = inb(com->data_port); ++com->bytes_in; if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #ifdef KGDB /* trap into kgdb? (XXX - needs testing and optim) */ if (recv_data == FRAME_END && !(com->tp->t_state & TS_ISOPEN) && kgdb_dev == makedev(commajor, unit)) { kgdb_connect(0); continue; } #endif /* KGDB */ ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { ++com_events; #if 0 /* for testing input latency vs efficiency */ if (com->iptr - com->ibuf == 8) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); /* XXX - move this out of isr */ if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } /* * "& 0x7F" is to avoid the gcc-1.40 generating a slow * jump from the top of the loop to here */ line_status = inb(com->line_status_port) & 0x7F; } /* modem status change? (always check before doing output) */ modem_status = inb(com->modem_status_port); if (modem_status != com->last_modem_status) { /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; setsofttty(); } /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } } /* output queued and everything ready? */ if (line_status & LSR_TXRDY && com->state >= (CS_ODEVREADY | CS_BUSY | CS_TTGO)) { ioptr = com->optr; if (com->tx_fifo_size > 1) { u_int ocount; ocount = com->obufend - ioptr; if (ocount > com->tx_fifo_size) ocount = com->tx_fifo_size; com->bytes_out += ocount; do outb(com->data_port, *ioptr++); while (--ocount != 0); } else { outb(com->data_port, *ioptr++); ++com->bytes_out; } com->optr = ioptr; if (ioptr >= com->obufend) { /* output just completed */ com_events += LOTS_OF_EVENTS; com->state ^= (CS_ODONE | CS_BUSY); setsofttty(); /* handle at high level ASAP */ } } /* finished? */ #ifndef COM_MULTIPORT if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } } static int tiocm_xxx2mcr(tiocm_xxx) int tiocm_xxx; { int mcr; mcr = 0; if (tiocm_xxx & TIOCM_DTR) mcr |= MCR_DTR; if (tiocm_xxx & TIOCM_RTS) mcr |= MCR_RTS; return (mcr); } int sioioctl(dev, cmd, data, flag, p) dev_t dev; int cmd; caddr_t data; int flag; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mcr; int msr; int mynor; int s; int tiocm_xxx; struct tty *tp; mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); iobase = com->iobase; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); #ifdef DSI_SOFT_MODEM /* * Download micro-code to Digicom modem. */ case TIOCDSIMICROCODE: { u_long l; u_char *p,*pi; pi = (u_char*)(*(caddr_t*)data); error = copyin(pi,&l,sizeof l); if(error) {return error;}; pi += sizeof l; p = malloc(l,M_TEMP,M_NOWAIT); if(!p) {return ENOBUFS;} error = copyin(pi,p,l); if(error) {free(p,M_TEMP); return error;}; if(error = LoadSoftModem( MINOR_TO_UNIT(mynor),iobase,l,p)) {free(p,M_TEMP); return error;} free(p,M_TEMP); return(0); } #endif /* DSI_SOFT_MODEM */ default: return (ENOTTY); } } tp = com->tp; if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); error = ttioctl(tp, cmd, data, flag); if (error >= 0) return (error); s = spltty(); switch (cmd) { case TIOCSBRK: outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: commctl(com, MCR_DTR, DMBIS); break; case TIOCCDTR: commctl(com, MCR_DTR, DMBIC); break; case TIOCMSET: commctl(com, tiocm_xxx2mcr(*(int *)data), DMSET); break; case TIOCMBIS: commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIS); break; case TIOCMBIC: commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIC); break; case TIOCMGET: tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */ mcr = com->mcr_image; if (mcr & MCR_DTR) tiocm_xxx |= TIOCM_DTR; if (mcr & MCR_RTS) tiocm_xxx |= TIOCM_RTS; msr = com->prev_modem_status; if (msr & MSR_CTS) tiocm_xxx |= TIOCM_CTS; if (msr & MSR_DCD) tiocm_xxx |= TIOCM_CD; if (msr & MSR_DSR) tiocm_xxx |= TIOCM_DSR; /* * XXX - MSR_RI is naturally volatile, and we make MSR_TERI * more volatile by reading the modem status a lot. Perhaps * we should latch both bits until the status is read here. */ if (msr & (MSR_RI | MSR_TERI)) tiocm_xxx |= TIOCM_RI; *(int *)data = tiocm_xxx; break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; default: splx(s); return (ENOTTY); } splx(s); return (0); } /* cancel pending output */ static void comflush(com) struct com_s *com; { disable_intr(); if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); enable_intr(); com->tp->t_state &= ~TS_BUSY; } void siopoll() { int unit; if (com_events == 0) return; repeat: for (unit = 0; unit < NSIO; ++unit) { u_char *buf; struct com_s *com; u_char *ibuf; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL) { /* * XXX forget any events related to closed devices * (actually never opened devices) so that we don't * loop. */ disable_intr(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; enable_intr(); if (incc != 0) log(LOG_DEBUG, "sio%d: %d events for device with no tp\n", unit, incc); continue; } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { /* * Prepare to reduce input latency for packet * discplines with a end of packet character. * XXX should be elsewhere. */ if (tp->t_line == SLIPDISC) com->hotchar = 0xc0; else if (tp->t_line == PPPDISC) com->hotchar = 0x7e; else com->hotchar = 0; buf = ibuf; disable_intr(); incc = com->iptr - buf; com_events -= incc; if (ibuf == com->ibuf1) ibuf = com->ibuf2; else ibuf = com->ibuf1; com->ibufend = ibuf + RS_IBUFSIZE; com->ihighwater = ibuf + RS_IHIGHWATER; com->iptr = ibuf; /* * There is now room for another low-level buffer full * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ /* * XXX this used not to look at CS_RTS_IFLOW. The * change is to allow full control of MCR_RTS via * ioctls after turning CS_RTS_IFLOW off. Check * for races. We shouldn't allow the ioctls while * CS_RTS_IFLOW is on. */ if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_TBLOCK)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); enable_intr(); com->ibuf = ibuf; } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; disable_intr(); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); if (delta_modem_status & MSR_DCD) (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } if (com->state & CS_ODONE) { comflush(com); /* XXX - why isn't the table used for t_line == 0? */ if (tp->t_line != 0) (*linesw[tp->t_line].l_start)(tp); else comstart(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) continue; if (((com->state & CS_RTS_IFLOW) || (tp->t_iflag & IXOFF)) && !(tp->t_state & TS_TBLOCK) && tp->t_rawq.c_cc + incc >= RB_I_HIGH_WATER /* * XXX - need flow control for all line disciplines. * Only have it in standard one now. */ && linesw[tp->t_line].l_rint == ttyinput) { if ((tp->t_iflag & IXOFF) && tp->t_cc[VSTOP] != _POSIX_VDISABLE && putc(tp->t_cc[VSTOP], &tp->t_outq) == 0 || (com->state & CS_RTS_IFLOW)) { tp->t_state |= TS_TBLOCK; ttstart(tp); } } /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ if (!(tp->t_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXOFF | IXON)) && !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG | PENDIN)) && !(tp->t_state & (TS_CNTTB | TS_LNCH)) && linesw[tp->t_line].l_rint == ttyinput) { tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; ttstart(tp); } } else { do { u_char line_status; int recv_data; line_status = (u_char) buf[CE_INPUT_OFFSET]; recv_data = (u_char) *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } (*linesw[tp->t_line].l_rint)(recv_data, tp); } while (--incc > 0); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { u_int cfcr; int cflag; struct com_s *com; int divisor; int error; Port_t iobase; int s; int unit; /* check requested parameters */ divisor = ttspeedtab(t->c_ospeed, comspeedtab); if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; s = spltty(); if (divisor == 0) commctl(com, MCR_DTR, DMBIC); /* hang up line */ else commctl(com, MCR_DTR, DMBIS); cflag = t->c_cflag; switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; default: cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if (!(cflag & PARODD)) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; if (com->hasfifo) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and * the total latency is less than about 30 msec. These * latencies are reasonable for humans. Serial comms * protocols shouldn't expect anything better since modem * latencies are larger. */ com->ftl = t->c_ospeed <= 4800 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_14; if (com->ftl > com->ftl_max) com->ftl = com->ftl_max; outb(iobase + com_fifo, FIFO_ENABLE | com->ftl); } /* * Some UARTs lock up if the divisor latch registers are selected * while the UART is doing output (they refuse to transmit anything * more until given a hard reset). Fix this by stopping filling * the device buffers and waiting for them to drain. Reading the * line status port outside of siointr1() might lose some receiver * error bits, but that is acceptable here. */ disable_intr(); retry: com->state &= ~CS_TTGO; enable_intr(); while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) { error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH, "siotx", hz / 100); if (error != 0 && error != EAGAIN) { if (!(tp->t_state & TS_TTSTOP)) { disable_intr(); com->state |= CS_TTGO; enable_intr(); } splx(s); return (error); } } disable_intr(); /* very important while com_data is hidden */ /* * XXX - clearing CS_TTGO is not sufficient to stop further output, * because siopoll() calls comstart() which usually sets it again * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be * sufficient, for similar reasons. */ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) goto retry; if (divisor != 0) { outb(iobase + com_cfcr, cfcr | CFCR_DLAB); outb(iobase + com_dlbl, divisor & 0xFF); outb(iobase + com_dlbh, (u_int) divisor >> 8); } outb(iobase + com_cfcr, com->cfcr_image = cfcr); if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */ else com->state &= ~CS_RTS_IFLOW; /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state &= ~CS_CTS_OFLOW; com->state |= CS_ODEVREADY; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; } /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); enable_intr(); splx(s); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); s = spltty(); disable_intr(); if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; if (tp->t_state & TS_TBLOCK) { if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } else { /* * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it * appropriately in comparam() if RTS-flow is being changed. * Check for races. */ if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) goto out; #if 0 /* XXX TK2.0 */ if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) ttwwakeup(tp); #else if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } selwakeup(&tp->t_wsel); } #endif if (tp->t_state & TS_BUSY) { disable_intr(); siointr1(com); enable_intr(); } else if (tp->t_outq.c_cc != 0) { u_int ocount; tp->t_state |= TS_BUSY; ocount = q_to_b(&tp->t_outq, com->obuf, sizeof com->obuf); disable_intr(); com->obufend = (com->optr = com->obuf) + ocount; com->state |= CS_BUSY; siointr1(com); /* fake interrupt to start output */ enable_intr(); } out: splx(s); } void siostop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (rw & FWRITE) comflush(com); disable_intr(); if (rw & FREAD) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; enable_intr(); } struct tty * siodevtotty(dev) dev_t dev; { int mynor; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO) return (NULL); return (&sio_tty[unit]); -} - -int -sioselect(dev, rw, p) - dev_t dev; - int rw; - struct proc *p; -{ - return (ttyselect(siodevtotty(dev), rw, p)); } static void commctl(com, bits, how) struct com_s *com; int bits; int how; { disable_intr(); switch (how) { case DMSET: outb(com->modem_ctl_port, com->mcr_image = bits | (com->mcr_image & MCR_IENABLE)); break; case DMBIS: outb(com->modem_ctl_port, com->mcr_image |= bits); break; case DMBIC: outb(com->modem_ctl_port, com->mcr_image &= ~bits); break; } enable_intr(); } static void comwakeup(chan) void *chan; { struct com_s *com; static int log_countdown = 1; int unit; timeout(comwakeup, (caddr_t)NULL, hz > 200 ? hz / 200 : 1); if (com_events != 0) { int s; s = splsofttty(); siopoll(); splx(s); } /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); } } /* * Check for and log errors, but not too often. */ if (--log_countdown > 0) return; log_countdown = hz > 200 ? 200 : hz; for (unit = 0; unit < NSIO; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; enable_intr(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); #if 0 /* * XXX if we resurrect this then we should move * the dropping of the ftl to somewhere with less * latency. */ if (errnum == CE_OVERRUN && com->hasfifo && com->ftl > FIFO_TRIGGER_1) { static u_char ftl_in_bytes[] = { 1, 4, 8, 14, }; com->ftl_init = FIFO_TRIGGER_8; #define FIFO_TRIGGER_DELTA FIFO_TRIGGER_4 com->ftl_max = com->ftl -= FIFO_TRIGGER_DELTA; outb(com->iobase + com_fifo, FIFO_ENABLE | com->ftl); log(LOG_DEBUG, "sio%d: reduced fifo trigger level to %d\n", unit, ftl_in_bytes[com->ftl / FIFO_TRIGGER_DELTA]); } #endif } } } /* * Following are all routines needed for SIO to act as console */ #include "i386/i386/cons.h" struct siocnstate { u_char dlbl; u_char dlbh; u_char ier; u_char cfcr; u_char mcr; }; static Port_t siocniobase; static void siocnclose __P((struct siocnstate *sp)); static void siocnopen __P((struct siocnstate *sp)); static void siocntxwait __P((void)); static void siocntxwait() { int timo; /* * Wait for any pending transmission to finish. Required to avoid * the UART lockup bug when the speed is changed, and for normal * transmits. */ timo = 100000; while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY) && --timo != 0) ; } static void siocnopen(sp) struct siocnstate *sp; { int divisor; Port_t iobase; /* * Save all the device control registers except the fifo register * and set our default ones (cs8 -parenb speed=comdefaultrate). * We can't save the fifo register since it is read-only. */ iobase = siocniobase; sp->ier = inb(iobase + com_ier); outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ siocntxwait(); sp->cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB); sp->dlbl = inb(iobase + com_dlbl); sp->dlbh = inb(iobase + com_dlbh); divisor = ttspeedtab(comdefaultrate, comspeedtab); outb(iobase + com_dlbl, divisor & 0xFF); outb(iobase + com_dlbh, (u_int) divisor >> 8); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); /* * We don't want interrupts, but must be careful not to "disable" * them by clearing the MCR_IENABLE bit, since that might cause * an interrupt by floating the IRQ line. */ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void siocnclose(sp) struct siocnstate *sp; { Port_t iobase; /* * Restore the device control registers. */ siocntxwait(); iobase = siocniobase; outb(iobase + com_cfcr, CFCR_DLAB); outb(iobase + com_dlbl, sp->dlbl); outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); } void siocnprobe(cp) struct consdev *cp; { int unit; /* locate the major number */ /* XXX - should be elsewhere since KGDB uses it */ for (commajor = 0; commajor < nchrdev; commajor++) if (cdevsw[commajor].d_open == sioopen) break; /* XXX: ick */ unit = DEV_TO_UNIT(CONUNIT); siocniobase = CONADDR; /* make sure hardware exists? XXX */ /* initialize required fields */ cp->cn_dev = makedev(commajor, unit); if (COMCONSOLE || boothowto & RB_SERIAL) cp->cn_pri = CN_REMOTE; /* Force a serial port console */ else cp->cn_pri = CN_NORMAL; } void siocninit(cp) struct consdev *cp; { /* * XXX can delete more comconsole stuff now that i/o routines are * fairly reentrant. */ comconsole = DEV_TO_UNIT(cp->cn_dev); } int siocncheckc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); if (inb(iobase + com_lsr) & LSR_RXRDY) c = inb(iobase + com_data); else c = 0; siocnclose(&sp); splx(s); return (c); } int siocngetc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp); splx(s); return (c); } void siocnputc(dev, c) dev_t dev; int c; { int s; struct siocnstate sp; s = spltty(); siocnopen(&sp); siocntxwait(); outb(siocniobase + com_data, c); siocnclose(&sp); splx(s); } #ifdef DSI_SOFT_MODEM /* * The magic code to download microcode to a "Connection 14.4+Fax" * modem from Digicom Systems Inc. Very magic. */ #define DSI_ERROR(str) { ptr = str; goto error; } static int LoadSoftModem(int unit, int base_io, u_long size, u_char *ptr) { int int_c,int_k; int data_0188, data_0187; /* * First see if it is a DSI SoftModem */ if(!((inb(base_io+7) ^ inb(base_io+7) & 0x80))) return ENODEV; data_0188 = inb(base_io+4); data_0187 = inb(base_io+3); outb(base_io+3,0x80); outb(base_io+4,0x0C); outb(base_io+0,0x31); outb(base_io+1,0x8C); outb(base_io+7,0x10); outb(base_io+7,0x19); if(0x18 != (inb(base_io+7) & 0x1A)) DSI_ERROR("dsp bus not granted"); if(0x01 != (inb(base_io+7) & 0x01)) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x01 != (inb(base_io+7) & 0x01)) DSI_ERROR("program mem not granted"); } int_c = 0; while(1) { if(int_c >= 7 || size <= 0x1800) break; for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; int_c++; } if(size > 0x1800) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; while(size > 0x1800) { for(int_k = 0 ; int_k < 0xC00; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; } if(size < 0x1800) { for(int_k=0;int_k 0) { if(int_c == 7) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } else { for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } } outb(base_io+7,0x11); outb(base_io+7,3); outb(base_io+4,data_0188 & 0xfb); outb(base_io+3,data_0187); return 0; error: printf("sio%d: DSI SoftModem microcode load failed: <%s>\n",ptr); outb(base_io+7,0x00); \ outb(base_io+3,data_0187); \ outb(base_io+4,data_0188); \ return EIO; } #endif /* DSI_SOFT_MODEM */ #endif /* NSIO > 0 */ Index: head/sys/dev/syscons/syscons.c =================================================================== --- head/sys/dev/syscons/syscons.c (revision 6781) +++ head/sys/dev/syscons/syscons.c (revision 6782) @@ -1,2978 +1,2967 @@ /*- * Copyright (c) 1992-1995 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software withough specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: syscons.c,v 1.104 1995/02/22 13:40:19 sos Exp $ + * $Id: syscons.c,v 1.105 1995/02/25 20:09:16 pst Exp $ */ #include "sc.h" #include "apm.h" #if NSC > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(MAXCONS) #define MAXCONS 16 #endif /* this may break on older VGA's but is usefull on real 32 bit systems */ #define bcopyw bcopy static default_attr user_default = { (FG_LIGHTGREY | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; static default_attr kernel_default = { (FG_WHITE | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; static scr_stat main_console; static scr_stat *console[MAXCONS]; scr_stat *cur_console; static scr_stat *new_scp, *old_scp; static term_stat kernel_console; static default_attr *current_default; static char init_done = FALSE; static char switch_in_progress = FALSE; static char blink_in_progress = FALSE; static char write_in_progress = FALSE; u_int crtc_addr = MONO_BASE; static char crtc_vga = FALSE; static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0, metas = 0; static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0; static char *font_8 = NULL, *font_14 = NULL, *font_16 = NULL; static int fonts_loaded = 0; char palette[3*256]; static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab); #if ASYNCH static u_char kbd_reply = 0; #endif static int delayed_next_scr = FALSE; static int configuration = 0; /* current setup */ static long scrn_blank_time = 0; /* screen saver timeout value */ int scrn_blanked = FALSE; /* screen saver active flag */ static int scrn_saver = 0; /* screen saver routine */ static long scrn_time_stamp; u_char scr_map[256]; static char *video_mode_ptr = NULL; static u_short mouse_and_mask[16] = { 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80, 0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000 }; static u_short mouse_or_mask[16] = { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800, 0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000 }; void none_saver(int blank) { } void (*current_saver)() = none_saver; /* OS specific stuff */ #ifdef not_yet_done #define VIRTUAL_TTY(x) (sccons[x] = ttymalloc(sccons[x])) struct CONSOLE_TTY (sccons[MAXCONS] = ttymalloc(sccons[MAXCONS])) struct tty *sccons[MAXCONS+1]; #else #define VIRTUAL_TTY(x) &sccons[x] #define CONSOLE_TTY &sccons[MAXCONS] struct tty sccons[MAXCONS+1]; #endif #define MONO_BUF pa_to_va(0xB0000) #define CGA_BUF pa_to_va(0xB8000) u_short *Crtat = (u_short *)MONO_BUF; #define WRAPHIST(scp, pointer, offset)\ ((scp->history) + ((((pointer) - (scp->history)) + (scp->history_size)\ + (offset)) % (scp->history_size))) struct isa_driver scdriver = { scprobe, scattach, "sc", 1 }; int scprobe(struct isa_device *dev) { int i, retries = 5; unsigned char val; /* Enable interrupts and keyboard controller */ kbd_wait(); outb(KB_STAT, KB_WRITE); kbd_wait(); outb(KB_DATA, KB_MODE); /* flush any noise in the buffer */ while (inb(KB_STAT) & KB_BUF_FULL) { DELAY(10); (void) inb(KB_DATA); } /* Reset keyboard hardware */ while (retries--) { kbd_wait(); outb(KB_DATA, KB_RESET); for (i=0; i<100000; i++) { DELAY(10); val = inb(KB_DATA); if (val == KB_ACK || val == KB_ECHO) goto gotres; if (val == KB_RESEND) break; } } gotres: if (!retries) printf("scprobe: keyboard won't accept RESET command\n"); else { gotack: DELAY(10); while ((inb(KB_STAT) & KB_BUF_FULL) == 0) DELAY(10); DELAY(10); val = inb(KB_DATA); if (val == KB_ACK) goto gotack; if (val != KB_RESET_DONE) printf("scprobe: keyboard RESET failed %02x\n", val); } #ifdef XT_KEYBOARD kbd_wait(); outb(KB_DATA, 0xF0); kbd_wait(); outb(KB_DATA, 1) kbd_wait(); #endif /* XT_KEYBOARD */ return (IO_KBDSIZE); } static struct kern_devconf kdc_sc[NSC] = { 0, 0, 0, /* filled in by dev_attach */ "sc", 0, { MDDT_ISA, 0, "tty" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ 0, /* parentdata */ DC_BUSY, /* the console is almost always busy */ "Graphics console" }; static inline void sc_registerdev(struct isa_device *id) { if(id->id_unit) kdc_sc[id->id_unit] = kdc_sc[0]; kdc_sc[id->id_unit].kdc_unit = id->id_unit; kdc_sc[id->id_unit].kdc_isa = id; dev_attach(&kdc_sc[id->id_unit]); } #if NAPM > 0 static int scresume(void *dummy) { shfts = 0; ctls = 0; alts = 0; agrs = 0; metas = 0; return 0; } #endif int scattach(struct isa_device *dev) { scr_stat *scp; scinit(); configuration = dev->id_flags; printf("sc%d: ", dev->id_unit); if (crtc_vga) if (crtc_addr == MONO_BASE) printf("VGA mono"); else printf("VGA color"); else if (crtc_addr == MONO_BASE) printf("MDA/hercules"); else printf("CGA/EGA"); printf(" <%d virtual consoles, flags=0x%x>\n", MAXCONS, configuration); scp = console[0]; scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_NOWAIT); /* copy screen to buffer */ bcopyw(Crtat, scp->scr_buf, scp->xsize * scp->ysize * sizeof(u_short)); scp->cursor_pos = scp->scr_buf + scp->xpos + scp->ypos * scp->xsize; scp->mouse_pos = scp->scr_buf; /* initialize history buffer & pointers */ scp->history_head = scp->history_pos = scp->history = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_NOWAIT); bzero(scp->history_head, scp->history_size*sizeof(u_short)); if (crtc_vga) { font_8 = (char *)malloc(8*256, M_DEVBUF, M_NOWAIT); font_14 = (char *)malloc(14*256, M_DEVBUF, M_NOWAIT); font_16 = (char *)malloc(16*256, M_DEVBUF, M_NOWAIT); copy_font(SAVE, FONT_16, font_16); fonts_loaded = FONT_16; scp->font = FONT_16; save_palette(); } /* get screen update going */ scrn_timer(); update_leds(scp->status); sc_registerdev(dev); #if NAPM > 0 scp->r_hook.ah_fun = scresume; scp->r_hook.ah_arg = NULL; scp->r_hook.ah_name = "system keyboard"; scp->r_hook.ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_RESUME , &scp->r_hook); #endif return 0; } struct tty *scdevtotty(dev_t dev) { int unit = minor(dev); if (unit > MAXCONS || unit < 0) return(NULL); if (unit == MAXCONS) return CONSOLE_TTY; return VIRTUAL_TTY(unit); -} - -int -scselect(dev_t dev, int rw, struct proc *p) -{ - struct tty *tp = scdevtotty(dev); - - if (tp == NULL) - return(ENXIO); - - return (ttyselect(tp, rw, p)); } static scr_stat *get_scr_stat(dev_t dev) { int unit = minor(dev); if (unit > MAXCONS || unit < 0) return(NULL); if (unit == MAXCONS) return console[0]; return console[unit]; } static int get_scr_num() { int i = 0; while ((i < MAXCONS) && (cur_console != console[i])) i++; return i < MAXCONS ? i : 0; } int scopen(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); tp->t_oproc = scstart; tp->t_param = scparam; tp->t_dev = dev; if (!(tp->t_state & TS_ISOPEN)) { ttychars(tp); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; scparam(tp, &tp->t_termios); ttsetwater(tp); } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) return(EBUSY); tp->t_state |= TS_CARR_ON; tp->t_cflag |= CLOCAL; if (!console[minor(dev)]) console[minor(dev)] = alloc_scp(); return((*linesw[tp->t_line].l_open)(dev, tp)); } int scclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); struct scr_stat *scp; if (!tp) return(ENXIO); if (minor(dev) < MAXCONS) { scp = get_scr_stat(tp->t_dev); if (scp->status & SWITCH_WAIT_ACQ) wakeup((caddr_t)&scp->smode); #if not_yet_done if (scp == &main_console) { scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; } else { free(scp->scr_buf, M_DEVBUF); free(scp->history, M_DEVBUF); free(scp, M_DEVBUF); console[minor(dev)] = NULL; } #else scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; #endif } (*linesw[tp->t_line].l_close)(tp, flag); ttyclose(tp); return(0); } int scread(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); return((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int scwrite(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); return((*linesw[tp->t_line].l_write)(tp, uio, flag)); } void scintr(int unit) { static struct tty *cur_tty; int c, len; u_char *cp; /* make screensaver happy */ scrn_time_stamp = time.tv_sec; if (scrn_blanked) { (*current_saver)(FALSE); cur_console->status |= UPDATE_SCREEN; } c = scgetc(1); cur_tty = VIRTUAL_TTY(get_scr_num()); if (!(cur_tty->t_state & TS_ISOPEN)) if (!((cur_tty = CONSOLE_TTY)->t_state & TS_ISOPEN)) return; switch (c & 0xff00) { case 0x0000: /* normal key */ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); break; case NOKEY: /* nothing there */ break; case FKEY: /* function key, return string */ if (cp = get_fstr((u_int)c, (u_int *)&len)) { while (len-- > 0) (*linesw[cur_tty->t_line].l_rint)(*cp++ & 0xFF, cur_tty); } break; case MKEY: /* meta is active, prepend ESC */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); break; case BKEY: /* backtab fixed sequence (esc [ Z) */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)('[', cur_tty); (*linesw[cur_tty->t_line].l_rint)('Z', cur_tty); break; } } int scparam(struct tty *tp, struct termios *t) { tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; return 0; } int scioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { int i, error; struct tty *tp; struct trapframe *fp; scr_stat *scp; tp = scdevtotty(dev); if (!tp) return ENXIO; scp = get_scr_stat(tp->t_dev); switch (cmd) { /* process console hardware related ioctl's */ case GIO_ATTR: /* get current attributes */ *(int*)data = scp->term.cur_attr; return 0; case GIO_COLOR: /* is this a color console ? */ if (crtc_addr == COLOR_BASE) *(int*)data = 1; else *(int*)data = 0; return 0; case CONS_CURRENT: /* get current adapter type */ if (crtc_vga) *(int*)data = KD_VGA; else if (crtc_addr == MONO_BASE) *(int*)data = KD_MONO; else *(int*)data = KD_CGA; return 0; case CONS_GET: /* get current video mode */ *(int*)data = scp->mode; return 0; case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */ scrn_blank_time = *(int*)data; return 0; case CONS_CURSORTYPE: /* set cursor type blink/noblink */ if ((*(int*)data) & 0x01) configuration |= BLINK_CURSOR; else configuration &= ~BLINK_CURSOR; if ((*(int*)data) & 0x02) configuration |= CHAR_CURSOR; else configuration &= ~CHAR_CURSOR; return 0; case CONS_BELLTYPE: /* set bell type sound/visual */ if (*data) configuration |= VISUAL_BELL; else configuration &= ~VISUAL_BELL; return 0; case CONS_HISTORY: /* set history size */ if (*data) { free(scp->history, M_DEVBUF); scp->history_size = *(int*)data; if (scp->history_size < scp->ysize) scp->history = NULL; else { scp->history_size *= scp->xsize; scp->history_head = scp->history_pos = scp->history = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_NOWAIT); bzero(scp->history_head, scp->history_size*sizeof(u_short)); } return 0; } else return EINVAL; case CONS_MOUSECTL: /* control mouse arrow */ { mouse_info_t *mouse = (mouse_info_t*)data; int fontsize; switch (scp->font) { default: case FONT_8: fontsize = 8; break; case FONT_14: fontsize = 14; break; case FONT_16: fontsize = 16; break; } switch (mouse->operation) { case MOUSE_SHOW: if (!(scp->status & MOUSE_ENABLED)) { scp->mouse_oldpos = Crtat + (scp->mouse_pos - scp->scr_buf); scp->status |= (UPDATE_MOUSE | MOUSE_ENABLED); } else return EINVAL; break; case MOUSE_HIDE: if (scp->status & MOUSE_ENABLED) { scp->status &= ~MOUSE_ENABLED; scp->status |= UPDATE_MOUSE; } else return EINVAL; break; case MOUSE_MOVEABS: scp->mouse_xpos = mouse->x; scp->mouse_ypos = mouse->y; goto set_mouse_pos; case MOUSE_MOVEREL: scp->mouse_xpos += mouse->x; scp->mouse_ypos += mouse->y; set_mouse_pos: if (scp->mouse_xpos < 0) scp->mouse_xpos = 0; if (scp->mouse_ypos < 0) scp->mouse_ypos = 0; if (scp->mouse_xpos >= scp->xsize*8) scp->mouse_xpos = (scp->xsize*8)-1; if (scp->mouse_ypos >= scp->ysize*fontsize) scp->mouse_ypos = (scp->ysize*fontsize)-1; scp->mouse_pos = scp->scr_buf + (scp->mouse_ypos/fontsize)*scp->xsize + scp->mouse_xpos/8; if (scp->status & MOUSE_ENABLED) scp->status |= UPDATE_MOUSE; break; case MOUSE_GETPOS: mouse->x = scp->mouse_xpos; mouse->y = scp->mouse_ypos; return 0; default: return EINVAL; } /* make screensaver happy */ if (scp == cur_console) { scrn_time_stamp = time.tv_sec; if (scrn_blanked) { (*current_saver)(FALSE); scp->status |= UPDATE_SCREEN; } } return 0; } case CONS_GETINFO: /* get current (virtual) console info */ { vid_info_t *ptr = (vid_info_t*)data; if (ptr->size == sizeof(struct vid_info)) { ptr->m_num = get_scr_num(); ptr->mv_col = scp->xpos; ptr->mv_row = scp->ypos; ptr->mv_csz = scp->xsize; ptr->mv_rsz = scp->ysize; ptr->mv_norm.fore = (scp->term.std_attr & 0x0f00)>>8; ptr->mv_norm.back = (scp->term.std_attr & 0xf000)>>12; ptr->mv_rev.fore = (scp->term.rev_attr & 0x0f00)>>8; ptr->mv_rev.back = (scp->term.rev_attr & 0xf000)>>12; ptr->mv_grfc.fore = 0; /* not supported */ ptr->mv_grfc.back = 0; /* not supported */ ptr->mv_ovscan = scp->border; ptr->mk_keylock = scp->status & LOCK_KEY_MASK; return 0; } return EINVAL; } case CONS_GETVERS: /* get version number */ *(int*)data = 0x200; /* version 2.0 */ return 0; /* VGA TEXT MODES */ case SW_VGA_C40x25: case SW_VGA_C80x25: case SW_VGA_M80x25: case SW_VGA_C80x30: case SW_VGA_M80x30: case SW_VGA_C80x50: case SW_VGA_M80x50: case SW_VGA_C80x60: case SW_VGA_M80x60: case SW_B40x25: case SW_C40x25: case SW_B80x25: case SW_C80x25: case SW_ENH_B40x25: case SW_ENH_C40x25: case SW_ENH_B80x25: case SW_ENH_C80x25: case SW_ENH_B80x43: case SW_ENH_C80x43: if (!crtc_vga || video_mode_ptr == NULL) return ENXIO; switch (cmd & 0xff) { case M_VGA_C80x60: case M_VGA_M80x60: if (!(fonts_loaded & FONT_8)) return EINVAL; scp->xsize = 80; scp->ysize = 60; break; case M_VGA_C80x50: case M_VGA_M80x50: if (!(fonts_loaded & FONT_8)) return EINVAL; scp->xsize = 80; scp->ysize = 50; break; case M_ENH_B80x43: case M_ENH_C80x43: if (!(fonts_loaded & FONT_8)) return EINVAL; scp->xsize = 80; scp->ysize = 43; break; case M_VGA_C80x30: case M_VGA_M80x30: scp->xsize = 80; scp->ysize = 30; break; default: if ((cmd & 0xff) > M_VGA_CG320) return EINVAL; else scp->xsize = *(video_mode_ptr+((cmd&0xff)*64)); scp->ysize = *(video_mode_ptr+((cmd&0xff)*64)+1)+1; break; } scp->mode = cmd & 0xff; scp->status &= ~UNKNOWN_MODE; /* text mode */ free(scp->scr_buf, M_DEVBUF); scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_NOWAIT); if (scp == cur_console) set_mode(scp); clear_screen(scp); if (tp->t_winsize.ws_col != scp->xsize || tp->t_winsize.ws_row != scp->ysize) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; /* GRAPHICS MODES */ case SW_BG320: case SW_BG640: case SW_CG320: case SW_CG320_D: case SW_CG640_E: case SW_CG640x350: case SW_ENH_CG640: case SW_BG640x480: case SW_CG640x480: case SW_VGA_CG320: if (!crtc_vga || video_mode_ptr == NULL) return ENXIO; scp->mode = cmd & 0xFF; scp->status |= UNKNOWN_MODE; /* graphics mode */ scp->xsize = (*(video_mode_ptr + (scp->mode*64))) * 8; scp->ysize = (*(video_mode_ptr + (scp->mode*64) + 1) + 1) * (*(video_mode_ptr + (scp->mode*64) + 2)); set_mode(scp); /* clear_graphics();*/ if (tp->t_winsize.ws_xpixel != scp->xsize || tp->t_winsize.ws_ypixel != scp->ysize) { tp->t_winsize.ws_xpixel = scp->xsize; tp->t_winsize.ws_ypixel = scp->ysize; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; case VT_SETMODE: /* set screen switcher mode */ bcopy(data, &scp->smode, sizeof(struct vt_mode)); if (scp->smode.mode == VT_PROCESS) { scp->proc = p; scp->pid = scp->proc->p_pid; } return 0; case VT_GETMODE: /* get screen switcher mode */ bcopy(&scp->smode, data, sizeof(struct vt_mode)); return 0; case VT_RELDISP: /* screen switcher ioctl */ switch(*data) { case VT_FALSE: /* user refuses to release screen, abort */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { old_scp->status &= ~SWITCH_WAIT_REL; switch_in_progress = FALSE; return 0; } return EINVAL; case VT_TRUE: /* user has released screen, go on */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { scp->status &= ~SWITCH_WAIT_REL; exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; return 0; } return EINVAL; case VT_ACKACQ: /* acquire acknowledged, switch completed */ if (scp == new_scp && (scp->status & SWITCH_WAIT_ACQ)) { scp->status &= ~SWITCH_WAIT_ACQ; switch_in_progress = FALSE; return 0; } return EINVAL; default: return EINVAL; } /* NOT REACHED */ case VT_OPENQRY: /* return free virtual console */ for (i = 0; i < MAXCONS; i++) { tp = VIRTUAL_TTY(i); if (!(tp->t_state & TS_ISOPEN)) { *data = i + 1; return 0; } } return EINVAL; case VT_ACTIVATE: /* switch to screen *data */ return switch_scr(scp, (*data) - 1); case VT_WAITACTIVE: /* wait for switch to occur */ if (*data > MAXCONS || *data < 0) return EINVAL; if (minor(dev) == (*data) - 1) return 0; if (*data == 0) { if (scp == cur_console) return 0; } else scp = console[(*data) - 1]; while ((error=tsleep((caddr_t)&scp->smode, PZERO|PCATCH, "waitvt", 0)) == ERESTART) ; return error; case VT_GETACTIVE: *data = get_scr_num()+1; return 0; case KDENABIO: /* allow io operations */ fp = (struct trapframe *)p->p_md.md_regs; fp->tf_eflags |= PSL_IOPL; return 0; case KDDISABIO: /* disallow io operations (default) */ fp = (struct trapframe *)p->p_md.md_regs; fp->tf_eflags &= ~PSL_IOPL; return 0; case KDSETMODE: /* set current mode of this (virtual) console */ switch (*data) { case KD_TEXT: /* switch to TEXT (known) mode */ /* restore fonts & palette ! */ if (crtc_vga) { if (fonts_loaded & FONT_8) copy_font(LOAD, FONT_8, font_8); if (fonts_loaded & FONT_14) copy_font(LOAD, FONT_14, font_14); if (fonts_loaded & FONT_16) copy_font(LOAD, FONT_16, font_16); load_palette(); } /* FALL THROUGH */ case KD_TEXT1: /* switch to TEXT (known) mode */ /* no restore fonts & palette */ scp->status &= ~UNKNOWN_MODE; if (crtc_vga && video_mode_ptr) set_mode(scp); clear_screen(scp); return 0; case KD_GRAPHICS: /* switch to GRAPHICS (unknown) mode */ scp->status |= UNKNOWN_MODE; return 0; default: return EINVAL; } /* NOT REACHED */ case KDGETMODE: /* get current mode of this (virtual) console */ *data = (scp->status & UNKNOWN_MODE) ? KD_GRAPHICS : KD_TEXT; return 0; case KDSBORDER: /* set border color of this (virtual) console */ if (!crtc_vga) return ENXIO; scp->border = *data; if (scp == cur_console) set_border(scp->border); return 0; case KDSKBSTATE: /* set keyboard state (locks) */ if (*data >= 0 && *data <= LOCK_KEY_MASK) { scp->status &= ~LOCK_KEY_MASK; scp->status |= *data; if (scp == cur_console) update_leds(scp->status); return 0; } return EINVAL; case KDGKBSTATE: /* get keyboard state (locks) */ *data = scp->status & LOCK_KEY_MASK; return 0; case KDSETRAD: /* set keyboard repeat & delay rates */ if (*data & 0x80) return EINVAL; i = spltty(); kbd_cmd(KB_SETRAD); kbd_cmd(*data); splx(i); return 0; case KDSKBMODE: /* set keyboard mode */ switch (*data) { case K_RAW: /* switch to RAW scancode mode */ scp->status |= KBD_RAW_MODE; return 0; case K_XLATE: /* switch to XLT ascii mode */ if (scp == cur_console && scp->status == KBD_RAW_MODE) shfts = ctls = alts = agrs = metas = 0; scp->status &= ~KBD_RAW_MODE; return 0; default: return EINVAL; } /* NOT REACHED */ case KDGKBMODE: /* get keyboard mode */ *data = (scp->status & KBD_RAW_MODE) ? K_RAW : K_XLATE; return 0; case KDMKTONE: /* sound the bell */ if (*(int*)data) do_bell(scp, (*(int*)data)&0xffff, (((*(int*)data)>>16)&0xffff)*hz/1000); else do_bell(scp, scp->bell_pitch, scp->bell_duration); return 0; case KIOCSOUND: /* make tone (*data) hz */ if (scp == cur_console) { if (*(int*)data) { int pitch = TIMER_FREQ/(*(int*)data); /* set command for counter 2, 2 byte write */ if (acquire_timer2(TIMER_16BIT|TIMER_SQWAVE)) return EBUSY; /* set pitch */ outb(TIMER_CNTR2, pitch); outb(TIMER_CNTR2, (pitch>>8)); /* enable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) | 3); } else { /* disable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) & 0xFC); release_timer2(); } } return 0; case KDGKBTYPE: /* get keyboard type */ *data = 0; /* type not known (yet) */ return 0; case KDSETLED: /* set keyboard LED status */ if (*data >= 0 && *data <= LED_MASK) { scp->status &= ~LED_MASK; scp->status |= *data; if (scp == cur_console) update_leds(scp->status); return 0; } return EINVAL; case KDGETLED: /* get keyboard LED status */ *data = scp->status & LED_MASK; return 0; case GETFKEY: /* get functionkey string */ if (*(u_short*)data < n_fkey_tab) { fkeyarg_t *ptr = (fkeyarg_t*)data; bcopy(&fkey_tab[ptr->keynum].str, ptr->keydef, fkey_tab[ptr->keynum].len); ptr->flen = fkey_tab[ptr->keynum].len; return 0; } else return EINVAL; case SETFKEY: /* set functionkey string */ if (*(u_short*)data < n_fkey_tab) { fkeyarg_t *ptr = (fkeyarg_t*)data; bcopy(ptr->keydef, &fkey_tab[ptr->keynum].str, min(ptr->flen, MAXFK)); fkey_tab[ptr->keynum].len = min(ptr->flen, MAXFK); return 0; } else return EINVAL; case GIO_SCRNMAP: /* get output translation table */ bcopy(&scr_map, data, sizeof(scr_map)); return 0; case PIO_SCRNMAP: /* set output translation table */ bcopy(data, &scr_map, sizeof(scr_map)); return 0; case GIO_KEYMAP: /* get keyboard translation table */ bcopy(&key_map, data, sizeof(key_map)); return 0; case PIO_KEYMAP: /* set keyboard translation table */ bcopy(data, &key_map, sizeof(key_map)); return 0; case PIO_FONT8x8: /* set 8x8 dot font */ if (!crtc_vga) return ENXIO; bcopy(data, font_8, 8*256); fonts_loaded |= FONT_8; copy_font(LOAD, FONT_8, font_8); return 0; case GIO_FONT8x8: /* get 8x8 dot font */ if (!crtc_vga) return ENXIO; if (fonts_loaded & FONT_8) { bcopy(font_8, data, 8*256); return 0; } else return ENXIO; case PIO_FONT8x14: /* set 8x14 dot font */ if (!crtc_vga) return ENXIO; bcopy(data, font_14, 14*256); fonts_loaded |= FONT_14; copy_font(LOAD, FONT_14, font_14); return 0; case GIO_FONT8x14: /* get 8x14 dot font */ if (!crtc_vga) return ENXIO; if (fonts_loaded & FONT_14) { bcopy(font_14, data, 14*256); return 0; } else return ENXIO; case PIO_FONT8x16: /* set 8x16 dot font */ if (!crtc_vga) return ENXIO; bcopy(data, font_16, 16*256); fonts_loaded |= FONT_16; copy_font(LOAD, FONT_16, font_16); return 0; case GIO_FONT8x16: /* get 8x16 dot font */ if (!crtc_vga) return ENXIO; if (fonts_loaded & FONT_16) { bcopy(font_16, data, 16*256); return 0; } else return ENXIO; default: break; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return(error); error = ttioctl(tp, cmd, data, flag); if (error >= 0) return(error); return(ENOTTY); } void scxint(dev_t dev) { struct tty *tp = scdevtotty(dev); if (!tp) return; tp->t_state &= ~TS_BUSY; if (tp->t_line) (*linesw[tp->t_line].l_start)(tp); else scstart(tp); } void scstart(struct tty *tp) { struct clist *rbp; int i, s, len; u_char buf[PCBURST]; scr_stat *scp = get_scr_stat(tp->t_dev); if (scp->status & SLKED || blink_in_progress) return; s = spltty(); if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) { tp->t_state |= TS_BUSY; splx(s); rbp = &tp->t_outq; while (rbp->c_cc) { len = q_to_b(rbp, buf, PCBURST); ansi_put(scp, buf, len); } scp->status |= UPDATE_SCREEN; s = spltty(); tp->t_state &= ~TS_BUSY; if (rbp->c_cc <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)rbp); } selwakeup(&tp->t_wsel); } } splx(s); } void pccnprobe(struct consdev *cp) { int maj; /* locate the major number */ for (maj = 0; maj < nchrdev; maj++) if ((void*)cdevsw[maj].d_open == (void*)scopen) break; /* initialize required fields */ cp->cn_dev = makedev(maj, MAXCONS); cp->cn_pri = CN_INTERNAL; } void pccninit(struct consdev *cp) { scinit(); } void pccnputc(dev_t dev, char c) { scr_stat *scp = console[0]; term_stat save = scp->term; scp->term = kernel_console; current_default = &kernel_default; if (scp->scr_buf == Crtat) draw_cursor(scp, FALSE); if (c == '\n') ansi_put(scp, "\r\n", 2); else ansi_put(scp, &c, 1); scp->status |= UPDATE_SCREEN; kernel_console = scp->term; current_default = &user_default; scp->term = save; if (scp == cur_console /* && scrn_timer not running */) { if (scp->scr_buf != Crtat) { bcopyw(scp->scr_buf, Crtat, (scp->xsize*scp->ysize)*sizeof(u_short)); scp->status &= ~CURSOR_SHOWN; } draw_cursor(scp, TRUE); scp->status &= ~UPDATE_SCREEN; } } int pccngetc(dev_t dev) { int s = spltty(); /* block scintr while we poll */ int c = scgetc(0); splx(s); return(c); } int pccncheckc(dev_t dev) { return (scgetc(1) & 0xff); } static void scrn_timer() { static int cursor_blinkrate; scr_stat *scp = cur_console; /* should we just return ? */ if ((scp->status&UNKNOWN_MODE) || blink_in_progress || switch_in_progress) { timeout((timeout_func_t)scrn_timer, 0, hz/10); return; } if (!scrn_blanked) { /* update entire screen image */ if (scp->status & UPDATE_SCREEN) { bcopyw(scp->scr_buf, Crtat, scp->xsize*scp->ysize*sizeof(u_short)); scp->status &= ~CURSOR_SHOWN; } /* update "pseudo" mouse arrow */ if ((scp->status & MOUSE_ENABLED) && ((scp->status & UPDATE_MOUSE) || (scp->status & UPDATE_SCREEN))) draw_mouse_image(scp); /* update cursor image */ if (scp->status & CURSOR_ENABLED) draw_cursor(scp, !(configuration&BLINK_CURSOR) || !(cursor_blinkrate++&0x04)); /* signal update done */ scp->status &= ~UPDATE_SCREEN; } if (scrn_blank_time && (time.tv_sec>scrn_time_stamp+scrn_blank_time)) (*current_saver)(TRUE); timeout((timeout_func_t)scrn_timer, 0, hz/25); } static void clear_screen(scr_stat *scp) { move_crsr(scp, 0, 0); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf, scp->xsize * scp->ysize); } static int switch_scr(scr_stat *scp, u_int next_scr) { if (switch_in_progress && (cur_console->proc != pfind(cur_console->pid))) switch_in_progress = FALSE; if (next_scr >= MAXCONS || switch_in_progress || (cur_console->smode.mode == VT_AUTO && cur_console->status & UNKNOWN_MODE)) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } /* is the wanted virtual console open ? */ if (next_scr) { struct tty *tp = VIRTUAL_TTY(next_scr); if (!(tp->t_state & TS_ISOPEN)) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } } /* delay switch if actively updating screen */ if (write_in_progress || blink_in_progress) { delayed_next_scr = next_scr+1; return 0; } switch_in_progress = TRUE; old_scp = cur_console; new_scp = console[next_scr]; wakeup((caddr_t)&new_scp->smode); if (new_scp == old_scp) { switch_in_progress = FALSE; delayed_next_scr = FALSE; return 0; } /* has controlling process died? */ if (old_scp->proc && (old_scp->proc != pfind(old_scp->pid))) old_scp->smode.mode = VT_AUTO; if (new_scp->proc && (new_scp->proc != pfind(new_scp->pid))) new_scp->smode.mode = VT_AUTO; /* check the modes and switch approbiatly */ if (old_scp->smode.mode == VT_PROCESS) { old_scp->status |= SWITCH_WAIT_REL; psignal(old_scp->proc, old_scp->smode.relsig); } else { exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; } return 0; } static void exchange_scr(void) { move_crsr(old_scp, old_scp->xpos, old_scp->ypos); cur_console = new_scp; if (old_scp->mode != new_scp->mode || (old_scp->status & UNKNOWN_MODE)){ if (crtc_vga && video_mode_ptr) set_mode(new_scp); } move_crsr(new_scp, new_scp->xpos, new_scp->ypos); if ((old_scp->status & UNKNOWN_MODE) && crtc_vga) { if (fonts_loaded & FONT_8) copy_font(LOAD, FONT_8, font_8); if (fonts_loaded & FONT_14) copy_font(LOAD, FONT_14, font_14); if (fonts_loaded & FONT_16) copy_font(LOAD, FONT_16, font_16); load_palette(); } if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE) shfts = ctls = alts = agrs = metas = 0; update_leds(new_scp->status); delayed_next_scr = FALSE; bcopyw(new_scp->scr_buf, Crtat, (new_scp->xsize*new_scp->ysize)*sizeof(u_short)); new_scp->status &= ~CURSOR_SHOWN; } static inline void move_crsr(scr_stat *scp, int x, int y) { if (x < 0 || y < 0 || x >= scp->xsize || y >= scp->ysize) return; scp->xpos = x; scp->ypos = y; scp->cursor_pos = scp->scr_buf + scp->ypos * scp->xsize + scp->xpos; } static void scan_esc(scr_stat *scp, u_char c) { static u_char ansi_col[16] = {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15}; int i, n; u_short *src, *dst, count; if (scp->term.esc == 1) { switch (c) { case '[': /* Start ESC [ sequence */ scp->term.esc = 2; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; case 'M': /* Move cursor up 1 line, scroll if at top */ if (scp->ypos > 0) move_crsr(scp, scp->xpos, scp->ypos - 1); else { bcopyw(scp->scr_buf, scp->scr_buf + scp->xsize, (scp->ysize - 1) * scp->xsize * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf, scp->xsize); } break; #if notyet case 'Q': scp->term.esc = 4; break; #endif case 'c': /* Clear screen & home */ clear_screen(scp); break; } } else if (scp->term.esc == 2) { if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case '=': scp->term.esc = 3; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; case 'A': /* up n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos - n); break; case 'B': /* down n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'C': /* right n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'D': /* left n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos - n, scp->ypos); break; case 'E': /* cursor to start of line n lines down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos + n); break; case 'F': /* cursor to start of line n lines up */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos - n); break; case 'f': /* Cursor move */ case 'H': if (scp->term.num_param == 0) move_crsr(scp, 0, 0); else if (scp->term.num_param == 2) move_crsr(scp, scp->term.param[1] - 1, scp->term.param[0] - 1); break; case 'J': /* Clear all or part of display */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of display */ fillw(scp->term.cur_attr | scr_map[0x20], scp->cursor_pos, scp->scr_buf + scp->xsize * scp->ysize - scp->cursor_pos); break; case 1: /* clear from beginning of display to cursor */ fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf, scp->cursor_pos - scp->scr_buf); break; case 2: /* clear entire display */ clear_screen(scp); break; } break; case 'K': /* Clear all or part of line */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of line */ fillw(scp->term.cur_attr | scr_map[0x20], scp->cursor_pos, scp->xsize - scp->xpos); break; case 1: /* clear from beginning of line to cursor */ fillw(scp->term.cur_attr|scr_map[0x20], scp->cursor_pos - (scp->xsize - scp->xpos), (scp->xsize - scp->xpos) + 1); break; case 2: /* clear entire line */ fillw(scp->term.cur_attr|scr_map[0x20], scp->cursor_pos - (scp->xsize - scp->xpos), scp->xsize); break; } break; case 'L': /* Insert n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; src = scp->scr_buf + scp->ypos * scp->xsize; dst = src + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopyw(src, dst, count * scp->xsize * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], src, n * scp->xsize); break; case 'M': /* Delete n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; dst = scp->scr_buf + scp->ypos * scp->xsize; src = dst + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopyw(src, dst, count * scp->xsize * sizeof(u_short)); src = dst + count * scp->xsize; fillw(scp->term.cur_attr | scr_map[0x20], src, n * scp->xsize); break; case 'P': /* Delete n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; dst = scp->cursor_pos; src = dst + n; count = scp->xsize - (scp->xpos + n); bcopyw(src, dst, count * sizeof(u_short)); src = dst + count; fillw(scp->term.cur_attr | scr_map[0x20], src, n); break; case '@': /* Insert n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; src = scp->cursor_pos; dst = src + n; count = scp->xsize - (scp->xpos + n); bcopyw(src, dst, count * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], src, n); break; case 'S': /* scroll up n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopyw(scp->scr_buf + (scp->xsize * n), scp->scr_buf, scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - n), scp->xsize * n); break; case 'T': /* scroll down n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopyw(scp->scr_buf, scp->scr_buf + (scp->xsize * n), scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf, scp->xsize * n); break; case 'X': /* delete n characters in line */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf + scp->xpos + ((scp->xsize*scp->ypos) * sizeof(u_short)), n); break; case 'Z': /* move n tabs backwards */ n = scp->term.param[0]; if (n < 1) n = 1; if ((i = scp->xpos & 0xf8) == scp->xpos) i -= 8*n; else i -= 8*(n-1); if (i < 0) i = 0; move_crsr(scp, i, scp->ypos); break; case '`': /* move cursor to column n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, n - 1, scp->ypos); break; case 'a': /* move cursor n columns to the right */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'd': /* move cursor to row n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, n - 1); break; case 'e': /* move cursor n rows down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'm': /* change attribute */ if (scp->term.num_param == 0) { scp->term.cur_attr = scp->term.std_attr; break; } for (i = 0; i < scp->term.num_param; i++) { switch (n = scp->term.param[i]) { case 0: /* back to normal */ scp->term.cur_attr = scp->term.std_attr; break; case 1: /* highlight (bold) */ scp->term.cur_attr &= 0xFF00; scp->term.cur_attr |= 0x0800; break; case 4: /* highlight (underline) */ scp->term.cur_attr &= 0xFF00; scp->term.cur_attr |= 0x0800; break; case 5: /* blink */ scp->term.cur_attr &= 0xFF00; scp->term.cur_attr |= 0x8000; break; case 7: /* reverse video */ scp->term.cur_attr = scp->term.rev_attr; break; case 30: case 31: /* set fg color */ case 32: case 33: case 34: case 35: case 36: case 37: scp->term.cur_attr = (scp->term.cur_attr&0xF8FF) | (ansi_col[(n-30)&7]<<8); break; case 40: case 41: /* set bg color */ case 42: case 43: case 44: case 45: case 46: case 47: scp->term.cur_attr = (scp->term.cur_attr&0x8FFF) | (ansi_col[(n-40)&7]<<12); break; } } break; case 'x': if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* reset attributes */ scp->term.cur_attr = scp->term.std_attr = current_default->std_attr; scp->term.rev_attr = current_default->rev_attr; break; case 1: /* set ansi background */ scp->term.cur_attr = scp->term.std_attr = (scp->term.std_attr & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); break; case 2: /* set ansi foreground */ scp->term.cur_attr = scp->term.std_attr = (scp->term.std_attr & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); break; case 3: /* set ansi attribute directly */ scp->term.cur_attr = scp->term.std_attr = (scp->term.param[1]&0xFF)<<8; break; case 5: /* set ansi reverse video background */ scp->term.rev_attr = (scp->term.rev_attr & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); break; case 6: /* set ansi reverse video foreground */ scp->term.rev_attr = (scp->term.rev_attr & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); break; case 7: /* set ansi reverse video directly */ scp->term.rev_attr = (scp->term.param[1]&0xFF)<<8; break; } break; case 'z': /* switch to (virtual) console n */ if (scp->term.num_param == 1) switch_scr(scp, scp->term.param[0]); break; } } else if (scp->term.esc == 3) { if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case 'A': /* set display border color */ if (scp->term.num_param == 1) scp->border=scp->term.param[0] & 0xff; if (scp == cur_console) set_border(scp->border); break; case 'B': /* set bell pitch and duration */ if (scp->term.num_param == 2) { scp->bell_pitch = scp->term.param[0]; scp->bell_duration = scp->term.param[1]*10; } break; case 'C': /* set cursor type & shape */ if (scp->term.num_param == 1) { if (scp->term.param[0] & 0x01) configuration |= BLINK_CURSOR; else configuration &= ~BLINK_CURSOR; if (scp->term.param[0] & 0x02) configuration |= CHAR_CURSOR; else configuration &= ~CHAR_CURSOR; } else if (scp->term.num_param == 2) { scp->cursor_start = scp->term.param[0] & 0x1F; scp->cursor_end = scp->term.param[1] & 0x1F; } break; case 'F': /* set ansi foreground */ if (scp->term.num_param == 1) scp->term.cur_attr = scp->term.std_attr = (scp->term.std_attr & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); break; case 'G': /* set ansi background */ if (scp->term.num_param == 1) scp->term.cur_attr = scp->term.std_attr = (scp->term.std_attr & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); break; case 'H': /* set ansi reverse video foreground */ if (scp->term.num_param == 1) scp->term.rev_attr = (scp->term.rev_attr & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); break; case 'I': /* set ansi reverse video background */ if (scp->term.num_param == 1) scp->term.rev_attr = (scp->term.rev_attr & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); break; } } scp->term.esc = 0; } static inline void draw_cursor(scr_stat *scp, int show) { if (show && !(scp->status & CURSOR_SHOWN)) { u_short cursor_image = *(Crtat + (scp->cursor_pos - scp->scr_buf)); scp->cursor_saveunder = cursor_image; if (configuration & CHAR_CURSOR) cursor_image = (cursor_image & 0xff00) | '_'; else { if ((cursor_image & 0x7000) == 0x7000) { cursor_image &= 0x8fff; if(!(cursor_image & 0x0700)) cursor_image |= 0x0f00; } else { cursor_image |= 0x7000; if ((cursor_image & 0x0f00) == 0x0f00) cursor_image &= 0xf0ff; } } *(Crtat + (scp->cursor_pos - scp->scr_buf)) = cursor_image; scp->status |= CURSOR_SHOWN; } if (!show && (scp->status & CURSOR_SHOWN)) { *(Crtat+(scp->cursor_pos-scp->scr_buf)) = scp->cursor_saveunder; scp->status &= ~CURSOR_SHOWN; } } static void ansi_put(scr_stat *scp, u_char *buf, int len) { u_char *ptr = buf; if (scp->status & UNKNOWN_MODE) return; /* make screensaver happy */ if (scp == cur_console) { scrn_time_stamp = time.tv_sec; if (scrn_blanked) (*current_saver)(FALSE); } write_in_progress++; outloop: if (scp->term.esc) { scan_esc(scp, *ptr++); len--; } else if (PRINTABLE(*ptr)) { /* Print only printables */ do { *scp->cursor_pos++ = (scp->term.cur_attr | scr_map[*ptr++]); scp->xpos++; len--; } while (len && PRINTABLE(*ptr) && (scp->xpos < scp->xsize)); if (scp->xpos >= scp->xsize) { scp->xpos = 0; scp->ypos++; } } else { switch(*ptr) { case 0x07: do_bell(scp, scp->bell_pitch, scp->bell_duration); break; case 0x08: /* non-destructive backspace */ if (scp->cursor_pos > scp->scr_buf) { scp->cursor_pos--; if (scp->xpos > 0) scp->xpos--; else { scp->xpos += scp->xsize - 1; scp->ypos--; } } break; case 0x09: /* non-destructive tab */ { int i = 8 - scp->xpos % 8u; scp->cursor_pos += i; if ((scp->xpos += i) >= scp->xsize) { scp->xpos = 0; scp->ypos++; } } break; case 0x0a: /* newline, same pos */ scp->cursor_pos += scp->xsize; scp->ypos++; break; case 0x0c: /* form feed, clears screen */ clear_screen(scp); break; case 0x0d: /* return, return to pos 0 */ scp->cursor_pos -= scp->xpos; scp->xpos = 0; break; case 0x1b: /* start escape sequence */ scp->term.esc = 1; scp->term.num_param = 0; break; } ptr++; len--; } /* do we have to scroll ?? */ if (scp->cursor_pos >= scp->scr_buf + scp->ysize * scp->xsize) { if (scp->history) { bcopyw(scp->scr_buf, scp->history_head, scp->xsize * sizeof(u_short)); scp->history_head += scp->xsize; if (scp->history_head + scp->xsize > scp->history + scp->history_size) scp->history_head = scp->history; } bcopyw(scp->scr_buf + scp->xsize, scp->scr_buf, scp->xsize * (scp->ysize - 1) * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - 1), scp->xsize); scp->cursor_pos -= scp->xsize; scp->ypos--; } if (len) goto outloop; write_in_progress--; if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } static void scinit(void) { u_short volatile *cp = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short), was; unsigned hw_cursor; int i; if (init_done) return; init_done = TRUE; /* * Crtat initialized to point to MONO buffer, if not present change * to CGA_BUF offset. ONLY add the difference since locore.s adds * in the remapped offset at the "right" time */ was = *cp; *cp = (u_short) 0xA55A; if (*cp != 0xA55A) crtc_addr = MONO_BASE; else { *cp = was; crtc_addr = COLOR_BASE; Crtat = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short); } /* extract cursor location */ outb(crtc_addr,14); hw_cursor = inb(crtc_addr+1)<<8 ; outb(crtc_addr,15); hw_cursor |= inb(crtc_addr+1); /* move hardware cursor out of the way */ outb(crtc_addr,14); outb(crtc_addr+1, 0xff); outb(crtc_addr,15); outb(crtc_addr+1, 0xff); /* is this a VGA or higher ? */ outb(crtc_addr, 7); if (inb(crtc_addr) == 7) { u_long pa; u_long segoff; crtc_vga = TRUE; /* * Get the BIOS video mode pointer. */ segoff = *(u_long *)pa_to_va(0x4a8); pa = (((segoff & 0xffff0000) >> 12) + (segoff & 0xffff)); if (ISMAPPED(pa, sizeof(u_long))) { segoff = *(u_long *)pa_to_va(pa); pa = (((segoff & 0xffff0000) >> 12) + (segoff & 0xffff)); if (ISMAPPED(pa, 64)) video_mode_ptr = (char *)pa_to_va(pa); } } current_default = &user_default; console[0] = &main_console; init_scp(console[0]); console[0]->scr_buf = console[0]->mouse_pos = Crtat; console[0]->cursor_pos = Crtat + hw_cursor; console[0]->xpos = hw_cursor % COL; console[0]->ypos = hw_cursor / COL; cur_console = console[0]; for (i=1; iscr_buf = scp->cursor_pos = scp->scr_buf = scp->mouse_pos = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_NOWAIT); scp->history_head = scp->history_pos = scp->history = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_NOWAIT); bzero(scp->history_head, scp->history_size*sizeof(u_short)); if (crtc_vga && video_mode_ptr) set_mode(scp); clear_screen(scp); return scp; } static void init_scp(scr_stat *scp) { scp->mode = M_VGA_C80x25; scp->font = FONT_16; scp->xsize = COL; scp->ysize = ROW; scp->term.esc = 0; scp->term.std_attr = current_default->std_attr; scp->term.rev_attr = current_default->rev_attr; scp->term.cur_attr = scp->term.std_attr; scp->border = BG_BLACK; scp->cursor_start = -1; scp->cursor_end = -1; scp->mouse_xpos = scp->mouse_ypos = 0; scp->bell_pitch = BELL_PITCH; scp->bell_duration = BELL_DURATION; scp->status = (*(char *)pa_to_va(0x417) & 0x20) ? NLKED : 0; scp->status |= CURSOR_ENABLED; scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; scp->history_head = scp->history_pos = scp->history = NULL; scp->history_size = HISTORY_SIZE; } static u_char *get_fstr(u_int c, u_int *len) { u_int i; if (!(c & FKEY)) return(NULL); i = (c & 0xFF) - F_FN; if (i > n_fkey_tab) return(NULL); *len = fkey_tab[i].len; return(fkey_tab[i].str); } static void update_leds(int which) { int s; static u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* replace CAPS led with ALTGR led for ALTGR keyboards */ if (key_map.n_keys > ALTGR_OFFSET) { if (which & ALKED) which |= CLKED; else which &= ~CLKED; } s = spltty(); kbd_cmd(KB_SETLEDS); kbd_cmd(xlate_leds[which & LED_MASK]); splx(s); } static void history_to_screen(scr_stat *scp) { int i; scp->status &= ~UPDATE_SCREEN; for (i=0; iysize; i++) bcopyw(scp->history + (((scp->history_pos - scp->history) + scp->history_size-((i+1)*scp->xsize))%scp->history_size), scp->scr_buf + (scp->xsize * (scp->ysize-1 - i)), scp->xsize * sizeof(u_short)); scp->status |= UPDATE_SCREEN; } static int history_up_line(scr_stat *scp) { if (WRAPHIST(scp, scp->history_pos, -(scp->xsize*scp->ysize)) != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, -scp->xsize); history_to_screen(scp); return 0; } else return -1; } static int history_down_line(scr_stat *scp) { if (scp->history_pos != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, scp->xsize); history_to_screen(scp); return 0; } else return -1; } /* * scgetc(noblock) - get character from keyboard. * If noblock = 0 wait until a key is pressed. * Else return NOKEY. */ u_int scgetc(int noblock) { u_char scancode, keycode; u_int state, action; struct key_t *key; static u_char esc_flag = 0, compose = 0; static u_int chr = 0; next_code: kbd_wait(); /* First see if there is something in the keyboard port */ if (inb(KB_STAT) & KB_BUF_FULL) scancode = inb(KB_DATA); else if (noblock) return(NOKEY); else goto next_code; if (cur_console->status & KBD_RAW_MODE) return scancode; #if ASYNCH if (scancode == KB_ACK || scancode == KB_RESEND) { kbd_reply = scancode; if (noblock) return(NOKEY); goto next_code; } #endif keycode = scancode & 0x7F; switch (esc_flag) { case 0x00: /* normal scancode */ switch(scancode) { case 0xB8: /* left alt (compose key) */ if (compose) { compose = 0; if (chr > 255) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); chr = 0; } } break; case 0x38: if (!compose) { compose = 1; chr = 0; } break; case 0xE0: case 0xE1: esc_flag = scancode; goto next_code; } break; case 0xE0: /* 0xE0 prefix */ esc_flag = 0; switch (keycode) { case 0x1C: /* right enter key */ keycode = 0x59; break; case 0x1D: /* right ctrl key */ keycode = 0x5A; break; case 0x35: /* keypad divide key */ keycode = 0x5B; break; case 0x37: /* print scrn key */ keycode = 0x5C; break; case 0x38: /* right alt key (alt gr) */ keycode = 0x5D; break; case 0x47: /* grey home key */ keycode = 0x5E; break; case 0x48: /* grey up arrow key */ keycode = 0x5F; break; case 0x49: /* grey page up key */ keycode = 0x60; break; case 0x4B: /* grey left arrow key */ keycode = 0x61; break; case 0x4D: /* grey right arrow key */ keycode = 0x62; break; case 0x4F: /* grey end key */ keycode = 0x63; break; case 0x50: /* grey down arrow key */ keycode = 0x64; break; case 0x51: /* grey page down key */ keycode = 0x65; break; case 0x52: /* grey insert key */ keycode = 0x66; break; case 0x53: /* grey delete key */ keycode = 0x67; break; /* the following 3 are only used on the MS "Natural" keyboard */ case 0x5b: /* left Window key */ keycode = 0x69; break; case 0x5c: /* right Window key */ keycode = 0x6a; break; case 0x5d: /* menu key */ keycode = 0x6b; break; default: /* ignore everything else */ goto next_code; } break; case 0xE1: /* 0xE1 prefix */ esc_flag = 0; if (keycode == 0x1D) esc_flag = 0x1D; goto next_code; /* NOT REACHED */ case 0x1D: /* pause / break */ esc_flag = 0; if (keycode != 0x45) goto next_code; keycode = 0x68; break; } /* if scroll-lock pressed allow history browsing */ if (cur_console->history && cur_console->status & SLKED) { int i; cur_console->status &= ~CURSOR_ENABLED; if (!(cur_console->status & BUFFER_SAVED)) { cur_console->status |= BUFFER_SAVED; cur_console->history_save = cur_console->history_head; /* copy screen into top of history buffer */ for (i=0; iysize; i++) { bcopyw(cur_console->scr_buf + (cur_console->xsize * i), cur_console->history_head, cur_console->xsize * sizeof(u_short)); cur_console->history_head += cur_console->xsize; if (cur_console->history_head + cur_console->xsize > cur_console->history + cur_console->history_size) cur_console->history_head=cur_console->history; } cur_console->history_pos = cur_console->history_head; history_to_screen(cur_console); } switch (scancode) { case 0x47: /* home key */ cur_console->history_pos = cur_console->history_head; history_to_screen(cur_console); goto next_code; case 0x4F: /* end key */ cur_console->history_pos = WRAPHIST(cur_console, cur_console->history_head, cur_console->xsize*cur_console->ysize); history_to_screen(cur_console); goto next_code; case 0x48: /* up arrow key */ if (history_up_line(cur_console)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; case 0x50: /* down arrow key */ if (history_down_line(cur_console)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; case 0x49: /* page up key */ for (i=0; iysize; i++) if (history_up_line(cur_console)) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; case 0x51: /* page down key */ for (i=0; iysize; i++) if (history_down_line(cur_console)) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; } } if (compose) { switch (scancode) { /* key pressed process it */ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ chr = (scancode - 0x40) + chr*10; goto next_code; case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ chr = (scancode - 0x47) + chr*10; goto next_code; case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ chr = (scancode - 0x4E) + chr*10; goto next_code; case 0x52: /* keypad 0 */ chr *= 10; goto next_code; /* key release, no interest here */ case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */ case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */ case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */ case 0xD2: /* keypad 0 */ goto next_code; case 0x38: /* left alt key */ break; default: if (chr) { compose = chr = 0; do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; } break; } } state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0)); if ((!agrs && (cur_console->status & ALKED)) || (agrs && !(cur_console->status & ALKED))) keycode += ALTGR_OFFSET; key = &key_map.key[keycode]; if ( ((key->flgs & FLAG_LOCK_C) && (cur_console->status & CLKED)) || ((key->flgs & FLAG_LOCK_N) && (cur_console->status & NLKED)) ) state ^= 1; /* Check for make/break */ action = key->map[state]; if (scancode & 0x80) { /* key released */ if (key->spcl & 0x80) { switch (action) { case LSH: shfts &= ~1; break; case RSH: shfts &= ~2; break; case LCTR: ctls &= ~1; break; case RCTR: ctls &= ~2; break; case LALT: alts &= ~1; break; case RALT: alts &= ~2; break; case NLK: nlkcnt = 0; break; case CLK: clkcnt = 0; break; case SLK: slkcnt = 0; break; case ASH: agrs = 0; break; case ALK: alkcnt = 0; break; case META: metas = 0; break; } } if (chr && !compose) { action = chr; chr = 0; return(action); } } else { /* key pressed */ if (key->spcl & (0x80>>state)) { switch (action) { /* LOCKING KEYS */ case NLK: if (!nlkcnt) { nlkcnt++; if (cur_console->status & NLKED) cur_console->status &= ~NLKED; else cur_console->status |= NLKED; update_leds(cur_console->status); } break; case CLK: if (!clkcnt) { clkcnt++; if (cur_console->status & CLKED) cur_console->status &= ~CLKED; else cur_console->status |= CLKED; update_leds(cur_console->status); } break; case SLK: if (!slkcnt) { slkcnt++; if (cur_console->status & SLKED) { cur_console->status &= ~SLKED; if (cur_console->status & BUFFER_SAVED){ int i; u_short *ptr = cur_console->history_save; for (i=0; iysize; i++) { bcopyw(ptr, cur_console->scr_buf + (cur_console->xsize*i), cur_console->xsize * sizeof(u_short)); ptr += cur_console->xsize; if (ptr + cur_console->xsize > cur_console->history + cur_console->history_size) ptr = cur_console->history; } cur_console->status&=~BUFFER_SAVED; cur_console->history_head=cur_console->history_save; cur_console->status|=(CURSOR_ENABLED|UPDATE_SCREEN); } scstart(VIRTUAL_TTY(get_scr_num())); } else cur_console->status |= SLKED; update_leds(cur_console->status); } break; case ALK: if (!alkcnt) { alkcnt++; if (cur_console->status & ALKED) cur_console->status &= ~ALKED; else cur_console->status |= ALKED; update_leds(cur_console->status); } break; /* NON-LOCKING KEYS */ case NOP: break; case RBT: shutdown_nice(); break; case SUSP: #if NAPM > 0 apm_suspend(); #endif break; case DBG: #ifdef DDB /* try to switch to console 0 */ if (cur_console->smode.mode == VT_AUTO && console[0]->smode.mode == VT_AUTO) switch_scr(cur_console, 0); Debugger("manual escape to debugger"); return(NOKEY); #else printf("No debugger in kernel\n"); #endif break; case LSH: shfts |= 1; break; case RSH: shfts |= 2; break; case LCTR: ctls |= 1; break; case RCTR: ctls |= 2; break; case LALT: alts |= 1; break; case RALT: alts |= 2; break; case ASH: agrs = 1; break; case META: metas = 1; break; case NEXT: switch_scr(cur_console, (get_scr_num() + 1) % MAXCONS); break; case BTAB: return(BKEY); default: if (action >= F_SCR && action <= L_SCR) { switch_scr(cur_console, action - F_SCR); break; } if (action >= F_FN && action <= L_FN) action |= FKEY; return(action); } } else { if (metas) action |= MKEY; return(action); } } goto next_code; } int scmmap(dev_t dev, int offset, int nprot) { if (offset > 0x20000 - PAGE_SIZE) return -1; return i386_btop((VIDEOMEM + offset)); } static void kbd_wait(void) { int i = 1000; while (i--) { if ((inb(KB_STAT) & KB_READY) == 0) break; DELAY (10); } } static void kbd_cmd(u_char command) { int retry = 5; do { int i = 100000; kbd_wait(); #if ASYNCH kbd_reply = 0; outb(KB_DATA, command); while (i--) { if (kbd_reply == KB_ACK) return; if (kbd_reply == KB_RESEND) break; } #else outb(KB_DATA, command); while (i--) { if (inb(KB_STAT) & KB_BUF_FULL) { int val; DELAY(10); val = inb(KB_DATA); if (val == KB_ACK) return; if (val == KB_RESEND) break; } } #endif } while (retry--); } static void set_mode(scr_stat *scp) { char *modetable; char special_modetable[64]; int mode, font_size; if (scp != cur_console) return; /* setup video hardware for the given mode */ switch (scp->mode) { case M_VGA_M80x60: bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64); goto special_80x60; case M_VGA_C80x60: bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64); special_80x60: special_modetable[2] = 0x08; special_modetable[19] = 0x47; goto special_480l; case M_VGA_M80x30: bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64); goto special_80x30; case M_VGA_C80x30: bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64); special_80x30: special_modetable[19] = 0x4f; special_480l: special_modetable[9] |= 0xc0; special_modetable[16] = 0x08; special_modetable[17] = 0x3e; special_modetable[26] = 0xea; special_modetable[28] = 0xdf; special_modetable[31] = 0xe7; special_modetable[32] = 0x04; modetable = special_modetable; goto setup_mode; case M_ENH_B80x43: bcopyw(video_mode_ptr+(64*M_ENH_B80x25),&special_modetable, 64); goto special_80x43; case M_ENH_C80x43: bcopyw(video_mode_ptr+(64*M_ENH_C80x25),&special_modetable, 64); special_80x43: special_modetable[28] = 87; goto special_80x50; case M_VGA_M80x50: bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64); goto special_80x50; case M_VGA_C80x50: bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64); special_80x50: special_modetable[2] = 8; special_modetable[19] = 7; modetable = special_modetable; goto setup_mode; case M_VGA_C40x25: case M_VGA_C80x25: case M_VGA_M80x25: case M_B40x25: case M_C40x25: case M_B80x25: case M_C80x25: case M_ENH_B40x25: case M_ENH_C40x25: case M_ENH_B80x25: case M_ENH_C80x25: modetable = video_mode_ptr + (scp->mode * 64); setup_mode: set_vgaregs(modetable); font_size = *(modetable + 2); /* set font type (size) */ switch (font_size) { case 0x10: outb(TSIDX, 0x03); outb(TSREG, 0x00); /* font 0 */ scp->font = FONT_16; break; case 0x0E: outb(TSIDX, 0x03); outb(TSREG, 0x05); /* font 1 */ scp->font = FONT_14; break; default: case 0x08: outb(TSIDX, 0x03); outb(TSREG, 0x0A); /* font 2 */ scp->font = FONT_8; break; } break; case M_BG320: case M_CG320: case M_BG640: case M_CG320_D: case M_CG640_E: case M_CG640x350: case M_ENH_CG640: case M_BG640x480: case M_CG640x480: case M_VGA_CG320: set_vgaregs(video_mode_ptr + (scp->mode * 64)); break; default: /* call user defined function XXX */ break; } /* set border color for this (virtual) console */ set_border(scp->border); return; } void set_border(int color) { inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x11); outb(ATC, color); inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x20); /* enable Palette */ } static void set_vgaregs(char *modetable) { int i, s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */ outb(TSIDX, 0x07); outb(TSREG, 0x00); /* unlock registers */ for (i=0; i<4; i++) { /* program sequencer */ outb(TSIDX, i+1); outb(TSREG, modetable[i+5]); } outb(MISC, modetable[9]); /* set dot-clock */ outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */ outb(crtc_addr, 0x11); outb(crtc_addr+1, inb(crtc_addr+1) & 0x7F); for (i=0; i<25; i++) { /* program crtc */ outb(crtc_addr, i); if (i == 14 || i == 15) /* no hardware cursor */ outb(crtc_addr+1, 0xff); else outb(crtc_addr+1, modetable[i+10]); } inb(crtc_addr+6); /* reset flip-flop */ for (i=0; i<20; i++) { /* program attribute ctrl */ outb(ATC, i); outb(ATC, modetable[i+35]); } for (i=0; i<9; i++) { /* program graph data ctrl */ outb(GDCIDX, i); outb(GDCREG, modetable[i+55]); } inb(crtc_addr+6); /* reset flip-flop */ outb(ATC ,0x20); /* enable palette */ splx(s); } static void set_font_mode() { /* setup vga for loading fonts (graphics plane mode) */ inb(crtc_addr+6); outb(ATC, 0x30); outb(ATC, 0x01); #if SLOW_VGA outb(TSIDX, 0x02); outb(TSREG, 0x04); outb(TSIDX, 0x04); outb(TSREG, 0x06); outb(GDCIDX, 0x04); outb(GDCREG, 0x02); outb(GDCIDX, 0x05); outb(GDCREG, 0x00); outb(GDCIDX, 0x06); outb(GDCREG, 0x05); #else outw(TSIDX, 0x0402); outw(TSIDX, 0x0604); outw(GDCIDX, 0x0204); outw(GDCIDX, 0x0005); outw(GDCIDX, 0x0506); /* addr = a0000, 64kb */ #endif } static void set_normal_mode() { int s = splhigh(); /* setup vga for normal operation mode again */ inb(crtc_addr+6); outb(ATC, 0x30); outb(ATC, 0x0C); #if SLOW_VGA outb(TSIDX, 0x02); outb(TSREG, 0x03); outb(TSIDX, 0x04); outb(TSREG, 0x02); outb(GDCIDX, 0x04); outb(GDCREG, 0x00); outb(GDCIDX, 0x05); outb(GDCREG, 0x10); if (crtc_addr == MONO_BASE) { outb(GDCIDX, 0x06); outb(GDCREG, 0x0A); /* addr = b0000, 32kb */ } else { outb(GDCIDX, 0x06); outb(GDCREG, 0x0E); /* addr = b8000, 32kb */ } #else outw(TSIDX, 0x0302); outw(TSIDX, 0x0204); outw(GDCIDX, 0x0004); outw(GDCIDX, 0x1005); if (crtc_addr == MONO_BASE) outw(GDCIDX, 0x0A06); /* addr = b0000, 32kb */ else outw(GDCIDX, 0x0E06); /* addr = b8000, 32kb */ #endif splx(s); } static void copy_font(int operation, int font_type, char* font_image) { int ch, line, segment, fontsize; u_char val; switch (font_type) { default: case FONT_8: segment = 0x8000; fontsize = 8; break; case FONT_14: segment = 0x4000; fontsize = 14; break; case FONT_16: segment = 0x0000; fontsize = 16; break; } outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */ outb(TSIDX, 0x01); outb(TSREG, val | 0x20); set_font_mode(); for (ch=0; ch < 256; ch++) for (line=0; line < fontsize; line++) if (operation) *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line) = font_image[(ch*fontsize)+line]; else font_image[(ch*fontsize)+line] = *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line); set_normal_mode(); outb(TSIDX, 0x01); outb(TSREG, val & 0xDF); /* enable screen */ } static void draw_mouse_image(scr_stat *scp) { caddr_t address; int i, font_size; char *font_buffer; u_short buffer[32]; u_short xoffset, yoffset; u_short *crt_pos = Crtat + (scp->mouse_pos - scp->scr_buf); xoffset = scp->mouse_xpos % 8; switch (scp->font) { default: case FONT_8: font_size = 8; font_buffer = font_8; yoffset = scp->mouse_ypos % 8; address = (caddr_t)VIDEOMEM+0x8000; break; case FONT_14: font_size = 14; font_buffer = font_14; yoffset = scp->mouse_ypos % 14; address = (caddr_t)VIDEOMEM+0x4000; break; case FONT_16: font_size = 16; font_buffer = font_16; yoffset = scp->mouse_ypos % 16; address = (caddr_t)VIDEOMEM; break; } bcopyw(font_buffer+((*(scp->mouse_pos) & 0xff)*font_size), &scp->mouse_cursor[0], font_size); bcopyw(font_buffer+((*(scp->mouse_pos+1) & 0xff)*font_size), &scp->mouse_cursor[32], font_size); bcopyw(font_buffer+((*(scp->mouse_pos+scp->xsize) & 0xff)*font_size), &scp->mouse_cursor[64], font_size); bcopyw(font_buffer+((*(scp->mouse_pos+scp->xsize+1) & 0xff)*font_size), &scp->mouse_cursor[96], font_size); for (i=0; imouse_cursor[i]<<8 | scp->mouse_cursor[i+32]; buffer[i+font_size]=scp->mouse_cursor[i+64]<<8|scp->mouse_cursor[i+96]; } for (i=0; i<16; i++) { buffer[i+yoffset] = ( buffer[i+yoffset] & ~(mouse_and_mask[i] >> xoffset)) | (mouse_or_mask[i] >> xoffset); } for (i=0; imouse_cursor[i] = (buffer[i] & 0xff00) >> 8; scp->mouse_cursor[i+32] = buffer[i] & 0xff; scp->mouse_cursor[i+64] = (buffer[i+font_size] & 0xff00) >> 8; scp->mouse_cursor[i+96] = buffer[i+font_size] & 0xff; } /* * if we didn't update entire screen, restore old mouse position * and check if we overwrote the cursor location.. */ if ((scp->status & UPDATE_MOUSE) && !(scp->status & UPDATE_SCREEN)) { u_short *ptr = scp->scr_buf + (scp->mouse_oldpos - Crtat); if (crt_pos != scp->mouse_oldpos) { *(scp->mouse_oldpos) = scp->mouse_saveunder[0]; *(scp->mouse_oldpos+1) = scp->mouse_saveunder[1]; *(scp->mouse_oldpos+scp->xsize) = scp->mouse_saveunder[2]; *(scp->mouse_oldpos+scp->xsize+1) = scp->mouse_saveunder[3]; } scp->mouse_saveunder[0] = *(scp->mouse_pos); scp->mouse_saveunder[1] = *(scp->mouse_pos+1); scp->mouse_saveunder[2] = *(scp->mouse_pos+scp->xsize); scp->mouse_saveunder[3] = *(scp->mouse_pos+scp->xsize+1); if ((scp->cursor_pos == (ptr)) || (scp->cursor_pos == (ptr+1)) || (scp->cursor_pos == (ptr+scp->xsize)) || (scp->cursor_pos == (ptr+scp->xsize+1)) || (scp->cursor_pos == (scp->mouse_pos)) || (scp->cursor_pos == (scp->mouse_pos+1)) || (scp->cursor_pos == (scp->mouse_pos+scp->xsize)) || (scp->cursor_pos == (scp->mouse_pos+scp->xsize+1))) scp->status &= ~CURSOR_SHOWN; } scp->mouse_oldpos = crt_pos; while (!(inb(crtc_addr+6) & 0x08)) /* wait for vertical retrace */ ; *(crt_pos) = *(scp->mouse_pos)&0xff00|0xd0; *(crt_pos+1) = *(scp->mouse_pos+1)&0xff00|0xd1; *(crt_pos+scp->xsize) = *(scp->mouse_pos+scp->xsize)&0xff00|0xd2; *(crt_pos+scp->xsize+1) = *(scp->mouse_pos+scp->xsize+1)&0xff00|0xd3; set_font_mode(); bcopy(scp->mouse_cursor, (char *)pa_to_va(address) + 0xd0 * 32, 128); set_normal_mode(); } static void save_palette(void) { int i; outb(PALRADR, 0x00); for (i=0x00; i<0x300; i++) palette[i] = inb(PALDATA); inb(crtc_addr+6); /* reset flip/flop */ } void load_palette(void) { int i; outb(PIXMASK, 0xFF); /* no pixelmask */ outb(PALWADR, 0x00); for (i=0x00; i<0x300; i++) outb(PALDATA, palette[i]); inb(crtc_addr+6); /* reset flip/flop */ outb(ATC, 0x20); /* enable palette */ } static void do_bell(scr_stat *scp, int pitch, int duration) { if (scp == cur_console) { if (configuration & VISUAL_BELL) { if (blink_in_progress) return; blink_in_progress = 4; blink_screen(scp); timeout((timeout_func_t)blink_screen, scp, hz/10); } else sysbeep(pitch, duration); } } static void blink_screen(scr_stat *scp) { if (blink_in_progress > 1) { if (blink_in_progress & 1) fillw(kernel_default.std_attr | scr_map[0x20], Crtat, scp->xsize * scp->ysize); else fillw(kernel_default.rev_attr | scr_map[0x20], Crtat, scp->xsize * scp->ysize); blink_in_progress--; timeout((timeout_func_t)blink_screen, scp, hz/10); } else { scp->status |= UPDATE_SCREEN; blink_in_progress = FALSE; if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } } #endif /* NSC */ Index: head/sys/dev/syscons/syscons.h =================================================================== --- head/sys/dev/syscons/syscons.h (revision 6781) +++ head/sys/dev/syscons/syscons.h (revision 6782) @@ -1,208 +1,208 @@ /*- * Copyright (c) 1995 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software withough specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: syscons.h,v 1.1 1995/02/22 13:40:21 sos Exp $ + * $Id: syscons.h,v 1.2 1995/02/25 20:09:21 pst Exp $ */ /* * The APM stuff is -not- under conditional compilation because we don't want * the size of the scr_stat structure to vary depending upon if APM has been * compiled in or not, that can cause utilities and lkms to crash! */ #include /* vm things */ #define ISMAPPED(pa, width) \ (((pa) <= (u_long)0x1000 - (width)) \ || ((pa) >= 0xa0000 && (pa) <= 0x100000 - (width))) #define pa_to_va(pa) (KERNBASE + (pa)) /* works if ISMAPPED(pa...) */ /* printable chars */ #define PRINTABLE(ch) (ch>0x1B || (ch>0x0d && ch<0x1b) || ch<0x07) /* status flags */ #define LOCK_KEY_MASK 0x0000F #define LED_MASK 0x00007 #define UNKNOWN_MODE 0x00010 #define KBD_RAW_MODE 0x00020 #define SWITCH_WAIT_REL 0x00040 #define SWITCH_WAIT_ACQ 0x00080 #define BUFFER_SAVED 0x00100 #define CURSOR_ENABLED 0x00200 #define CURSOR_SHOWN 0x00400 #define MOUSE_ENABLED 0x00800 #define UPDATE_MOUSE 0x01000 #define UPDATE_SCREEN 0x02000 /* configuration flags */ #define VISUAL_BELL 0x00001 #define BLINK_CURSOR 0x00002 #define CHAR_CURSOR 0x00004 /* video hardware memory addresses */ #define VIDEOMEM 0x000A0000 /* misc defines */ #define FALSE 0 #define TRUE 1 #define MAX_ESC_PAR 5 #define LOAD 1 #define SAVE 0 #define COL 80 #define ROW 25 #define BELL_DURATION 5 #define BELL_PITCH 800 #define TIMER_FREQ 1193182 /* should be in isa.h */ #define CONSOLE_BUFSIZE 1024 #define PCBURST 128 #define FONT_8 0x001 #define FONT_14 0x002 #define FONT_16 0x004 #define HISTORY_SIZE 100*80 /* defines related to hardware addresses */ #define MONO_BASE 0x3B4 /* crt controller base mono */ #define COLOR_BASE 0x3D4 /* crt controller base color */ #define MISC 0x3C2 /* misc output register */ #define ATC IO_VGA+0x00 /* attribute controller */ #define TSIDX IO_VGA+0x04 /* timing sequencer idx */ #define TSREG IO_VGA+0x05 /* timing sequencer data */ #define PIXMASK IO_VGA+0x06 /* pixel write mask */ #define PALRADR IO_VGA+0x07 /* palette read address */ #define PALWADR IO_VGA+0x08 /* palette write address */ #define PALDATA IO_VGA+0x09 /* palette data register */ #define GDCIDX IO_VGA+0x0E /* graph data controller idx */ #define GDCREG IO_VGA+0x0F /* graph data controller data */ /* special characters */ #define cntlc 0x03 #define cntld 0x04 #define bs 0x08 #define lf 0x0a #define cr 0x0d #define del 0x7f typedef struct term_stat { int esc; /* processing escape sequence */ int num_param; /* # of parameters to ESC */ int last_param; /* last parameter # */ int param[MAX_ESC_PAR]; /* contains ESC parameters */ int cur_attr; /* current attributes */ int std_attr; /* normal attributes */ int rev_attr; /* reverse attributes */ } term_stat; typedef struct scr_stat { u_short *scr_buf; /* buffer when off screen */ int xpos; /* current X position */ int ypos; /* current Y position */ int xsize; /* X size */ int ysize; /* Y size */ term_stat term; /* terminal emulation stuff */ int status; /* status (bitfield) */ u_short *cursor_pos; /* cursor buffer position */ u_short cursor_saveunder; /* saved chars under cursor */ char cursor_start; /* cursor start line # */ char cursor_end; /* cursor end line # */ u_short *mouse_pos; /* mouse buffer position */ u_short *mouse_oldpos; /* mouse old buffer position */ u_short mouse_saveunder[4]; /* saved chars under mouse */ short mouse_xpos; /* mouse x coordinate */ short mouse_ypos; /* mouse y coordinate */ u_char mouse_cursor[128]; /* mouse cursor bitmap store */ u_short bell_duration; u_short bell_pitch; u_char border; /* border color */ u_char mode; /* mode */ u_char font; /* font on this screen */ pid_t pid; /* pid of controlling proc */ struct proc *proc; /* proc* of controlling proc */ struct vt_mode smode; /* switch mode */ u_short *history; /* circular history buffer */ u_short *history_head; /* current head position */ u_short *history_pos; /* position shown on screen */ u_short *history_save; /* save area index */ int history_size; /* size of history buffer */ struct apmhook r_hook; /* reconfiguration support */ } scr_stat; typedef struct default_attr { int std_attr; /* normal attributes */ int rev_attr; /* reverse attributes */ } default_attr; /* function prototypes */ int scprobe(struct isa_device *dev); int scattach(struct isa_device *dev); int scopen(dev_t dev, int flag, int mode, struct proc *p); int scclose(dev_t dev, int flag, int mode, struct proc *p); int scread(dev_t dev, struct uio *uio, int flag); int scwrite(dev_t dev, struct uio *uio, int flag); int scparam(struct tty *tp, struct termios *t); int scioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p); void scxint(dev_t dev); void scstart(struct tty *tp); void pccnprobe(struct consdev *cp); void pccninit(struct consdev *cp); void pccnputc(dev_t dev, char c); int pccngetc(dev_t dev); int pccncheckc(dev_t dev); void scintr(int unit); int pcmmap(dev_t dev, int offset, int nprot); static void scinit(void); static u_int scgetc(int noblock); -static struct tty *get_tty_ptr(dev_t dev); +struct tty *scdevtotty(dev_t dev); static scr_stat *get_scr_stat(dev_t dev); static scr_stat *alloc_scp(); static void init_scp(scr_stat *scp); static int get_scr_num(); static void scrn_timer(); static void clear_screen(scr_stat *scp); static int switch_scr(scr_stat *scp, u_int next_scr); static void exchange_scr(void); static inline void move_crsr(scr_stat *scp, int x, int y); static void scan_esc(scr_stat *scp, u_char c); static inline void draw_cursor(scr_stat *scp, int show); static void ansi_put(scr_stat *scp, u_char *buf, int len); static u_char *get_fstr(u_int c, u_int *len); static void update_leds(int which); static void history_to_screen(scr_stat *scp); static int history_up_line(scr_stat *scp); static int history_down_line(scr_stat *scp); static void kbd_wait(void); static void kbd_cmd(u_char command); static void set_mode(scr_stat *scp); void set_border(int color); static void set_vgaregs(char *modetable); static void set_font_mode(); static void set_normal_mode(); static void copy_font(int operation, int font_type, char* font_image); static void draw_mouse_image(scr_stat *scp); static void save_palette(void); void load_palette(void); static void do_bell(scr_stat *scp, int pitch, int duration); static void blink_screen(scr_stat *scp); Index: head/sys/gnu/isdn/iitty.c =================================================================== --- head/sys/gnu/isdn/iitty.c (revision 6781) +++ head/sys/gnu/isdn/iitty.c (revision 6782) @@ -1,347 +1,346 @@ -static char _ittyid[] = "@(#)$Id: iitty.c,v 1.2 1995/02/15 06:28:28 jkh Exp $"; +static char _ittyid[] = "@(#)$Id: iitty.c,v 1.3 1995/02/25 20:08:52 pst Exp $"; /******************************************************************************* - * II - Version 0.1 $Revision: 1.2 $ $State: Exp $ + * II - Version 0.1 $Revision: 1.3 $ $State: Exp $ * * Copyright 1994 Dietmar Friede ******************************************************************************* * Bug reports, patches, comments, suggestions should be sent to: * * jkr@saarlink.de or jkrause@guug.de * ******************************************************************************* * $Log: iitty.c,v $ + * Revision 1.3 1995/02/25 20:08:52 pst + * (a) remove the pointer to each driver's tty structure array from cdevsw + * (b) add a function callback vector to tty drivers that will return a pointer + * to a valid tty structure based upon a dev_t + * (c) make syscons structures the same size whether or not APM is enabled so + * utilities don't crash if NAPM changes (and make the damn kernel compile!) + * (d) rewrite /dev/snp ioctl interface so that it is device driver and i386 + * independant + * * Revision 1.2 1995/02/15 06:28:28 jkh * Fix up include paths, nuke some warnings. * * Revision 1.1 1995/02/14 15:00:32 jkh * An ISDN driver that supports the EDSS1 and the 1TR6 ISDN interfaces. * EDSS1 is the "Euro-ISDN", 1TR6 is the soon obsolete german ISDN Interface. * Obtained from: Dietmar Friede and * Juergen Krause * * This is only one part - the rest to follow in a couple of hours. * This part is a benign import, since it doesn't affect anything else. * * ******************************************************************************/ #include "ity.h" #if NITY > 0 #include "param.h" #include "systm.h" #include "ioctl.h" #include "select.h" #include "tty.h" #include "proc.h" #include "user.h" #include "conf.h" #include "file.h" #include "uio.h" #include "kernel.h" #include "syslog.h" #include "types.h" #include "gnu/isdn/isdn_ioctl.h" int ityattach(), ityparam(); void itystart(); int nity = NITY; int itydefaultrate = 64000; short ity_addr[NITY]; struct tty ity_tty[NITY]; static int applnr[NITY]; static int next_if= 0; #define UNIT(x) (minor(x)&0x3f) #define OUTBOUND(x) ((minor(x)&0x80)==0x80) int ityattach(int ap) { if(next_if >= NITY) return(-1); applnr[next_if]= ap; return(next_if++); } /* ARGSUSED */ int ityopen(dev_t dev, int flag, int mode, struct proc * p) { register struct tty *tp; register int unit; int error = 0; unit = UNIT(dev); if (unit >= next_if) return (ENXIO); tp = &ity_tty[unit]; tp->t_oproc = itystart; tp->t_param = ityparam; tp->t_dev = dev; if ((tp->t_state & TS_ISOPEN) == 0) { tp->t_state |= TS_WOPEN; ttychars(tp); if (tp->t_ispeed == 0) { tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = itydefaultrate; } ityparam(tp, &tp->t_termios); ttsetwater(tp); } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) return (EBUSY); (void) spltty(); if(OUTBOUND(dev)) tp->t_cflag |= CLOCAL; while ((flag & O_NONBLOCK) == 0 && (tp->t_cflag & CLOCAL) == 0 && (tp->t_state & TS_CARR_ON) == 0) { tp->t_state |= TS_WOPEN; if (error = ttysleep(tp, (caddr_t) & tp->t_rawq, TTIPRI | PCATCH, ttopen, 0)) break; } (void) spl0(); if (error == 0) error = (*linesw[tp->t_line].l_open) (dev, tp); return (error); } /* ARGSUSED */ int ityclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { register struct tty *tp; register ity; register int unit; unit = UNIT(dev); ity = ity_addr[unit]; if(tp = &ity_tty[unit]) (*linesw[tp->t_line].l_close) (tp, flag); ttyclose(tp); isdn_disconnect(applnr[unit],0); return (0); } int ityread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { register struct tty *tp = &ity_tty[UNIT(dev)]; return ((*linesw[tp->t_line].l_read) (tp, uio, flag)); } int itywrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int unit = UNIT(dev); register struct tty *tp = &ity_tty[unit]; return ((*linesw[tp->t_line].l_write) (tp, uio, flag)); } int ity_input(int no, int len, char *buf) { register struct tty *tp = &ity_tty[no]; int i; if (tp->t_state & TS_ISOPEN) for(i= 0; it_line].l_rint)(buf[i], tp); else len= 0; return(len); } void itystart(struct tty *tp) { int s, unit; unit = UNIT(tp->t_dev); s = splhigh(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { splx(s); return; } if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t) & tp->t_outq); } selwakeup(&tp->t_wsel); } if (tp->t_outq.c_cc) { if(OUTBOUND(tp->t_dev) && (tp->t_cflag & CLOCAL) && ((tp->t_state & TS_CARR_ON) == 0)) isdn_msg(applnr[unit]); else isdn_output(applnr[unit]); tp->t_state |= TS_BUSY; } splx(s); } int ity_out(int no, char *buf, int len) { struct tty *tp = &ity_tty[no]; int i; if(tp == NULL) return(0); if(tp->t_outq.c_cc) { for (i = 0; i < len && tp->t_outq.c_cc; ++i) buf[i]= getc(&tp->t_outq); return(i); } tp->t_state &=~ (TS_BUSY|TS_FLUSH); if (tp->t_line) (*linesw[tp->t_line].l_start)(tp); else itystart(tp); return(0); } void ity_connect(int no) { struct tty *tp = &ity_tty[no]; if(tp == NULL) return; if(OUTBOUND(tp->t_dev)) tp->t_cflag &= ~CLOCAL; (*linesw[tp->t_line].l_modem) (tp, 1); tp->t_state |= TS_CARR_ON; tp->t_state &=~ (TS_BUSY|TS_FLUSH); if (tp->t_line) (*linesw[tp->t_line].l_start)(tp); else itystart(tp); } void ity_disconnect(int no) { struct tty *tp = &ity_tty[no]; if(tp) (*linesw[tp->t_line].l_modem) (tp, 0); } int ityioctl(dev, cmd, data, flag,p) dev_t dev; int cmd; caddr_t data; int flag; struct proc *p; { register struct tty *tp; register int unit = UNIT(dev); register int error; tp = &ity_tty[unit]; error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag,p); if (error >= 0) return (error); error = ttioctl(tp, cmd, data, flag); if (error >= 0) return (error); switch (cmd) { default: return (ENOTTY); } return (0); } int ityparam(tp, t) register struct tty *tp; register struct termios *t; { register ity; register int cfcr, cflag = t->c_cflag; int unit = UNIT(tp->t_dev); int ospeed = t->c_ospeed; /* check requested parameters */ if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) return (EINVAL); /* and copy to tty */ tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = cflag; if (ospeed == 0) { isdn_disconnect(applnr[unit],0); return (0); } return (0); } /* * Stop output on a line. */ /* ARGSUSED */ void itystop(struct tty *tp, int flag) { register int s; s = splhigh(); if (tp->t_state & TS_BUSY) { if ((tp->t_state & TS_TTSTOP) == 0) tp->t_state |= TS_FLUSH; } splx(s); } struct tty * itydevtotty(dev_t dev) { register int unit = UNIT(dev); if (unit >= next_if) return (NULL); return (&ity_tty[unit]); -} - -int -ityselect(dev_t dev, int rw, struct proc *p) -{ - register int unit = UNIT(dev); - if (unit >= next_if) - return (ENXIO); - - return (ttyselect(&ity_tty[unit], rw, p)); } #endif Index: head/sys/i386/i386/conf.c =================================================================== --- head/sys/i386/i386/conf.c (revision 6781) +++ head/sys/i386/i386/conf.c (revision 6782) @@ -1,1204 +1,1196 @@ /* * Copyright (c) UNIX System Laboratories, Inc. All or some portions * of this file are derived from material licensed to the * University of California by American Telephone and Telegraph Co. * or UNIX System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. */ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)conf.c 5.8 (Berkeley) 5/12/91 - * $Id: conf.c,v 1.68 1995/02/25 20:09:03 pst Exp $ + * $Id: conf.c,v 1.69 1995/02/27 19:46:27 ugen Exp $ */ #include #include #include #include #include #include #include #include d_rdwr_t rawread, rawwrite; d_strategy_t swstrategy; /* Lots of bogus defines for shorthand purposes */ #define noopen (d_open_t *)enodev #define noclose (d_close_t *)enodev #define noread (d_rdwr_t *)enodev #define nowrite noread #define noioc (d_ioctl_t *)enodev #define nostop (d_stop_t *)enodev #define noreset (d_reset_t *)enodev #define noselect (d_select_t *)enodev #define nommap (d_mmap_t *)enodev #define nostrat (d_strategy_t *)enodev #define nodump (d_dump_t *)enodev #define nodevtotty (d_ttycv_t *)nullop #define nxopen (d_open_t *)enxio #define nxclose (d_close_t *)enxio #define nxread (d_rdwr_t *)enxio #define nxwrite nxread #define nxstrategy (d_strategy_t *)enxio #define nxioctl (d_ioctl_t *)enxio #define nxdump (d_dump_t *)enxio #define nxstop (d_stop_t *)enxio #define nxreset (d_reset_t *)enxio #define nxselect (d_select_t *)enxio #define nxmmap (d_mmap_t *)enxio #define nxdevtotty (d_ttycv_t *)nullop #define nullopen (d_open_t *)nullop #define nullclose (d_close_t *)nullop #define nullstop (d_stop_t *)nullop #define nullreset (d_reset_t *)nullop #define zerosize (d_psize_t *)0 int lkmenodev(); #define lkmopen (d_open_t *)lkmenodev #define lkmclose (d_close_t *)lkmenodev #define lkmread (d_rdwr_t *)lkmenodev #define lkmwrite (d_rdwr_t *)lkmenodev #define lkmstrategy (d_strategy_t *)lkmenodev #define lkmioctl (d_ioctl_t *)lkmenodev #define lkmdump (d_dump_t *)lkmenodev #define lkmsize zerosize #define lkmstop (d_stop_t *)lkmenodev #define lkmreset (d_reset_t *)lkmenodev #define lkmmmap (d_mmap_t *)lkmenodev #define lkmselect (d_select_t *)lkmenodev #include "wd.h" #if (NWD > 0) d_open_t wdopen; d_close_t wdclose; d_strategy_t wdstrategy; d_ioctl_t wdioctl; d_dump_t wddump; d_psize_t wdsize; #else #define wdopen nxopen #define wdclose nxclose #define wdstrategy nxstrategy #define wdioctl nxioctl #define wddump nxdump #define wdsize zerosize #endif #include "sd.h" #if NSD > 0 d_open_t sdopen; d_close_t sdclose; d_strategy_t sdstrategy; d_ioctl_t sdioctl; d_dump_t sddump; d_psize_t sdsize; #else #define sdopen nxopen #define sdclose nxclose #define sdstrategy nxstrategy #define sdioctl nxioctl #define sddump nxdump #define sdsize zerosize #endif #include "st.h" #if NST > 0 d_open_t stopen; d_close_t stclose; d_strategy_t ststrategy; d_ioctl_t stioctl; /*int stdump(),stsize();*/ #define stdump nxdump #define stsize zerosize #else #define stopen nxopen #define stclose nxclose #define ststrategy nxstrategy #define stioctl nxioctl #define stdump nxdump #define stsize zerosize #endif #include "cd.h" #if NCD > 0 d_open_t cdopen; d_close_t cdclose; d_strategy_t cdstrategy; d_ioctl_t cdioctl; d_psize_t cdsize; #define cddump nxdump #else #define cdopen nxopen #define cdclose nxclose #define cdstrategy nxstrategy #define cdioctl nxioctl #define cddump nxdump #define cdsize zerosize #endif #include "mcd.h" #if NMCD > 0 d_open_t mcdopen; d_close_t mcdclose; d_strategy_t mcdstrategy; d_ioctl_t mcdioctl; d_psize_t mcdsize; #define mcddump nxdump #else #define mcdopen nxopen #define mcdclose nxclose #define mcdstrategy nxstrategy #define mcdioctl nxioctl #define mcddump nxdump #define mcdsize zerosize #endif #include "scd.h" #if NSCD > 0 d_open_t scdopen; d_close_t scdclose; d_strategy_t scdstrategy; d_ioctl_t scdioctl; d_psize_t scdsize; #define scddump nxdump #else #define scdopen nxopen #define scdclose nxclose #define scdstrategy nxstrategy #define scdioctl nxioctl #define scddump nxdump #define scdsize zerosize #endif #include "pcd.h" #if NPCD > 0 d_open_t pcdopen; d_close_t pcdclose; d_strategy_t pcdstrategy; d_ioctl_t pcdioctl; d_psize_t pcdsize; #define pcddump nxdump #else #define pcdopen nxopen #define pcdclose nxclose #define pcdstrategy nxstrategy #define pcdioctl nxioctl #define pcddump nxdump #define pcdsize zerosize #endif #include "ch.h" #if NCH > 0 d_open_t chopen; d_close_t chclose; d_ioctl_t chioctl; #else #define chopen nxopen #define chclose nxclose #define chioctl nxioctl #endif #include "wt.h" #if NWT > 0 d_open_t wtopen; d_close_t wtclose; d_strategy_t wtstrategy; d_ioctl_t wtioctl; d_dump_t wtdump; d_psize_t wtsize; #else #define wtopen nxopen #define wtclose nxclose #define wtstrategy nxstrategy #define wtioctl nxioctl #define wtdump nxdump #define wtsize zerosize #endif #include "fd.h" #if NFD > 0 d_open_t Fdopen; d_close_t fdclose; d_strategy_t fdstrategy; d_ioctl_t fdioctl; #define fddump nxdump #define fdsize zerosize #else #define Fdopen nxopen #define fdclose nxclose #define fdstrategy nxstrategy #define fdioctl nxioctl #define fddump nxdump #define fdsize zerosize #endif #include "vn.h" #if NVN > 0 d_open_t vnopen; d_close_t vnclose; d_strategy_t vnstrategy; d_ioctl_t vnioctl; d_dump_t vndump; d_psize_t vnsize; #else #define vnopen nxopen #define vnclose nxclose #define vnstrategy nxstrategy #define vnioctl nxioctl #define vndump nxdump #define vnsize zerosize #endif #define swopen noopen #define swclose noclose #define swioctl noioc #define swdump nodump #define swsize (d_psize_t *)enodev d_rdwr_t swread, swwrite; struct bdevsw bdevsw[] = { { wdopen, wdclose, wdstrategy, wdioctl, /*0*/ wddump, wdsize, 0 }, { swopen, swclose, swstrategy, swioctl, /*1*/ swdump, swsize, 0 }, { Fdopen, fdclose, fdstrategy, fdioctl, /*2*/ fddump, fdsize, 0 }, { wtopen, wtclose, wtstrategy, wtioctl, /*3*/ wtdump, wtsize, B_TAPE }, { sdopen, sdclose, sdstrategy, sdioctl, /*4*/ sddump, sdsize, 0 }, { stopen, stclose, ststrategy, stioctl, /*5*/ stdump, stsize, 0 }, { cdopen, cdclose, cdstrategy, cdioctl, /*6*/ cddump, cdsize, 0 }, { mcdopen, mcdclose, mcdstrategy, mcdioctl, /*7*/ mcddump, mcdsize, 0 }, { lkmopen, lkmclose, lkmstrategy, lkmioctl, /*8*/ lkmdump, lkmsize, NULL }, { lkmopen, lkmclose, lkmstrategy, lkmioctl, /*9*/ lkmdump, lkmsize, NULL }, { lkmopen, lkmclose, lkmstrategy, lkmioctl, /*10*/ lkmdump, lkmsize, NULL }, { lkmopen, lkmclose, lkmstrategy, lkmioctl, /*11*/ lkmdump, lkmsize, NULL }, { lkmopen, lkmclose, lkmstrategy, lkmioctl, /*12*/ lkmdump, lkmsize, NULL }, { lkmopen, lkmclose, lkmstrategy, lkmioctl, /*13*/ lkmdump, lkmsize, NULL }, /* block device 14 is reserved for local use */ { nxopen, nxclose, nxstrategy, nxioctl, /*14*/ nxdump, zerosize, NULL }, { vnopen, vnclose, vnstrategy, vnioctl, /*15*/ vndump, vnsize, 0 }, { scdopen, scdclose, scdstrategy, scdioctl, /*16*/ scddump, scdsize, 0 }, { pcdopen, pcdclose, pcdstrategy, pcdioctl, /*17*/ pcddump, pcdsize, 0 } /* * If you need a bdev major number for a driver that you intend to donate * back to the group or release publically, please contact the FreeBSD team * by sending mail to "FreeBSD-hackers@freefall.cdrom.com". * If you assign one yourself it may conflict with someone else. * Otherwise, simply use the one reserved for local use. */ }; int nblkdev = sizeof (bdevsw) / sizeof (bdevsw[0]); /* console */ #include "machine/cons.h" /* more console */ #include "sc.h" #include "vt.h" #if NSC > 0 d_open_t scopen; d_close_t scclose; d_rdwr_t scread, scwrite; -d_select_t scselect; d_ioctl_t scioctl; d_mmap_t scmmap; d_ttycv_t scdevtotty; #else #define scopen nxopen #define scclose nxclose #define scread nxread #define scwrite nxwrite -#define scselect nxselect #define scioctl nxioctl #define scmmap nxmmap #define scdevtotty nxdevtotty #endif /* controlling TTY */ d_open_t cttyopen; d_rdwr_t cttyread; d_rdwr_t cttywrite; d_ioctl_t cttyioctl; d_select_t cttyselect; d_ttycv_t cttydevtotty; /* /dev/mem */ d_open_t mmopen; d_close_t mmclose; d_rdwr_t mmrw; d_mmap_t memmmap; #define mmselect seltrue #include "pty.h" #if NPTY > 0 d_open_t ptsopen; d_close_t ptsclose; d_rdwr_t ptsread; d_rdwr_t ptswrite; d_stop_t ptsstop; -d_select_t ptsselect; d_open_t ptcopen; d_close_t ptcclose; d_rdwr_t ptcread; d_rdwr_t ptcwrite; d_select_t ptcselect; d_ttycv_t ptydevtotty; d_ioctl_t ptyioctl; #else #define ptsopen nxopen #define ptsclose nxclose #define ptsread nxread #define ptswrite nxwrite -#define ptsselect nxselect #define ptcopen nxopen #define ptcclose nxclose #define ptcread nxread #define ptcwrite nxwrite #define ptyioctl nxioctl #define ptcselect nxselect #define ptsstop nullstop #define ptydevtotty nxdevtotty #endif #include "snp.h" #if NSNP > 0 d_open_t snpopen; d_close_t snpclose; d_rdwr_t snpread; d_rdwr_t snpwrite; d_select_t snpselect; d_ioctl_t snpioctl; #else #define snpopen nxopen #define snpclose nxclose #define snpread nxread #define snpwrite nxwrite #define snpioctl nxioctl #define snpselect nxselect #endif /* /dev/klog */ d_open_t logopen; d_close_t logclose; d_rdwr_t logread; d_ioctl_t logioctl; d_select_t logselect; #include "bqu.h" #if NBQU > 0 d_open_t bquopen; d_close_t bquclose; d_rdwr_t bquread, bquwrite; d_select_t bquselect; d_ioctl_t bquioctl; #else #define bquopen nxopen #define bquclose nxclose #define bquread nxread #define bquwrite nxwrite #define bquselect nxselect #define bquioctl nxioctl #endif #include "lpt.h" #if NLPT > 0 d_open_t lptopen; d_close_t lptclose; d_rdwr_t lptwrite; d_ioctl_t lptioctl; #else #define lptopen nxopen #define lptclose nxclose #define lptwrite nxwrite #define lptioctl nxioctl #endif #include "tw.h" #if NTW > 0 d_open_t twopen; d_close_t twclose; d_rdwr_t twread, twwrite; d_select_t twselect; d_ttycv_t twdevtotty; #else #define twopen nxopen #define twclose nxclose #define twread nxread #define twwrite nxwrite #define twselect nxselect #define twdevtotty nxdevtotty #endif #include "psm.h" #if NPSM > 0 d_open_t psmopen; d_close_t psmclose; d_rdwr_t psmread; d_select_t psmselect; d_ioctl_t psmioctl; #else #define psmopen nxopen #define psmclose nxclose #define psmread nxread #define psmselect nxselect #define psmioctl nxioctl #endif #include "snd.h" /* General Sound Driver */ #if NSND > 0 d_open_t sndopen; d_close_t sndclose; d_ioctl_t sndioctl; d_rdwr_t sndread, sndwrite; d_select_t sndselect; #else #define sndopen nxopen #define sndclose nxclose #define sndioctl nxioctl #define sndread nxread #define sndwrite nxwrite #define sndselect seltrue #endif #include "vat_audio.h" /* BSD audio driver emulator for voxware */ #if NVAT_AUDIO > 0 /* not general purpose, just vat */ d_open_t vaopen; d_close_t vaclose; d_ioctl_t vaioctl; d_rdwr_t varead, vawrite; d_select_t vaselect; #else #define vaopen nxopen #define vaclose nxclose #define vaioctl nxioctl #define varead nxread #define vawrite nxwrite #define vaselect seltrue #endif /* /dev/fd/NNN */ d_open_t fdopen; #include "bpfilter.h" #if NBPFILTER > 0 d_open_t bpfopen; d_close_t bpfclose; d_rdwr_t bpfread, bpfwrite; d_select_t bpfselect; d_ioctl_t bpfioctl; #else #define bpfopen nxopen #define bpfclose nxclose #define bpfread nxread #define bpfwrite nxwrite #define bpfselect nxselect #define bpfioctl nxioctl #endif #include "speaker.h" #if NSPEAKER > 0 d_open_t spkropen; d_close_t spkrclose; d_rdwr_t spkrwrite; d_ioctl_t spkrioctl; #else #define spkropen nxopen #define spkrclose nxclose #define spkrwrite nxwrite #define spkrioctl nxioctl #endif #include "pca.h" #if NPCA > 0 d_open_t pcaopen; d_close_t pcaclose; d_rdwr_t pcawrite; d_ioctl_t pcaioctl; d_select_t pcaselect; #else #define pcaopen nxopen #define pcaclose nxclose #define pcawrite nxwrite #define pcaioctl nxioctl #define pcaselect nxselect #endif #include "mse.h" #if NMSE > 0 d_open_t mseopen; d_close_t mseclose; d_rdwr_t mseread; d_select_t mseselect; #else #define mseopen nxopen #define mseclose nxclose #define mseread nxread #define mseselect nxselect #endif #include "sio.h" #if NSIO > 0 d_open_t sioopen; d_close_t sioclose; d_rdwr_t sioread, siowrite; d_ioctl_t sioioctl; -d_select_t sioselect; d_stop_t siostop; d_ttycv_t siodevtotty; #define sioreset nxreset #else #define sioopen nxopen #define sioclose nxclose #define sioread nxread #define siowrite nxwrite #define sioioctl nxioctl #define siostop nxstop #define sioreset nxreset -#define sioselect nxselect #define siodevtotty nxdevtotty #endif #include "su.h" #if NSU > 0 d_open_t suopen; d_close_t suclose; d_ioctl_t suioctl; d_rdwr_t suread, suwrite; d_select_t suselect; #define summap nxmmap d_strategy_t sustrategy; #else #define suopen nxopen #define suclose nxclose #define suioctl nxioctl #define suread nxread #define suwrite nxwrite #define suselect nxselect #define summap nxmmap #define sustrategy nxstrategy #endif #include "uk.h" #if NUK > 0 d_open_t ukopen; d_close_t ukclose; d_ioctl_t ukioctl; #else #define ukopen nxopen #define ukclose nxclose #define ukioctl nxioctl #endif d_open_t lkmcopen; d_close_t lkmcclose; d_ioctl_t lkmcioctl; #include "apm.h" #if NAPM > 0 d_open_t apmopen; d_close_t apmclose; d_ioctl_t apmioctl; #else #define apmopen nxopen #define apmclose nxclose #define apmioctl nxioctl #endif #ifdef IBCS2 d_open_t sockopen; d_close_t sockclose; d_ioctl_t sockioctl; #else #define sockopen nxopen #define sockclose nxclose #define sockioctl nxioctl #endif #include "ctx.h" #if NCTX > 0 d_open_t ctxopen; d_close_t ctxclose; d_rdwr_t ctxread; d_rdwr_t ctxwrite; d_ioctl_t ctxioctl; #else #define ctxopen nxopen #define ctxclose nxclose #define ctxread nxread #define ctxwrite nxwrite #define ctxioctl nxioctl #endif #include "ssc.h" #if NSSC > 0 d_open_t sscopen; d_close_t sscclose; d_ioctl_t sscioctl; d_rdwr_t sscread, sscwrite; d_select_t sscselect; #define sscmmap nxmmap d_strategy_t sscstrategy; #else #define sscopen nxopen #define sscclose nxclose #define sscioctl nxioctl #define sscread nxread #define sscwrite nxwrite #define sscselect nxselect #define sscmmap nxmmap #define sscstrategy nxstrategy #endif #include "cx.h" #if NCX > 0 d_open_t cxopen; d_close_t cxclose; d_rdwr_t cxread, cxwrite; d_ioctl_t cxioctl; d_select_t cxselect; d_stop_t cxstop; +d_ttycv_t cxdevtotty; #else #define cxopen nxopen #define cxclose nxclose #define cxread nxread #define cxwrite nxwrite #define cxioctl nxioctl #define cxstop nxstop #define cxselect nxselect +#define cxdevtotty nxdevtotty #endif #include "gp.h" #if NGP > 0 d_open_t gpopen; d_close_t gpclose; d_rdwr_t gpwrite; d_ioctl_t gpioctl; #else #define gpopen nxopen #define gpclose nxclose #define gpwrite nxwrite #define gpioctl nxioctl #endif #include "gsc.h" #if NGSC > 0 d_open_t gscopen; d_close_t gscclose; d_rdwr_t gscread; d_ioctl_t gscioctl; #else #define gscopen nxopen #define gscclose nxclose #define gscread nxread #define gscioctl nxioctl #endif #include "joy.h" #if NJOY > 0 d_open_t joyopen; d_close_t joyclose; d_rdwr_t joyread; d_ioctl_t joyioctl; #else #define joyopen nxopen #define joyclose nxclose #define joyread nxread #define joyioctl nxioctl #endif #include "tun.h" #if NTUN > 0 d_open_t tunopen; d_close_t tunclose; d_rdwr_t tunread, tunwrite; d_ioctl_t tunioctl; d_select_t tunselect; #else #define tunopen nxopen #define tunclose nxclose #define tunread nxread #define tunwrite nxwrite #define tunioctl nxioctl #define tunselect nxselect #endif #include "spigot.h" #if NSPIGOT > 0 d_open_t spigot_open; d_close_t spigot_close; d_ioctl_t spigot_ioctl; d_rdwr_t spigot_read, spigot_write; d_select_t spigot_select; d_mmap_t spigot_mmap; #else #define spigot_open nxopen #define spigot_close nxclose #define spigot_ioctl nxioctl #define spigot_read nxread #define spigot_write nxwrite #define spigot_select seltrue #define spigot_mmap nommap #endif /* Cyclades serial driver */ #include "cy.h" #if NCY > 0 d_open_t cyopen; d_close_t cyclose; d_read_t cyread; d_write_t cywrite; d_ioctl_t cyioctl; d_stop_t cystop; -d_select_t cyselect; d_ttycv_t cydevtotty; #define cyreset nxreset #define cymmap nxmmap #define cystrategy nxstrategy #else #define cyopen nxopen #define cyclose nxclose #define cyread nxread #define cywrite nxwrite #define cyioctl nxioctl #define cystop nxstop #define cyreset nxreset -#define cyselect nxselect #define cymmap nxmmap #define cystrategy nxstrategy #define cydevtotty nxdevtotty #endif #include "ity.h" #if NITY > 0 d_open_t ityopen; d_close_t ityclose; d_read_t ityread; d_write_t itywrite; d_ioctl_t ityioctl; -d_select_t ityselect; d_ttycv_t itydevtotty; #define ityreset nxreset #else #define ityopen nxopen #define ityclose nxclose #define ityread nxread #define itywrite nxwrite #define ityioctl nxioctl #define ityreset nxreset -#define ityselect nxselect #define itydevtotty nxdevtotty #endif #include "nic.h" #if NNIC > 0 d_open_t nicopen; d_close_t nicclose; d_ioctl_t nicioctl; #else #define nicopen nxopen #define nicclose nxclose #define nicioctl nxioctl #endif #include "nnic.h" #if NNNIC > 0 d_open_t nnicopen; d_close_t nnicclose; d_ioctl_t nnicioctl; #else #define nnicopen nxopen #define nnicclose nxclose #define nnicioctl nxioctl #endif #include "isdn.h" #if NISDN > 0 d_open_t isdnopen; d_close_t isdnclose; d_read_t isdnread; d_ioctl_t isdnioctl; #else #define isdnopen nxopen #define isdnclose nxclose #define isdnread nxread #define isdnioctl nxioctl #endif #include "itel.h" #if NITEL > 0 d_open_t itelopen; d_close_t itelclose; d_read_t itelread; d_write_t itelwrite; d_ioctl_t itelioctl; #else #define itelopen nxopen #define itelclose nxclose #define itelread nxread #define itelwrite nxwrite #define itelioctl nxioctl #endif #include "ispy.h" #if NISPY > 0 d_open_t ispyopen; d_close_t ispyclose; d_read_t ispyread; d_write_t ispywrite; d_ioctl_t ispyioctl; #else #define ispyopen nxopen #define ispyclose nxclose #define ispyread nxread #define ispywrite nxwrite #define ispyioctl nxioctl #endif /* open, close, read, write, ioctl, stop, reset, ttys, select, mmap, strat */ struct cdevsw cdevsw[] = { { cnopen, cnclose, cnread, cnwrite, /*0*/ cnioctl, nullstop, nullreset, nodevtotty,/* console */ cnselect, nommap, NULL }, { cttyopen, nullclose, cttyread, cttywrite, /*1*/ cttyioctl, nullstop, nullreset, nodevtotty,/* tty */ cttyselect, nommap, NULL }, { mmopen, mmclose, mmrw, mmrw, /*2*/ noioc, nullstop, nullreset, nodevtotty,/* memory */ mmselect, memmmap, NULL }, { wdopen, wdclose, rawread, rawwrite, /*3*/ wdioctl, nostop, nullreset, nodevtotty,/* wd */ seltrue, nommap, wdstrategy }, { nullopen, nullclose, rawread, rawwrite, /*4*/ noioc, nostop, noreset, nodevtotty,/* swap */ noselect, nommap, swstrategy }, { ptsopen, ptsclose, ptsread, ptswrite, /*5*/ ptyioctl, ptsstop, nullreset, ptydevtotty,/* ttyp */ - ptsselect, nommap, NULL }, + ttselect, nommap, NULL }, { ptcopen, ptcclose, ptcread, ptcwrite, /*6*/ ptyioctl, nullstop, nullreset, ptydevtotty,/* ptyp */ ptcselect, nommap, NULL }, { logopen, logclose, logread, nowrite, /*7*/ logioctl, nostop, nullreset, nodevtotty,/* klog */ logselect, nommap, NULL }, { bquopen, bquclose, bquread, bquwrite, /*8*/ bquioctl, nostop, nullreset, nodevtotty,/* tputer */ bquselect, nommap, NULL }, { Fdopen, fdclose, rawread, rawwrite, /*9*/ fdioctl, nostop, nullreset, nodevtotty,/* Fd (!=fd) */ seltrue, nommap, fdstrategy }, { wtopen, wtclose, rawread, rawwrite, /*10*/ wtioctl, nostop, nullreset, nodevtotty,/* wt */ seltrue, nommap, wtstrategy }, { spigot_open, spigot_close, spigot_read, spigot_write, /*11*/ spigot_ioctl, nostop, nullreset, nodevtotty,/* Spigot */ spigot_select, spigot_mmap, NULL }, { scopen, scclose, scread, scwrite, /*12*/ scioctl, nullstop, nullreset, scdevtotty,/* sc */ - scselect, scmmap, NULL }, + ttselect, scmmap, NULL }, { sdopen, sdclose, rawread, rawwrite, /*13*/ sdioctl, nostop, nullreset, nodevtotty,/* sd */ seltrue, nommap, sdstrategy }, { stopen, stclose, rawread, rawwrite, /*14*/ stioctl, nostop, nullreset, nodevtotty,/* st */ seltrue, nommap, ststrategy }, { cdopen, cdclose, rawread, nowrite, /*15*/ cdioctl, nostop, nullreset, nodevtotty,/* cd */ seltrue, nommap, cdstrategy }, { lptopen, lptclose, noread, lptwrite, /*16*/ lptioctl, nullstop, nullreset, nodevtotty,/* lpt */ seltrue, nommap, nostrat}, { chopen, chclose, noread, nowrite, /*17*/ chioctl, nostop, nullreset, nodevtotty,/* ch */ noselect, nommap, nostrat }, { suopen, suclose, suread, suwrite, /*18*/ suioctl, nostop, nullreset, nodevtotty,/* scsi */ suselect, summap, sustrategy }, /* 'generic' */ { twopen, twclose, twread, twwrite, /*19*/ noioc, nullstop, nullreset, nodevtotty,/* tw */ twselect, nommap, nostrat }, /* * If you need a cdev major number for a driver that you intend to donate * back to the group or release publically, please contact the FreeBSD team * by sending mail to "hackers@freebsd.org". * If you assign one yourself it may conflict with someone else. * Otherwise, simply use the one reserved for local use. */ /* character device 20 is reserved for local use */ { nxopen, nxclose, nxread, /*20*/ nxwrite, nxioctl, nxstop, nxreset, nxdevtotty, nxselect, nxmmap, NULL }, { psmopen, psmclose, psmread, nowrite, /*21*/ psmioctl, nostop, nullreset, nodevtotty,/* psm mice */ psmselect, nommap, NULL }, { fdopen, noclose, noread, nowrite, /*22*/ noioc, nostop, nullreset, nodevtotty,/* fd (!=Fd) */ noselect, nommap, nostrat }, { bpfopen, bpfclose, bpfread, bpfwrite, /*23*/ bpfioctl, nostop, nullreset, nodevtotty,/* bpf */ bpfselect, nommap, NULL }, { pcaopen, pcaclose, noread, pcawrite, /*24*/ pcaioctl, nostop, nullreset, nodevtotty,/* pcaudio */ pcaselect, nommap, NULL }, { vaopen, vaclose, varead, vawrite, /*25*/ vaioctl, nostop, nullreset, nodevtotty,/* vat */ vaselect, nommap, NULL }, { spkropen, spkrclose, noread, spkrwrite, /*26*/ spkrioctl, nostop, nullreset, nodevtotty,/* spkr */ seltrue, nommap, NULL }, { mseopen, mseclose, mseread, nowrite, /*27*/ noioc, nostop, nullreset, nodevtotty,/* mse */ mseselect, nommap, NULL }, { sioopen, sioclose, sioread, siowrite, /*28*/ sioioctl, siostop, sioreset, siodevtotty,/* sio */ - sioselect, nommap, NULL }, + ttselect, nommap, NULL }, { mcdopen, mcdclose, rawread, nowrite, /*29*/ mcdioctl, nostop, nullreset, nodevtotty,/* mitsumi cd */ seltrue, nommap, mcdstrategy }, { sndopen, sndclose, sndread, sndwrite, /*30*/ sndioctl, nostop, nullreset, nodevtotty,/* sound */ sndselect, nommap, NULL }, { ukopen, ukclose, noread, nowrite, /*31*/ ukioctl, nostop, nullreset, nodevtotty,/* unknown */ seltrue, nommap, NULL }, /* scsi */ { lkmcopen, lkmcclose, noread, nowrite, /*32*/ lkmcioctl, nostop, nullreset, nodevtotty, noselect, nommap, NULL }, { lkmopen, lkmclose, lkmread, lkmwrite, /*33*/ lkmioctl, lkmstop, lkmreset, nodevtotty, lkmselect, lkmmmap, NULL }, { lkmopen, lkmclose, lkmread, lkmwrite, /*34*/ lkmioctl, lkmstop, lkmreset, nodevtotty, lkmselect, lkmmmap, NULL }, { lkmopen, lkmclose, lkmread, lkmwrite, /*35*/ lkmioctl, lkmstop, lkmreset, nodevtotty, lkmselect, lkmmmap, NULL }, { lkmopen, lkmclose, lkmread, lkmwrite, /*36*/ lkmioctl, lkmstop, lkmreset, nodevtotty, lkmselect, lkmmmap, NULL }, { lkmopen, lkmclose, lkmread, lkmwrite, /*37*/ lkmioctl, lkmstop, lkmreset, nodevtotty, lkmselect, lkmmmap, NULL }, { lkmopen, lkmclose, lkmread, lkmwrite, /*38*/ lkmioctl, lkmstop, lkmreset, nodevtotty, lkmselect, lkmmmap, NULL }, { apmopen, apmclose, noread, nowrite, /*39*/ apmioctl, nostop, nullreset, nodevtotty,/* APM */ seltrue, nommap, NULL }, { ctxopen, ctxclose, ctxread, ctxwrite, /*40*/ ctxioctl, nostop, nullreset, nodevtotty,/* cortex */ seltrue, nommap, NULL }, { sockopen, sockclose, noread, nowrite, /*41*/ sockioctl, nostop, nullreset, nodevtotty,/* socksys */ seltrue, nommap, NULL }, { cxopen, cxclose, cxread, cxwrite, /*42*/ - cxioctl, cxstop, nullreset, nodevtotty,/* cronyx */ + cxioctl, cxstop, nullreset, cxdevtotty,/* cronyx */ cxselect, nommap, NULL }, { vnopen, vnclose, rawread, rawwrite, /*43*/ vnioctl, nostop, nullreset, nodevtotty,/* vn */ seltrue, nommap, vnstrategy }, { gpopen, gpclose, noread, gpwrite, /*44*/ gpioctl, nostop, nullreset, nodevtotty,/* GPIB */ seltrue, nommap, NULL }, { scdopen, scdclose, rawread, nowrite, /*45*/ scdioctl, nostop, nullreset, nodevtotty,/* sony cd */ seltrue, nommap, scdstrategy }, { pcdopen, pcdclose, rawread, nowrite, /*46*/ pcdioctl, nostop, nullreset, nodevtotty,/* pana cd */ seltrue, nommap, pcdstrategy }, { gscopen, gscclose, gscread, nowrite, /*47*/ gscioctl, nostop, nullreset, nodevtotty,/* gsc */ seltrue, nommap, NULL }, { cyopen, cyclose, cyread, cywrite, /*48*/ cyioctl, cystop, cyreset, cydevtotty,/*cyclades*/ - cyselect, cymmap, cystrategy }, + ttselect, cymmap, cystrategy }, { sscopen, sscclose, sscread, sscwrite, /*49*/ sscioctl, nostop, nullreset, nodevtotty,/* scsi super */ sscselect, sscmmap, sscstrategy }, { nxopen, nxclose, nxread, nxwrite, /*50*/ nxioctl, nxstop, nxreset, nodevtotty,/* pcmcia */ nxselect, nxmmap, NULL }, { joyopen, joyclose, joyread, nowrite, /*51*/ joyioctl, nostop, nullreset, nodevtotty,/*joystick */ seltrue, nommap, NULL}, { tunopen, tunclose, tunread, tunwrite, /*52*/ tunioctl, nostop, nullreset, nodevtotty,/* tunnel */ tunselect, nommap, NULL }, { snpopen, snpclose, snpread, snpwrite, /*53*/ snpioctl, nostop, nullreset, nodevtotty,/* snoop */ snpselect, nommap, NULL }, { nicopen, nicclose, noread, nowrite, /*54*/ nicioctl, nostop, nullreset, nodevtotty,/* nic */ seltrue, nommap, NULL }, { isdnopen, isdnclose, isdnread, nowrite, /*55*/ isdnioctl, nostop, nullreset, nodevtotty,/* isdn */ seltrue, nommap, NULL }, { ityopen, ityclose, ityread, itywrite, /*56*/ ityioctl, nostop, ityreset, itydevtotty,/* ity */ - ityselect, nommap, NULL }, + ttselect, nommap, NULL }, { itelopen, itelclose, itelread, itelwrite, /*57*/ itelioctl, nostop, nullreset, nodevtotty,/* itel */ seltrue, nommap, NULL }, { nxopen, nxclose, nxread, nxwrite, /*58*/ nxioctl, nxstop, nxreset, nxdevtotty,/* unused */ seltrue, nxmmap, NULL }, { ispyopen, ispyclose, ispyread, nowrite, /*59*/ ispyioctl, nostop, nullreset, nodevtotty,/* ispy */ seltrue, nommap, NULL }, { nnicopen, nnicclose, noread, nowrite, /*60*/ nnicioctl, nostop, nullreset, nodevtotty,/* nnic */ seltrue, nommap, NULL }, }; int nchrdev = sizeof (cdevsw) / sizeof (cdevsw[0]); /* * Swapdev is a fake device implemented * in sw.c used only internally to get to swstrategy. * It cannot be provided to the users, because the * swstrategy routine munches the b_dev and b_blkno entries * before calling the appropriate driver. This would horribly * confuse, e.g. the hashing routines. Instead, /dev/drum is * provided as a character (raw) device. */ dev_t swapdev = makedev(1, 0); /* * Routine that identifies /dev/mem and /dev/kmem. * * A minimal stub routine can always return 0. */ int iskmemdev(dev) dev_t dev; { return (major(dev) == 2 && (minor(dev) == 0 || minor(dev) == 1)); } int iszerodev(dev) dev_t dev; { return (major(dev) == 2 && minor(dev) == 12); } /* * Routine to determine if a device is a disk. * * A minimal stub routine can always return 0. */ int isdisk(dev, type) dev_t dev; int type; { switch (major(dev)) { case 15: return (1); case 0: case 2: case 4: case 6: case 7: if (type == VBLK) return (1); return (0); case 3: case 9: case 13: case 29: case 43: if (type == VCHR) return (1); /* fall through */ default: return (0); } /* NOTREACHED */ } /* * Routine to convert from character to block device number. * * A minimal stub routine can always return NODEV. */ dev_t chrtoblk(dev) dev_t dev; { int blkmaj; switch (major(dev)) { case 3: blkmaj = 0; break; case 9: blkmaj = 2; break; case 10: blkmaj = 3; break; case 13: blkmaj = 4; break; case 14: blkmaj = 5; break; case 15: blkmaj = 6; break; case 29: blkmaj = 7; break; case 43: blkmaj = 15; break; default: return (NODEV); } return (makedev(blkmaj, minor(dev))); } Index: head/sys/i386/isa/cx.c =================================================================== --- head/sys/i386/isa/cx.c (revision 6781) +++ head/sys/i386/isa/cx.c (revision 6782) @@ -1,892 +1,909 @@ /* * Cronyx-Sigma adapter driver for FreeBSD. * Supports PPP/HDLC protocol in synchronous mode, * and asyncronous channels with full modem control. * * Copyright (C) 1994 Cronyx Ltd. * Author: Serge Vakulenko, * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Authors grant any other persons or organisations permission to use * or modify this software as long as this message is kept with the software, * all derivative works or modified versions. * * Version 1.2, Tue Nov 22 18:57:27 MSK 1994 */ #undef DEBUG #include "cx.h" #if NCX > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ # if __FreeBSD__ < 2 # include # define RB_GETC(q) getc(q) # else /* BSD 4.4 Lite */ # include # endif # define oproc_func_t void(*)(struct tty*) #endif #ifdef __bsdi__ # include # include # define tsleep(tp,pri,msg,x) ((tp)->t_state |= TS_WOPEN,\ ttysleep (tp, (caddr_t)&tp->t_rawq, pri, msg, x)) # define oproc_func_t int(*)() # define timeout_func_t void(*)() #endif #if !defined (__FreeBSD__) || __FreeBSD__ >= 2 # define t_out t_outq # define RB_LEN(q) ((q).c_cc) # define RB_GETC(q) getc(&q) # define TSA_CARR_ON(tp) tp # define TSA_OLOWAT(q) ((caddr_t)&(q)->t_out) #endif #include #include #ifdef DEBUG # define print(s) printf s #else # define print(s) /*void*/ #endif #define DMABUFSZ (6*256) /* buffer size */ #define BYTE *(unsigned char*)& #define UNIT(u) ((u) & 077) #define UNIT_CTL 077 extern cx_board_t cxboard [NCX]; /* adapter state structures */ extern cx_chan_t *cxchan [NCX*NCHAN]; /* unit to channel struct pointer */ #if __FreeBSD__ >= 2 extern struct kern_devconf kdc_cx [NCX]; struct tty cx_tty [NCX*NCHAN]; /* tty data */ #else struct tty *cx_tty [NCX*NCHAN]; /* tty data */ #endif void cxoproc (struct tty *tp); int cxparam (struct tty *tp, struct termios *t); void cxswitch (cx_chan_t *c, cx_soft_opt_t new); int cxopen (dev_t dev, int flag, int mode, struct proc *p) { int unit = UNIT (dev); cx_chan_t *c = cxchan[unit]; unsigned short port; struct tty *tp; int error = 0; if (unit == UNIT_CTL) { print (("cx: cxopen /dev/cronyx\n")); return (0); } if (unit >= NCX*NCHAN || !c || c->type==T_NONE) return (ENXIO); port = c->chip->port; print (("cx%d.%d: cxopen unit=%d\n", c->board->num, c->num, unit)); if (c->mode != M_ASYNC) return (EBUSY); if (! c->ttyp) { #if __FreeBSD__ >= 2 c->ttyp = &cx_tty[unit]; #else MALLOC (cx_tty[unit], struct tty*, sizeof (struct tty), M_DEVBUF, M_WAITOK); bzero (cx_tty[unit], sizeof (*cx_tty[unit])); c->ttyp = cx_tty[unit]; #endif c->ttyp->t_oproc = (oproc_func_t) cxoproc; c->ttyp->t_param = cxparam; } #ifdef __bsdi__ if (! c->ttydev) { MALLOC (c->ttydev, struct ttydevice_tmp*, sizeof (struct ttydevice_tmp), M_DEVBUF, M_WAITOK); bzero (c->ttydev, sizeof (*c->ttydev)); strcpy (c->ttydev->tty_name, "cx"); c->ttydev->tty_unit = unit; c->ttydev->tty_base = unit; c->ttydev->tty_count = 1; c->ttydev->tty_ttys = c->ttyp; tty_attach (c->ttydev); } #endif tp = c->ttyp; tp->t_dev = dev; if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE) && p->p_ucred->cr_uid != 0) return (EBUSY); if (! (tp->t_state & TS_ISOPEN)) { ttychars (tp); if (tp->t_ispeed == 0) { #ifdef __bsdi__ tp->t_termios = deftermios; #else tp->t_iflag = 0; tp->t_oflag = 0; tp->t_lflag = 0; tp->t_cflag = CREAD | CS8 | HUPCL; tp->t_ispeed = c->rxbaud; tp->t_ospeed = c->txbaud; #endif } cxparam (tp, &tp->t_termios); ttsetwater (tp); } spltty (); if (! (tp->t_state & TS_ISOPEN)) { /* * Compute optimal receiver buffer length. * The best choice is rxbaud/400. * Make it even, to avoid byte-wide DMA transfers. * -------------------------- * Baud rate Buffer length * -------------------------- * 300 4 * 1200 4 * 9600 24 * 19200 48 * 38400 96 * 57600 192 * 115200 288 * -------------------------- */ int rbsz = (c->rxbaud + 800 - 1) / 800 * 2; if (rbsz < 4) rbsz = 4; else if (rbsz > DMABUFSZ) rbsz = DMABUFSZ; /* Initialize channel, enable receiver. */ cx_cmd (port, CCR_INITCH | CCR_ENRX); cx_cmd (port, CCR_INITCH | CCR_ENRX); /* Start receiver. */ outw (ARBCNT(port), rbsz); outw (BRBCNT(port), rbsz); outw (ARBSTS(port), BSTS_OWN24); outw (BRBSTS(port), BSTS_OWN24); /* Enable interrupts. */ outb (IER(port), IER_RXD | IER_RET | IER_TXD | IER_MDM); cx_chan_dtr (c, 1); cx_chan_rts (c, 1); } if (cx_chan_cd (c)) tp->t_state |= TS_CARR_ON; if (! (flag & O_NONBLOCK)) { /* Lock the channel against cxconfig while we are * waiting for carrier. */ c->sopt.lock = 1; while (!(tp->t_cflag & CLOCAL) && !(tp->t_state & TS_CARR_ON)) if ((error = tsleep (TSA_CARR_ON(tp), TTIPRI | PCATCH, "cxdcd", 0))) break; c->sopt.lock = 0; /* Unlock the channel. */ } print (("cx%d.%d: cxopen done csr=%b\n", c->board->num, c->num, inb(CSR(c->chip->port)), CSRA_BITS)); spl0 (); if (error) return (error); #if __FreeBSD__ >= 2 error = (*linesw[tp->t_line].l_open) (dev, tp); if (tp->t_state & TS_ISOPEN) /* Mark the board busy on the first startup. * Never goes idle. */ kdc_cx[c->board->num].kdc_state = DC_BUSY; #else error = (*linesw[tp->t_line].l_open) (dev, tp, 0); #endif return (error); } int cxclose (dev_t dev, int flag, int mode, struct proc *p) { int unit = UNIT (dev); cx_chan_t *c = cxchan[unit]; struct tty *tp; int s; if (unit == UNIT_CTL) return (0); tp = c->ttyp; (*linesw[tp->t_line].l_close) (tp, flag); /* Disable receiver. * Transmitter continues sending the queued data. */ s = spltty (); outb (CAR(c->chip->port), c->num & 3); outb (IER(c->chip->port), IER_TXD | IER_MDM); cx_cmd (c->chip->port, CCR_DISRX); /* Clear DTR and RTS. */ if ((tp->t_cflag & HUPCL) || ! (tp->t_state & TS_ISOPEN)) { cx_chan_dtr (c, 0); cx_chan_rts (c, 0); } /* Stop sending break. */ if (c->brk == BRK_SEND) { c->brk = BRK_STOP; if (! (tp->t_state & TS_BUSY)) cxoproc (tp); } splx (s); ttyclose (tp); return (0); } int cxread (dev_t dev, struct uio *uio, int flag) { int unit = UNIT (dev); struct tty *tp; if (unit == UNIT_CTL) return (EIO); tp = cxchan[unit]->ttyp; return ((*linesw[tp->t_line].l_read) (tp, uio, flag)); } int cxwrite (dev_t dev, struct uio *uio, int flag) { int unit = UNIT (dev); struct tty *tp; if (unit == UNIT_CTL) return (EIO); tp = cxchan[unit]->ttyp; return ((*linesw[tp->t_line].l_write) (tp, uio, flag)); } int cxioctl (dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { int unit = UNIT (dev); cx_chan_t *c; struct tty *tp; int error, s; unsigned char msv; if (unit == UNIT_CTL) { /* Process an ioctl request on /dev/cronyx */ cx_options_t *o = (cx_options_t*) data; if (o->board >= NCX || o->channel >= NCHAN) return (EINVAL); c = &cxboard[o->board].chan[o->channel]; if (c->type == T_NONE) return (ENXIO); switch (cmd) { default: return (EINVAL); case CXIOCSETMODE: print (("cx%d.%d: CXIOCSETMODE\n", o->board, o->channel)); if (c->type == T_NONE) return (EINVAL); if (c->type == T_ASYNC && o->mode != M_ASYNC) return (EINVAL); if (o->mode == M_ASYNC) switch (c->type) { case T_SYNC_RS232: case T_SYNC_V35: case T_SYNC_RS449: return (EINVAL); } /* Somebody is waiting for carrier? */ if (c->sopt.lock) return (EBUSY); /* /dev/ttyXX is already opened by someone? */ if (c->mode == M_ASYNC && c->ttyp && (c->ttyp->t_state & TS_ISOPEN)) return (EBUSY); /* Network interface is up? */ if (c->mode != M_ASYNC && (c->ifp->if_flags & IFF_UP)) return (EBUSY); c->mode = o->mode; c->rxbaud = o->rxbaud; c->txbaud = o->txbaud; c->opt = o->opt; c->aopt = o->aopt; c->hopt = o->hopt; c->bopt = o->bopt; c->xopt = o->xopt; switch (c->num) { case 0: c->board->if0type = o->iftype; break; case 8: c->board->if8type = o->iftype; break; } cxswitch (c, o->sopt); s = spltty (); cx_setup_chan (c); outb (IER(c->chip->port), 0); splx (s); break; case CXIOCGETMODE: print (("cx%d.%d: CXIOCGETMODE\n", o->board, o->channel)); o->type = c->type; o->mode = c->mode; o->rxbaud = c->rxbaud; o->txbaud = c->txbaud; o->opt = c->opt; o->aopt = c->aopt; o->hopt = c->hopt; o->bopt = c->bopt; o->xopt = c->xopt; o->sopt = c->sopt; switch (c->num) { case 0: o->iftype = c->board->if0type; break; case 8: o->iftype = c->board->if8type; break; } break; } return (0); } c = cxchan[unit]; tp = c->ttyp; if (! tp) return (EINVAL); #if __FreeBSD__ >= 2 error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag, p); #else error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag); #endif if (error >= 0) return (error); error = ttioctl (tp, cmd, data, flag); if (error >= 0) return (error); s = spltty (); switch (cmd) { default: splx (s); return (ENOTTY); case TIOCSBRK: /* Start sending line break */ c->brk = BRK_SEND; if (! (tp->t_state & TS_BUSY)) cxoproc (tp); break; case TIOCCBRK: /* Stop sending line break */ c->brk = BRK_STOP; if (! (tp->t_state & TS_BUSY)) cxoproc (tp); break; case TIOCSDTR: /* Set DTR */ cx_chan_dtr (c, 1); break; case TIOCCDTR: /* Clear DTR */ cx_chan_dtr (c, 0); break; case TIOCMSET: /* Set DTR/RTS */ cx_chan_dtr (c, (*(int*)data & TIOCM_DTR) ? 1 : 0); cx_chan_rts (c, (*(int*)data & TIOCM_RTS) ? 1 : 0); break; case TIOCMBIS: /* Add DTR/RTS */ if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 1); if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 1); break; case TIOCMBIC: /* Clear DTR/RTS */ if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 0); if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 0); break; case TIOCMGET: /* Get modem status */ msv = inb (MSVR(c->chip->port)); *(int*)data = TIOCM_LE; /* always enabled while open */ if (msv & MSV_DSR) *(int*)data |= TIOCM_DSR; if (msv & MSV_CTS) *(int*)data |= TIOCM_CTS; if (msv & MSV_CD) *(int*)data |= TIOCM_CD; if (c->dtr) *(int*)data |= TIOCM_DTR; if (c->rts) *(int*)data |= TIOCM_RTS; break; } splx (s); return (0); } /* * Fill transmitter buffer with data. */ void cxout (cx_chan_t *c, char b) { unsigned char *buf, *p, sym; unsigned short port = c->chip->port, len = 0, cnt_port, sts_port; struct tty *tp = c->ttyp; int i; if (! tp) return; /* Choose the buffer. */ if (b == 'A') { buf = c->atbuf; cnt_port = ATBCNT(port); sts_port = ATBSTS(port); } else { buf = c->btbuf; cnt_port = BTBCNT(port); sts_port = BTBSTS(port); } /* Is it busy? */ if (inb (sts_port) & BSTS_OWN24) { tp->t_state |= TS_BUSY; return; } switch (c->brk) { case BRK_SEND: *buf++ = 0; /* extended transmit command */ *buf++ = 0x81; /* send break */ *buf++ = 0; /* extended transmit command */ *buf++ = 0x82; /* insert delay */ *buf++ = 250; /* 1/4 of second */ *buf++ = 0; /* extended transmit command */ *buf++ = 0x82; /* insert delay */ *buf++ = 250; /* + 1/4 of second */ len = 8; c->brk = BRK_IDLE; break; case BRK_STOP: *buf++ = 0; /* extended transmit command */ *buf++ = 0x83; /* stop break */ len = 2; c->brk = BRK_IDLE; break; case BRK_IDLE: len = RB_LEN (tp->t_out); if (tp->t_iflag & IXOFF) for (i=0, p=buf; it_out); /* Send XON/XOFF out of band. */ if (sym == tp->t_cc[VSTOP]) { outb (STCR(port), STC_SNDSPC|STC_SSPC_2); continue; } if (sym == tp->t_cc[VSTART]) { outb (STCR(port), STC_SNDSPC|STC_SSPC_1); continue; } /* Duplicate NULLs in ETC mode. */ if (! sym) *p++ = 0; *p++ = sym; } else for (i=0, p=buf; it_out); /* Duplicate NULLs in ETC mode. */ if (! sym) *p++ = 0; *p++ = sym; } len = p - buf; break; } /* Start transmitter. */ if (len) { outw (cnt_port, len); outb (sts_port, BSTS_INTR | BSTS_OWN24); tp->t_state |= TS_BUSY; print (("cx%d.%d: out %d bytes to %c\n", c->board->num, c->num, len, b)); } } void cxoproc (struct tty *tp) { int unit = UNIT (tp->t_dev); cx_chan_t *c = cxchan[unit]; unsigned short port = c->chip->port; int s = spltty (); /* Set current channel number */ outb (CAR(port), c->num & 3); if (! (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))) { /* Start transmitter. */ if (! (inb (CSR(port)) & CSRA_TXEN)) cx_cmd (port, CCR_ENTX); /* Determine the buffer order. */ if (inb (DMABSTS(port)) & DMABSTS_NTBUF) { cxout (c, 'B'); cxout (c, 'A'); } else { cxout (c, 'A'); cxout (c, 'B'); } } #if defined (__FreeBSD__) && __FreeBSD__ < 2 if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) ttwwakeup (tp); #else /* FreeBSD 2.x and BSDI */ if (RB_LEN (tp->t_out) <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } selwakeup(&tp->t_wsel); } #endif /* * Enable TXMPTY interrupt, * to catch the case when the second buffer is empty. */ if ((inb (ATBSTS(port)) & BSTS_OWN24) && (inb (BTBSTS(port)) & BSTS_OWN24)) { outb (IER(port), IER_RXD|IER_RET|IER_TXD|IER_TXMPTY|IER_MDM); } else outb (IER(port), IER_RXD|IER_RET|IER_TXD|IER_MDM); splx (s); } int cxparam (struct tty *tp, struct termios *t) { int unit = UNIT (tp->t_dev); cx_chan_t *c = cxchan[unit]; unsigned short port = c->chip->port; int clock, period, s; cx_cor1_async_t cor1; if (t->c_ospeed == 0) { /* Clear DTR and RTS. */ s = spltty (); cx_chan_dtr (c, 0); cx_chan_rts (c, 0); splx (s); print (("cx%d.%d: cxparam (hangup)\n", c->board->num, c->num)); return (0); } print (("cx%d.%d: cxparam\n", c->board->num, c->num)); /* Check requested parameters. */ if (t->c_ospeed < 300 || t->c_ospeed > 256*1024) return(EINVAL); if (t->c_ispeed && (t->c_ispeed < 300 || t->c_ispeed > 256*1024)) return(EINVAL); #ifdef __bsdi__ /* CLOCAL flag set -- wakeup everybody who waits for CD. */ /* FreeBSD does this themselves. */ if (! (tp->t_cflag & CLOCAL) && (t->c_cflag & CLOCAL)) wakeup ((caddr_t) &tp->t_rawq); #endif /* And copy them to tty and channel structures. */ c->rxbaud = tp->t_ispeed = t->c_ispeed; c->txbaud = tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; /* Set character length and parity mode. */ BYTE cor1 = 0; switch (t->c_cflag & CSIZE) { default: case CS8: cor1.charlen = 7; break; case CS7: cor1.charlen = 6; break; case CS6: cor1.charlen = 5; break; case CS5: cor1.charlen = 4; break; } if (t->c_cflag & PARENB) { cor1.parmode = PARM_NORMAL; cor1.ignpar = 0; cor1.parity = (t->c_cflag & PARODD) ? PAR_ODD : PAR_EVEN; } else { cor1.parmode = PARM_NOPAR; cor1.ignpar = 1; } /* Enable/disable hardware CTS. */ c->aopt.cor2.ctsae = (t->c_cflag & CRTSCTS) ? 1 : 0; /* Handle DSR as CTS. */ c->aopt.cor2.dsrae = (t->c_cflag & CRTSCTS) ? 1 : 0; /* Enable extended transmit command mode. * Unfortunately, there is no other method for sending break. */ c->aopt.cor2.etc = 1; /* Enable/disable hardware XON/XOFF. */ c->aopt.cor2.ixon = (t->c_iflag & IXON) ? 1 : 0; c->aopt.cor2.ixany = (t->c_iflag & IXANY) ? 1 : 0; /* Set the number of stop bits. */ if (t->c_cflag & CSTOPB) c->aopt.cor3.stopb = STOPB_2; else c->aopt.cor3.stopb = STOPB_1; /* Disable/enable passing XON/XOFF chars to the host. */ c->aopt.cor3.scde = (t->c_iflag & IXON) ? 1 : 0; c->aopt.cor3.flowct = (t->c_iflag & IXON) ? FLOWCC_NOTPASS : FLOWCC_PASS; c->aopt.schr1 = t->c_cc[VSTART]; /* XON */ c->aopt.schr2 = t->c_cc[VSTOP]; /* XOFF */ /* Set current channel number. */ s = spltty (); outb (CAR(port), c->num & 3); /* Set up receiver clock values. */ cx_clock (c->chip->oscfreq, c->rxbaud, &clock, &period); c->opt.rcor.clk = clock; outb (RCOR(port), BYTE c->opt.rcor); outb (RBPR(port), period); /* Set up transmitter clock values. */ cx_clock (c->chip->oscfreq, c->txbaud, &clock, &period); c->opt.tcor.clk = clock; c->opt.tcor.ext1x = 0; outb (TCOR(port), BYTE c->opt.tcor); outb (TBPR(port), period); outb (COR2(port), BYTE c->aopt.cor2); outb (COR3(port), BYTE c->aopt.cor3); outb (SCHR1(port), c->aopt.schr1); outb (SCHR2(port), c->aopt.schr2); if (BYTE c->aopt.cor1 != BYTE cor1) { BYTE c->aopt.cor1 = BYTE cor1; outb (COR1(port), BYTE c->aopt.cor1); /* Any change to COR1 require reinitialization. */ /* Unfortunately, it may cause transmitter glitches... */ cx_cmd (port, CCR_INITCH); } splx (s); return (0); } + +struct tty *cxdevtotty (dev_t dev) +{ + int unit = UNIT(dev); + + if (unit == UNIT_CTL) + return (NULL); + + if (unit > NCX*NCHAN) + return (NULL); + + return (cxchan[unit]->ttyp); +} int cxselect (dev_t dev, int flag, struct proc *p) { int unit = UNIT (dev); if (unit == UNIT_CTL) return (0); - return (ttyselect (cxchan[unit]->ttyp, flag, p)); + + if (unit > NCX*NCHAN) + return (ENXIO); + + return (ttyselect(cxchan[unit]->ttyp, flag, p)); } /* * Stop output on a line */ void cxstop (struct tty *tp, int flag) { cx_chan_t *c = cxchan[UNIT(tp->t_dev)]; unsigned short port = c->chip->port; int s = spltty (); if (tp->t_state & TS_BUSY) { print (("cx%d.%d: cxstop\n", c->board->num, c->num)); /* Set current channel number */ outb (CAR(port), c->num & 3); /* Stop transmitter */ cx_cmd (port, CCR_DISTX); } splx (s); } /* * Handle receive interrupts, including receive errors and * receive timeout interrupt. */ int cxrinta (cx_chan_t *c) { unsigned short port = c->chip->port; unsigned short len = 0, risr = inw (RISR(port)), reoir = 0; struct tty *tp = c->ttyp; /* Compute optimal receiver buffer length. */ int rbsz = (c->rxbaud + 800 - 1) / 800 * 2; if (rbsz < 4) rbsz = 4; else if (rbsz > DMABUFSZ) rbsz = DMABUFSZ; if (risr & RISA_TIMEOUT) { unsigned long rcbadr = (unsigned short) inw (RCBADRL(port)) | (long) inw (RCBADRU(port)) << 16; unsigned char *buf = 0; unsigned short cnt_port = 0, sts_port = 0; if (rcbadr >= c->brphys && rcbadr < c->brphys+DMABUFSZ) { buf = c->brbuf; len = rcbadr - c->brphys; cnt_port = BRBCNT(port); sts_port = BRBSTS(port); } else if (rcbadr >= c->arphys && rcbadr < c->arphys+DMABUFSZ) { buf = c->arbuf; len = rcbadr - c->arphys; cnt_port = ARBCNT(port); sts_port = ARBSTS(port); } else printf ("cx%d.%d: timeout: invalid buffer address\n", c->board->num, c->num); if (len) { print (("cx%d.%d: async receive timeout (%d bytes), risr=%b, arbsts=%b, brbsts=%b\n", c->board->num, c->num, len, risr, RISA_BITS, inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS)); if (tp && (tp->t_state & TS_ISOPEN)) { int i; void (*rint)() = (void(*)()) linesw[tp->t_line].l_rint; for (i=0; iboard->num, c->num, risr, RISA_BITS, inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS)); if (risr & RIS_BUSERR) printf ("cx%d.%d: receive bus error\n", c->board->num, c->num); if (risr & (RIS_OVERRUN | RISA_PARERR | RISA_FRERR)) { print (("cx%d.%d: receive error\n", c->board->num, c->num)); if (tp && (tp->t_state & TS_ISOPEN)) (*linesw[tp->t_line].l_rint) (' ' | (risr & RISA_FRERR) ? TTY_FE : TTY_PE, tp); } /* Handle line break condition. */ if ((risr & RISA_BREAK) && tp && (tp->t_state & TS_ISOPEN)) (*linesw[tp->t_line].l_rint) (TTY_FE, tp); /* Discard exception characters. */ if ((risr & RISA_SCMASK) && (tp->t_iflag & IXON)) reoir |= REOI_DISCEXC; /* Handle received data. */ if ((risr & RIS_EOBUF) && tp && (tp->t_state & TS_ISOPEN)) { void (*rint)() = (void(*)()) linesw[tp->t_line].l_rint; unsigned char *buf; int i; len = (risr & RIS_BB) ? inw(BRBCNT(port)) : inw(ARBCNT(port)); print (("cx%d.%d: async: %d bytes received\n", c->board->num, c->num, len)); buf = (risr & RIS_BB) ? c->brbuf : c->arbuf; for (i=0; ittyp; unsigned short port = c->chip->port; unsigned char tisr = inb (TISR(port)); print (("cx%d.%d: async transmit interrupt, tisr=%b, atbsts=%b, btbsts=%b\n", c->board->num, c->num, tisr, TIS_BITS, inb (ATBSTS(port)), BSTS_BITS, inb (BTBSTS(port)), BSTS_BITS)); if (tisr & TIS_BUSERR) printf ("cx%d.%d: transmit bus error\n", c->board->num, c->num); else if (tisr & TIS_UNDERRUN) printf ("cx%d.%d: transmit underrun error\n", c->board->num, c->num); if (tp) { tp->t_state &= ~(TS_BUSY | TS_FLUSH); if (tp->t_line) (*linesw[tp->t_line].l_start) (tp); else cxoproc (tp); } } /* * Handle modem interrupt. */ void cxmint (cx_chan_t *c) { unsigned short port = c->chip->port; unsigned char misr = inb (MISR(port)); unsigned char msvr = inb (MSVR(port)); struct tty *tp = c->ttyp; if (c->mode != M_ASYNC) { printf ("cx%d.%d: unexpected modem interrupt, misr=%b, msvr=%b\n", c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS); return; } print (("cx%d.%d: modem interrupt, misr=%b, msvr=%b\n", c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS)); /* Ignore DSR events. */ /* Ignore RTC/CTS events, handled by hardware. */ /* Handle carrier detect/loss. */ if (tp && (misr & MIS_CCD)) (*linesw[tp->t_line].l_modem) (tp, (msvr & MSV_CD) != 0); } /* * Recover after lost transmit interrupts. */ void cxtimeout (caddr_t a) { cx_board_t *b; cx_chan_t *c; struct tty *tp; int s; for (b=cxboard; bchan; cchan+NCHAN; ++c) { tp = c->ttyp; if (c->type==T_NONE || c->mode!=M_ASYNC || !tp) continue; s = spltty (); if (tp->t_state & TS_BUSY) { tp->t_state &= ~TS_BUSY; if (tp->t_line) (*linesw[tp->t_line].l_start) (tp); else cxoproc (tp); } splx (s); } timeout ((timeout_func_t) cxtimeout, 0, hz*5); } #endif /* NCX */ Index: head/sys/i386/isa/cy.c =================================================================== --- head/sys/i386/isa/cy.c (revision 6781) +++ head/sys/i386/isa/cy.c (revision 6782) @@ -1,1683 +1,1673 @@ /* * cyclades cyclom-y serial driver * Andrew Herbert , 17 August 1993 * * Copyright (c) 1993 Andrew Herbert. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name Andrew Herbert may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: cy.c,v 1.2 1995/02/15 18:41:41 bde Exp $ + * $Id: cy.c,v 1.3 1995/02/25 20:09:12 pst Exp $ */ /* * Device minor number encoding: * * c c x x u u u u - bits in the minor device number * * bits meaning * ---- ------- * uuuu physical serial line (i.e. unit) to use * 0-7 on a cyclom-8Y, 0-15 on a cyclom-16Y * xx unused * cc carrier control mode * 00 complete hardware carrier control of the tty. * DCD must be high for the open(2) to complete. * 01 dialin pseudo-device (not yet implemented) * 10 carrier ignored until a high->low transition * 11 carrier completed ignored */ /* * Known deficiencies: * * * no BREAK handling - breaks are ignored, and can't be sent either * * no support for bad-char reporting, except via PARMRK * * no support for dialin + dialout devices */ #include "cy.h" #if NCY > 0 /* This disgusing hack because we actually have 16 units on one controller */ #if NCY < 2 #undef NCY #define NCY (16) #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NetBSD #include #endif #include #include #include #define RxFifoThreshold 3 /* 3 characters (out of 12) in the receive * FIFO before an interrupt is generated */ #define FastRawInput /* bypass the regular char-by-char canonical input * processing whenever possible */ #define PollMode /* use polling-based irq service routine, not the * hardware svcack lines. Must be defined for * cyclom-16y boards. * * XXX cyclom-8y doesn't work without this defined * either (!) */ #define LogOverruns /* log receive fifo overruns */ #undef TxBuffer /* buffer driver output, to be slightly more * efficient * * XXX presently buggy */ #undef Smarts /* enable slightly more CD1400 intelligence. Mainly * the output CR/LF processing, plus we can avoid a * few checks usually done in ttyinput(). * * XXX not yet implemented, and not particularly * worthwhile either. */ #define CyDebug /* include debugging code (minimal effect on * performance) */ #define CY_RX_BUFS 2 /* two receive buffers per port */ #define CY_RX_BUF_SIZE 256 /* bytes per receive buffer */ #define CY_TX_BUF_SIZE 512 /* bytes per transmit buffer */ /* #define CD1400s_PER_CYCLOM 1 */ /* cyclom-4y */ /* #define CD1400s_PER_CYCLOM 2 */ /* cyclom-8y */ #define CD1400s_PER_CYCLOM 4 /* cyclom-16y */ /* FreeBSD's getty doesn't know option for setting RTS/CTS handshake. Its getty, like a lot of other old cruft, should be replaced with something which used POSIX tty interfaces which at least allow enabling it. In the meantime, use the force. */ #define ALWAYS_RTS_CTS 1 #if CD1400s_PER_CYCLOM < 4 #define CD1400_MEMSIZE 0x400 /* 4*256 bytes per chip: cyclom-[48]y */ #else #define CD1400_MEMSIZE 0x100 /* 256 bytes per chip: cyclom-16y */ /* XXX or is it 0x400 like the rest? */ #define CYCLOM_16 1 /* This is a cyclom-16Y */ #endif #define PORTS_PER_CYCLOM (CD1400_NO_OF_CHANNELS * CD1400s_PER_CYCLOM) #define CYCLOM_RESET_16 0x1400 /* cyclom-16y reset */ #define CYCLOM_CLEAR_INTR 0x1800 /* intr ack address */ #define CYCLOM_CLOCK 25000000 /* baud rate clock */ #define CY_UNITMASK 0x0f #define CY_CARRIERMASK 0xC0 #define CY_CARRIERSHIFT 6 #define UNIT(x) (minor(x) & CY_UNITMASK) #define CARRIER_MODE(x) ((minor(x) & CY_CARRIERMASK) >> CY_CARRIERSHIFT) typedef u_char * volatile cy_addr; int cyprobe(struct isa_device *dev); int cyattach(struct isa_device *isdp); void cystart(struct tty *tp); int cyparam(struct tty *tp, struct termios *t); int cyspeed(int speed, int *prescaler_io); static void cy_channel_init(dev_t dev, int reset); static void cd1400_channel_cmd(cy_addr base, u_char cmd); /* hsu@clinet.fi: sigh */ #ifdef __NetBSD__ #define DELAY(foo) delay(foo) void delay(int delay); #endif /* Better get rid of this until the core people agree on kernel interfaces. At least it will then compile on both WhichBSDs. */ #if 0 extern unsigned int delaycount; /* calibrated 1 ms cpu-spin delay */ #endif struct isa_driver cydriver = { cyprobe, cyattach, "cy" }; /* low-level ping-pong buffer structure */ struct cy_buf { u_char *next_char; /* location of next char to write */ u_int free; /* free chars remaining in buffer */ struct cy_buf *next_buf; /* circular, you know */ u_char buf[CY_RX_BUF_SIZE]; /* start of the buffer */ }; /* low-level ring buffer */ #ifdef TxBuffer struct cy_ring { u_char buf[CY_TX_BUF_SIZE]; u_char *head; u_char *tail; /* next pos. to insert char */ u_char *endish; /* physical end of buf */ u_int used; /* no. of chars in queue */ }; #endif /* * define a structure to keep track of each serial line */ struct cy { cy_addr base_addr; /* base address of this port's cd1400 */ struct tty *tty; u_int dtrwait; /* time (in ticks) to hold dtr low after close */ u_int recv_exception; /* exception chars received */ u_int recv_normal; /* normal chars received */ u_int xmit; /* chars transmitted */ u_int mdm; /* modem signal changes */ #ifdef CyDebug u_int start_count; /* no. of calls to cystart() */ u_int start_real; /* no. of calls that did something */ #endif u_char carrier_mode; /* hardware carrier handling mode */ /* * 0 = always use * 1 = always use (dialin port) * 2 = ignore during open, then use it * 3 = ignore completely */ u_char carrier_delta; /* true if carrier has changed state */ u_char fifo_overrun; /* true if cd1400 receive fifo has... */ u_char rx_buf_overrun; /* true if low-level buf overflow */ u_char intr_enable; /* CD1400 SRER shadow */ u_char modem_sig; /* CD1400 modem signal shadow */ u_char channel_control;/* CD1400 CCR control command shadow */ u_char cor[3]; /* CD1400 COR1-3 shadows */ #ifdef Smarts u_char spec_char[4]; /* CD1400 SCHR1-4 shadows */ #endif struct cy_buf *rx_buf; /* current receive buffer */ struct cy_buf rx_buf_pool[CY_RX_BUFS];/* receive ping-pong buffers */ #ifdef TxBuffer struct cy_ring tx_buf; /* transmit buffer */ #endif }; int cydefaultrate = TTYDEF_SPEED; cy_addr cyclom_base; /* base address of the card */ static struct cy *info[NCY*PORTS_PER_CYCLOM]; #ifdef __FreeBSD__ /* XXX actually only temporarily for 2.1-Development */ struct tty cy_tty[NCY*PORTS_PER_CYCLOM]; #else struct tty *cy_tty[NCY*PORTS_PER_CYCLOM]; #endif static volatile u_char timeout_scheduled = 0; /* true if a timeout has been scheduled */ #ifdef CyDebug u_int cy_svrr_probes = 0; /* debugging */ u_int cy_timeouts = 0; u_int cy_timeout_req = 0; #endif /**********************************************************************/ int cyprobe(struct isa_device *dev) { int i, j; u_char version = 0; /* firmware version */ /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ i = *(cy_addr)(dev->id_maddr + CYCLOM_RESET_16); DELAY(500); /* wait for the board to get its act together (500 us) */ for (i = 0; i < CD1400s_PER_CYCLOM; i++) { cy_addr base = dev->id_maddr + i * CD1400_MEMSIZE; /* wait for chip to become ready for new command */ for (j = 0; j < 100; j += 50) { DELAY(50); /* wait 50 us */ if (!*(base + CD1400_CCR)) break; } /* clear the GFRCR register */ *(base + CD1400_GFRCR) = 0; /* issue a reset command */ *(base + CD1400_CCR) = CD1400_CMD_RESET; /* wait for the CD1400 to initialise itself */ for (j = 0; j < 1000; j += 50) { DELAY(50); /* wait 50 us */ /* retrieve firmware version */ version = *(base + CD1400_GFRCR); if (version) break; } /* anything in the 40-4f range is fine */ if ((version & 0xf0) != 0x40) { return 0; } } return 1; /* found */ } int cyattach(struct isa_device *isdp) { /* u_char unit = UNIT(isdp->id_unit); */ int i, j, k; /* global variable used various routines */ cyclom_base = (cy_addr)isdp->id_maddr; for (i = 0, k = 0; i < CD1400s_PER_CYCLOM; i++) { cy_addr base = cyclom_base + i * CD1400_MEMSIZE; /* setup a 1ms clock tick */ *(base + CD1400_PPR) = CD1400_CLOCK_25_1MS; for (j = 0; j < CD1400_NO_OF_CHANNELS; j++, k++) { struct cy *ip; /* * grab some space. it'd be more polite to do this in cyopen(), * but hey. */ info[k] = ip = malloc(sizeof(struct cy), M_DEVBUF, M_WAITOK); /* clear all sorts of junk */ bzero(ip, sizeof(struct cy)); ip->base_addr = base; /* initialise the channel, without resetting it first */ cy_channel_init(k, 0); } } /* clear interrupts */ *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; return 1; } int cyopen(dev_t dev, int flag, int mode, struct proc *p) { u_int unit = UNIT(dev); struct cy *infop; cy_addr base; struct tty *tp; int error = 0; u_char carrier; if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) return (ENXIO); infop = info[unit]; base = infop->base_addr; #ifdef __FreeBSD__ infop->tty = &cy_tty[unit]; #else if (!cy_tty[unit]) infop->tty = cy_tty[unit] = ttymalloc(); #endif tp = infop->tty; tp->t_oproc = cystart; tp->t_param = cyparam; tp->t_dev = dev; if (!(tp->t_state & TS_ISOPEN)) { tp->t_state |= TS_WOPEN; ttychars(tp); if (tp->t_ispeed == 0) { tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = cydefaultrate; } (void) spltty(); cy_channel_init(unit, 1); /* reset the hardware */ /* * raise dtr and generally set things up correctly. this * has the side-effect of selecting the appropriate cd1400 * channel, to help us with subsequent channel control stuff */ cyparam(tp, &tp->t_termios); /* check carrier, and set t_state's TS_CARR_ON flag accordingly */ infop->modem_sig = *(base + CD1400_MSVR); carrier = infop->modem_sig & CD1400_MSVR_CD; if (carrier || (infop->carrier_mode >= 2)) tp->t_state |= TS_CARR_ON; else tp->t_state &=~ TS_CARR_ON; /* * enable modem & rx interrupts - relies on cyparam() * having selected the appropriate cd1400 channel */ infop->intr_enable = (1 << 7) | (1 << 4); *(base + CD1400_SRER) = infop->intr_enable; ttsetwater(tp); } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) return (EBUSY); if (!(flag & O_NONBLOCK)) while (!(tp->t_cflag & CLOCAL) && !(tp->t_state & TS_CARR_ON) && !error) error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI|PCATCH, ttopen, 0); (void) spl0(); if (!error) error = (*linesw[(u_char)tp->t_line].l_open)(dev, tp); return (error); } /* end of cyopen() */ void cyclose_wakeup(void *arg) { wakeup(arg); } /* end of cyclose_wakeup() */ int cyclose(dev_t dev, int flag, int mode, struct proc *p) { u_int unit = UNIT(dev); struct cy *infop = info[unit]; struct tty *tp = infop->tty; cy_addr base = infop->base_addr; int s; (*linesw[(u_char)tp->t_line].l_close)(tp, flag); s = spltty(); /* select the appropriate channel on the CD1400 */ *(base + CD1400_CAR) = (u_char)(unit & 0x03); /* disable this channel and lower DTR */ infop->intr_enable = 0; *(base + CD1400_SRER) = (u_char)0; /* no intrs */ *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; /* no DTR */ infop->modem_sig &= ~CD1400_MSVR_DTR; /* disable receiver (leave transmitter enabled) */ infop->channel_control = (1 << 4) | (1 << 3) | 1; cd1400_channel_cmd(base, infop->channel_control); splx(s); ttyclose(tp); #ifdef broken /* session holds a ref to the tty; can't deallocate */ ttyfree(tp); infop->tty = cy_tty[unit] = (struct tty *)NULL; #endif if (infop->dtrwait) { int error; timeout(cyclose_wakeup, (caddr_t)&infop->dtrwait, infop->dtrwait); do { error = tsleep((caddr_t)&infop->dtrwait, TTIPRI|PCATCH, "cyclose", 0); } while (error == ERESTART); } return 0; } /* end of cyclose() */ int cyread(dev_t dev, struct uio *uio, int flag) { u_int unit = UNIT(dev); struct tty *tp = info[unit]->tty; return (*linesw[(u_char)tp->t_line].l_read)(tp, uio, flag); } /* end of cyread() */ int cywrite(dev_t dev, struct uio *uio, int flag) { u_int unit = UNIT(dev); struct tty *tp = info[unit]->tty; #ifdef Smarts /* XXX duplicate ttwrite(), but without so much output processing on * CR & LF chars. Hardly worth the effort, given that high-throughput * sessions are raw anyhow. */ #else return (*linesw[(u_char)tp->t_line].l_write)(tp, uio, flag); #endif } /* end of cywrite() */ #ifdef Smarts /* standard line discipline input routine */ int cyinput(int c, struct tty *tp) { /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK * bits, as they are done by the CD1400. Hardly worth the effort, * given that high-throughput sessions are raw anyhow. */ } /* end of cyinput() */ #endif /* Smarts */ inline static void service_upper_rx(int unit) { struct cy *ip = info[unit]; struct tty *tp = ip->tty; struct cy_buf *buf; int i; u_char *ch; buf = ip->rx_buf; /* give service_rx() a new one */ disable_intr(); /* faster than spltty() */ ip->rx_buf = buf->next_buf; enable_intr(); if (tp->t_state & TS_ISOPEN) { ch = buf->buf; i = buf->next_char - buf->buf; #ifdef FastRawInput /* try to avoid calling the line discipline stuff if we can */ if ((tp->t_line == 0) && !(tp->t_iflag & (ICRNL | IMAXBEL | INLCR)) && !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG | PENDIN)) && !(tp->t_state & (TS_CNTTB | TS_LNCH))) { i = b_to_q(ch, i, &tp->t_rawq); if (i) { /* * we have no RTS flow control support on cy-8 * boards, so this is really just tough luck */ log(LOG_WARNING, "cy%d: tty input queue overflow\n", unit); } ttwakeup(tp); /* notify any readers */ } else #endif /* FastRawInput */ { while (i--) (*linesw[(u_char)tp->t_line].l_rint)((int)*ch++, tp); } } /* clear the buffer we've just processed */ buf->next_char = buf->buf; buf->free = CY_RX_BUF_SIZE; } /* end of service_upper_rx() */ #ifdef TxBuffer static void service_upper_tx(int unit) { struct cy *ip = info[unit]; struct tty *tp = ip->tty; tp->t_state &=~ (TS_BUSY|TS_FLUSH); if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } selwakeup(&tp->t_wsel); } if (tp->t_outq.c_cc > 0) { struct cy_ring *txq = &ip->tx_buf; int free_count = CY_TX_BUF_SIZE - ip->tx_buf.used; u_char *cp = txq->tail; int count; int chars_done; tp->t_state |= TS_BUSY; /* find the largest contig. copy we can do */ count = ((txq->endish - cp) > free_count) ? free_count : txq->endish - cp; count = ((cp + free_count) > txq->endish) ? txq->endish - cp : free_count; /* copy the first slab */ chars_done = q_to_b(&tp->t_outq, cp, count); /* check for wrap-around time */ cp += chars_done; if (cp == txq->endish) cp = txq->buf; /* back to the start */ /* copy anything else, after we've wrapped around */ if ((chars_done == count) && (count != free_count)) { /* copy the second slab */ count = q_to_b(&tp->t_outq, cp, free_count - count); cp += count; chars_done += count; } /* * update queue, protecting ourselves from any rampant * lower-layers */ disable_intr(); txq->tail = cp; txq->used += chars_done; enable_intr(); } if (!tp->t_outq.c_cc) tp->t_state &=~ TS_BUSY; } /* end of service_upper_tx() */ #endif /* TxBuffer */ inline static void service_upper_mdm(int unit) { struct cy *ip = info[unit]; if (ip->carrier_delta) { int carrier = ip->modem_sig & CD1400_MSVR_CD; struct tty *tp = ip->tty; if (!(*linesw[(u_char)tp->t_line].l_modem)(tp, carrier)) { cy_addr base = ip->base_addr; /* clear DTR */ disable_intr(); *(base + CD1400_CAR) = (u_char)(unit & 0x03); *(base + CD1400_DTR) = (u_char)CD1400_DTR_CLEAR; ip->modem_sig &= ~CD1400_MSVR_DTR; ip->carrier_delta = 0; enable_intr(); } else { disable_intr(); ip->carrier_delta = 0; enable_intr(); } } } /* end of service_upper_mdm() */ /* upper level character processing routine */ void cytimeout(void *ptr) { int unit; timeout_scheduled = 0; #ifdef CyDebug cy_timeouts++; #endif /* check each port in turn */ for (unit = 0; unit < NCY*PORTS_PER_CYCLOM; unit++) { struct cy *ip = info[unit]; #ifndef TxBuffer struct tty *tp = ip->tty; #endif /* ignore anything that is not open */ if (!ip->tty) continue; /* * any received chars to handle? (doesn't matter if intr routine * kicks in while we're testing this) */ if (ip->rx_buf->free != CY_RX_BUF_SIZE) service_upper_rx(unit); #ifdef TxBuffer /* anything to add to the transmit buffer (low-water mark)? */ if (ip->tx_buf.used < CY_TX_BUF_SIZE/2) service_upper_tx(unit); #else if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } selwakeup(&tp->t_wsel); } #endif /* anything modem signals altered? */ service_upper_mdm(unit); /* any overruns to log? */ #ifdef LogOverruns if (ip->fifo_overrun) { /* * turn off the alarm - not important enough to bother * with interrupt protection. */ ip->fifo_overrun = 0; log(LOG_WARNING, "cy%d: receive fifo overrun\n", unit); } #endif if (ip->rx_buf_overrun) { /* * turn off the alarm - not important enough to bother * with interrupt protection. */ ip->rx_buf_overrun = 0; log(LOG_WARNING, "cy%d: receive buffer full\n", unit); } } } /* cytimeout() */ inline static void schedule_upper_service(void) { #ifdef CyDebug cy_timeout_req++; #endif if (!timeout_scheduled) { timeout(cytimeout, (caddr_t)0, 1); /* call next tick */ timeout_scheduled = 1; } } /* end of schedule_upper_service() */ /* initialise a channel on the cyclom board */ static void cy_channel_init(dev_t dev, int reset) { u_int unit = UNIT(dev); int carrier_mode = CARRIER_MODE(dev); struct cy *ip = info[unit]; cy_addr base = ip->base_addr; struct tty *tp = ip->tty; struct cy_buf *buf, *next_buf; int i; #ifndef PollMode u_char cd1400_unit; #endif /* clear the structure and refill it */ bzero(ip, sizeof(struct cy)); ip->base_addr = base; ip->tty = tp; ip->carrier_mode = carrier_mode; /* select channel of the CD1400 */ *(base + CD1400_CAR) = (u_char)(unit & 0x03); if (reset) cd1400_channel_cmd(base, 0x80); /* reset the channel */ /* set LIVR to 0 - intr routines depend on this */ *(base + CD1400_LIVR) = 0; #ifndef PollMode /* set top four bits of {R,T,M}ICR to the cd1400 * number, cd1400_unit */ cd1400_unit = unit / CD1400_NO_OF_CHANNELS; *(base + CD1400_RICR) = (u_char)(cd1400_unit << 4); *(base + CD1400_TICR) = (u_char)(cd1400_unit << 4); *(base + CD1400_MICR) = (u_char)(cd1400_unit << 4); #endif ip->dtrwait = hz/4; /* quarter of a second */ /* setup low-level buffers */ i = CY_RX_BUFS; ip->rx_buf = next_buf = &ip->rx_buf_pool[0]; while (i--) { buf = &ip->rx_buf_pool[i]; buf->next_char = buf->buf; /* first char to use */ buf->free = CY_RX_BUF_SIZE; /* i.e. empty */ buf->next_buf = next_buf; /* where to go next */ next_buf = buf; } #ifdef TxBuffer ip->tx_buf.endish = ip->tx_buf.buf + CY_TX_BUF_SIZE; /* clear the low-level tx buffer */ ip->tx_buf.head = ip->tx_buf.tail = ip->tx_buf.buf; ip->tx_buf.used = 0; #endif /* clear the low-level rx buffer */ ip->rx_buf->next_char = ip->rx_buf->buf; /* first char to use */ ip->rx_buf->free = CY_RX_BUF_SIZE; /* completely empty */ } /* end of cy_channel_init() */ /* service a receive interrupt */ inline static void service_rx(int cd, caddr_t base) { struct cy *infop; unsigned count; int ch; u_char serv_type, channel; #ifdef PollMode u_char save_rir, save_car; #endif /* setup */ #ifdef PollMode save_rir = *(base + CD1400_RIR); channel = cd * CD1400_NO_OF_CHANNELS + (save_rir & 0x3); save_car = *(base + CD1400_CAR); *(base + CD1400_CAR) = save_rir; /* enter modem service */ serv_type = *(base + CD1400_RIVR); #else serv_type = *(base + CD1400_SVCACKR); /* ack receive service */ channel = ((u_char)*(base + CD1400_RICR)) >> 2; /* get cyclom channel # */ #ifdef CyDebug if (channel >= PORTS_PER_CYCLOM) { printf("cy: service_rx - channel %02x\n", channel); panic("cy: service_rx - bad channel"); } #endif #endif infop = info[channel]; /* read those chars */ if (serv_type & CD1400_RIVR_EXCEPTION) { /* read the exception status */ u_char status = *(base + CD1400_RDSR); /* XXX is it a break? Do something if it is! */ /* XXX is IGNPAR not set? Store a null in the buffer. */ #ifdef LogOverruns if (status & CD1400_RDSR_OVERRUN) { #if 0 ch |= TTY_PE; /* for SLIP */ #endif infop->fifo_overrun++; } #endif infop->recv_exception++; } else { struct cy_buf *buf = infop->rx_buf; count = (u_char)*(base + CD1400_RDCR); /* how many to read? */ infop->recv_normal += count; if (buf->free < count) { infop->rx_buf_overrun += count; /* read & discard everything */ while (count--) ch = (u_char)*(base + CD1400_RDSR); } else { /* slurp it into our low-level buffer */ buf->free -= count; while (count--) { ch = (u_char)*(base + CD1400_RDSR); /* read the char */ *(buf->next_char++) = ch; } } } #ifdef PollMode *(base + CD1400_RIR) = (u_char)(save_rir & 0x3f); /* terminate service context */ #else *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ #endif } /* end of service_rx */ /* service a transmit interrupt */ inline static void service_tx(int cd, caddr_t base) { struct cy *ip; #ifdef TxBuffer struct cy_ring *txq; #else struct tty *tp; #endif u_char channel; #ifdef PollMode u_char save_tir, save_car; #else u_char vector; #endif /* setup */ #ifdef PollMode save_tir = *(base + CD1400_TIR); channel = cd * CD1400_NO_OF_CHANNELS + (save_tir & 0x3); save_car = *(base + CD1400_CAR); *(base + CD1400_CAR) = save_tir; /* enter tx service */ #else vector = *(base + CD1400_SVCACKT); /* ack transmit service */ channel = ((u_char)*(base + CD1400_TICR)) >> 2; /* get cyclom channel # */ #ifdef CyDebug if (channel >= PORTS_PER_CYCLOM) { printf("cy: service_tx - channel %02x\n", channel); panic("cy: service_tx - bad channel"); } #endif #endif ip = info[channel]; #ifdef TxBuffer txq = &ip->tx_buf; if (txq->used > 0) { cy_addr base = ip->base_addr; int count = min(CD1400_FIFOSIZE, txq->used); int chars_done = count; u_char *cp = txq->head; u_char *buf_end = txq->endish; /* ip->state |= CY_BUSY; */ while (count--) { *(base + CD1400_TDR) = *cp++; if (cp >= buf_end) cp = txq->buf; }; txq->head = cp; txq->used -= chars_done; /* important that this is atomic */ ip->xmit += chars_done; } /* * disable tx intrs if no more chars to send. we re-enable * them in cystart() */ if (!txq->used) { ip->intr_enable &=~ (1 << 2); *(base + CD1400_SRER) = ip->intr_enable; /* ip->state &= ~CY_BUSY; */ } #else tp = ip->tty; if (!(tp->t_state & TS_TTSTOP) && (tp->t_outq.c_cc > 0)) { cy_addr base = ip->base_addr; int count = min(CD1400_FIFOSIZE, tp->t_outq.c_cc); ip->xmit += count; tp->t_state |= TS_BUSY; while (count--) *(base + CD1400_TDR) = getc(&tp->t_outq); } /* * disable tx intrs if no more chars to send. we re-enable them * in cystart() */ if (!tp->t_outq.c_cc) { ip->intr_enable &=~ (1 << 2); *(base + CD1400_SRER) = ip->intr_enable; tp->t_state &= ~TS_BUSY; } #endif #ifdef PollMode *(base + CD1400_TIR) = (u_char)(save_tir & 0x3f); /* terminate service context */ #else *(base + CD1400_EOSRR) = (u_char)0; /* terminate service context */ #endif } /* end of service_tx */ /* service a modem status interrupt */ inline static void service_mdm(int cd, caddr_t base) { struct cy *infop; u_char channel, deltas; #ifdef PollMode u_char save_mir, save_car; #else u_char vector; #endif /* setup */ #ifdef PollMode save_mir = *(base + CD1400_MIR); channel = cd * CD1400_NO_OF_CHANNELS + (save_mir & 0x3); save_car = *(base + CD1400_CAR); *(base + CD1400_CAR) = save_mir; /* enter modem service */ #else vector = *(base + CD1400_SVCACKM); /* ack modem service */ channel = ((u_char)*(base + CD1400_MICR)) >> 2; /* get cyclom channel # */ #ifdef CyDebug if (channel >= PORTS_PER_CYCLOM) { printf("cy: service_mdm - channel %02x\n", channel); panic("cy: service_mdm - bad channel"); } #endif #endif infop = info[channel]; /* read the siggies and see what's changed */ infop->modem_sig = (u_char)*(base + CD1400_MSVR); deltas = (u_char)*(base + CD1400_MISR); if ((infop->carrier_mode <= 2) && (deltas & CD1400_MISR_CDd)) /* something for the upper layer to deal with */ infop->carrier_delta = 1; infop->mdm++; /* terminate service context */ #ifdef PollMode *(base + CD1400_MIR) = (u_char)(save_mir & 0x3f); #else *(base + CD1400_EOSRR) = (u_char)0; #endif } /* end of service_mdm */ int cyintr(int unit) { int cd; u_char status; /* check each CD1400 in turn */ for (cd = 0; cd < CD1400s_PER_CYCLOM; cd++) { cy_addr base = cyclom_base + cd*CD1400_MEMSIZE; /* poll to see if it has any work */ while (status = (u_char)*(base + CD1400_SVRR)) { #ifdef CyDebug cy_svrr_probes++; #endif /* service requests as appropriate, giving priority to RX */ if (status & CD1400_SVRR_RX) service_rx(cd, base); if (status & CD1400_SVRR_TX) service_tx(cd, base); if (status & CD1400_SVRR_MDM) service_mdm(cd, base); } } /* request upper level service to deal with whatever happened */ schedule_upper_service(); /* re-enable interrupts on the cyclom */ *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; return 1; } int cyioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { int unit = UNIT(dev); struct cy *infop = info[unit]; struct tty *tp = infop->tty; int error; error = (*linesw[(u_char)tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); error = ttioctl(tp, cmd, data, flag #ifdef NetBSD , p #endif ); if (error >= 0) return (error); switch (cmd) { #ifdef notyet /* sigh - more junk to do XXX */ case TIOCSBRK: break; case TIOCCBRK: break; case TIOCSDTR: break; case TIOCCDTR: break; case TIOCMSET: break; case TIOCMBIS: break; case TIOCMBIC: break; #endif /* notyet */ case TIOCMGET: { int bits = 0; u_char status = infop->modem_sig; if (status & CD1400_MSVR_DTR) bits |= TIOCM_DTR | TIOCM_RTS; if (status & CD1400_MSVR_CD) bits |= TIOCM_CD; if (status & CD1400_MSVR_CTS) bits |= TIOCM_CTS; if (status & CD1400_MSVR_DSR) bits |= TIOCM_DSR; #ifdef CYCLOM_16 if (status & CD1400_MSVR_RI) bits |= TIOCM_RI; #endif if (infop->channel_control & 0x02) bits |= TIOCM_LE; *(int *)data = bits; break; } #ifdef TIOCMSBIDIR case TIOCMSBIDIR: return (ENOTTY); #endif /* TIOCMSBIDIR */ #ifdef TIOCMGBIDIR case TIOCMGBIDIR: return (ENOTTY); #endif /* TIOCMGBIDIR */ #ifdef TIOCMSDTRWAIT case TIOCMSDTRWAIT: /* must be root to set dtr delay */ if (p->p_ucred->cr_uid != 0) return(EPERM); infop->dtrwait = *(u_int *)data; break; #endif /* TIOCMSDTRWAIT */ #ifdef TIOCMGDTRWAIT case TIOCMGDTRWAIT: *(u_int *)data = infop->dtrwait; break; #endif /* TIOCMGDTRWAIT */ default: return (ENOTTY); } return 0; } /* end of cyioctl() */ int cyparam(struct tty *tp, struct termios *t) { u_char unit = UNIT(tp->t_dev); struct cy *infop = info[unit]; cy_addr base = infop->base_addr; int cflag = t->c_cflag; int iflag = t->c_iflag; int ispeed, ospeed; int itimeout; int iprescaler, oprescaler; int s; u_char cor_change = 0; u_char opt; if (!t->c_ispeed) t->c_ispeed = t->c_ospeed; s = spltty(); /* select the appropriate channel on the CD1400 */ *(base + CD1400_CAR) = unit & 0x03; /* handle DTR drop on speed == 0 trick */ if (t->c_ospeed == 0) { *(base + CD1400_DTR) = CD1400_DTR_CLEAR; infop->modem_sig &= ~CD1400_MSVR_DTR; } else { *(base + CD1400_DTR) = CD1400_DTR_SET; infop->modem_sig |= CD1400_MSVR_DTR; } /* set baud rates if they've changed from last time */ if ((ospeed = cyspeed(t->c_ospeed, &oprescaler)) < 0) return EINVAL; *(base + CD1400_TBPR) = (u_char)ospeed; *(base + CD1400_TCOR) = (u_char)oprescaler; if ((ispeed = cyspeed(t->c_ispeed, &iprescaler)) < 0) return EINVAL; *(base + CD1400_RBPR) = (u_char)ispeed; *(base + CD1400_RCOR) = (u_char)iprescaler; /* * set receive time-out period * generate a rx interrupt if no new chars are received in * this many ticks * don't bother comparing old & new VMIN, VTIME and ispeed - it * can't be much worse just to calculate and set it each time! * certainly less hassle. :-) */ /* * calculate minimum timeout period: * 5 ms or the time it takes to receive 1 char, rounded up to the * next ms, whichever is greater */ if (t->c_ispeed > 0) { itimeout = (t->c_ispeed > 2200) ? 5 : (10000/t->c_ispeed + 1); /* if we're using VTIME as an inter-char timeout, and it is set to * be longer than the minimum calculated above, go for it */ if (t->c_cc[VMIN] && t->c_cc[VTIME] && t->c_cc[VTIME]*10 > itimeout) itimeout = t->c_cc[VTIME]*10; /* store it, taking care not to overflow the byte-sized register */ *(base + CD1400_RTPR) = (u_char)((itimeout <= 255) ? itimeout : 255); } /* * channel control * receiver enable * transmitter enable (always set) */ opt = (1 << 4) | (1 << 3) | ((cflag & CREAD) ? (1 << 1) : 1); if (opt != infop->channel_control) { infop->channel_control = opt; cd1400_channel_cmd(base, opt); } #ifdef Smarts /* set special chars */ if (t->c_cc[VSTOP] != _POSIX_VDISABLE && (t->c_cc[VSTOP] != infop->spec_char[0])) { *(base + CD1400_SCHR1) = infop->spec_char[0] = t->c_cc[VSTOP]; } if (t->c_cc[VSTART] != _POSIX_VDISABLE && (t->c_cc[VSTART] != infop->spec_char[1])) { *(base + CD1400_SCHR2) = infop->spec_char[0] = t->c_cc[VSTART]; } if (t->c_cc[VINTR] != _POSIX_VDISABLE && (t->c_cc[VINTR] != infop->spec_char[2])) { *(base + CD1400_SCHR3) = infop->spec_char[0] = t->c_cc[VINTR]; } if (t->c_cc[VSUSP] != _POSIX_VDISABLE && (t->c_cc[VSUSP] != infop->spec_char[3])) { *(base + CD1400_SCHR4) = infop->spec_char[0] = t->c_cc[VSUSP]; } #endif /* * set channel option register 1 - * parity mode * stop bits * char length */ opt = 0; /* parity */ if (cflag & PARENB) { if (cflag & PARODD) opt |= 1 << 7; opt |= 2 << 5; /* normal parity mode */ } if (!(iflag & INPCK)) opt |= 1 << 4; /* ignore parity */ /* stop bits */ if (cflag & CSTOPB) opt |= 2 << 2; /* char length */ opt |= (cflag & CSIZE) >> 8; /* nasty, but fast */ if (opt != infop->cor[0]) { cor_change |= 1 << 1; *(base + CD1400_COR1) = opt; } /* * set channel option register 2 - * flow control */ opt = 0; #ifdef Smarts if (iflag & IXANY) opt |= 1 << 7; /* auto output restart on any char after XOFF */ if (iflag & IXOFF) opt |= 1 << 6; /* auto XOFF output flow-control */ #endif #ifndef ALWAYS_RTS_CTS if (cflag & CCTS_OFLOW) #endif opt |= 1 << 1; /* auto CTS flow-control */ if (opt != infop->cor[1]) { cor_change |= 1 << 2; *(base + CD1400_COR2) = opt; } /* * set channel option register 3 - * receiver FIFO interrupt threshold * flow control */ opt = RxFifoThreshold; /* rx fifo threshold */ #ifdef Smarts if (t->c_lflag & ICANON) opt |= 1 << 6; /* detect INTR & SUSP chars */ if (iflag & IXOFF) opt |= (1 << 5) | (1 << 4); /* transparent in-band flow control */ #endif if (opt != infop->cor[2]) { cor_change |= 1 << 3; *(base + CD1400_COR3) = opt; } /* notify the CD1400 if COR1-3 have changed */ if (cor_change) { cor_change |= 1 << 6; /* COR change flag */ cd1400_channel_cmd(base, cor_change); } /* * set channel option register 4 - * CR/NL processing * break processing * received exception processing */ opt = 0; if (iflag & IGNCR) opt |= 1 << 7; #ifdef Smarts /* * we need a new ttyinput() for this, as we don't want to * have ICRNL && INLCR being done in both layers, or to have * synchronisation problems */ if (iflag & ICRNL) opt |= 1 << 6; if (iflag & INLCR) opt |= 1 << 5; #endif if (iflag & IGNBRK) opt |= 1 << 4; if (!(iflag & BRKINT)) opt |= 1 << 3; if (iflag & IGNPAR) #ifdef LogOverruns opt |= 0; /* broken chars cause receive exceptions */ #else opt |= 2; /* discard broken chars */ #endif else { if (iflag & PARMRK) opt |= 4; /* precede broken chars with 0xff 0x0 */ else #ifdef LogOverruns opt |= 0; /* broken chars cause receive exceptions */ #else opt |= 3; /* convert framing/parity errs to nulls */ #endif } *(base + CD1400_COR4) = opt; /* * set channel option register 5 - */ opt = 0; if (iflag & ISTRIP) opt |= 1 << 7; if (t->c_iflag & IEXTEN) { opt |= 1 << 6; /* enable LNEXT (e.g. ctrl-v quoting) handling */ } #ifdef Smarts if (t->c_oflag & ONLCR) opt |= 1 << 1; if (t->c_oflag & OCRNL) opt |= 1; #endif *(base + CD1400_COR5) = opt; /* * set modem change option register 1 * generate modem interrupts on which 1 -> 0 input transitions * also controls auto-DTR output flow-control, which we don't use */ opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ *(base + CD1400_MCOR1) = opt; /* * set modem change option register 2 * generate modem interrupts on specific 0 -> 1 input transitions */ opt = (cflag & CLOCAL) ? 0 : 1 << 4; /* CD */ *(base + CD1400_MCOR2) = opt; splx(s); return 0; } /* end of cyparam */ void cystart(struct tty *tp) { u_char unit = UNIT(tp->t_dev); struct cy *infop = info[unit]; cy_addr base = infop->base_addr; int s; #ifdef CyDebug infop->start_count++; #endif /* check the flow-control situation */ if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) return; if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } selwakeup(&tp->t_wsel); } #ifdef TxBuffer service_upper_tx(unit); /* feed the monster */ #endif s = spltty(); if (!(infop->intr_enable & (1 << 2))) { /* select the channel */ *(base + CD1400_CAR) = unit & (u_char)3; /* (re)enable interrupts to set things in motion */ infop->intr_enable |= (1 << 2); *(base + CD1400_SRER) = infop->intr_enable; infop->start_real++; } splx(s); } /* end of cystart() */ int cystop(struct tty *tp, int flag) { u_char unit = UNIT(tp->t_dev); struct cy *ip = info[unit]; cy_addr base = ip->base_addr; int s; s = spltty(); /* select the channel */ *(base + CD1400_CAR) = unit & 3; /* halt output by disabling transmit interrupts */ ip->intr_enable &=~ (1 << 2); *(base + CD1400_SRER) = ip->intr_enable; splx(s); return 0; } struct tty * cydevtotty(dev_t dev) { u_char unit = UNIT(dev); if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) return NULL; return info[unit]->tty; -} - -int -cyselect(dev_t dev, int rw, struct proc *p) -{ - u_char unit = UNIT(dev); - if (unit >= /* NCY * ? */ PORTS_PER_CYCLOM) - return (ENXIO); - - return (ttyselect(info[unit]->tty, rw, p)); } int cyspeed(int speed, int *prescaler_io) { int actual; int error; int divider; int prescaler; int prescaler_unit; if (speed == 0) return 0; if (speed < 0 || speed > 150000) return -1; /* determine which prescaler to use */ for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; prescaler_unit--, prescaler >>= 2) { if (CYCLOM_CLOCK/prescaler/speed > 63) break; } divider = (CYCLOM_CLOCK/prescaler*2/speed + 1)/2; /* round off */ if (divider > 255) divider = 255; actual = CYCLOM_CLOCK/prescaler/divider; error = ((actual-speed)*2000/speed +1)/2; /* percentage */ /* 3.0% max error tolerance */ if (error < -30 || error > 30) return -1; #if 0 printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); printf("divider = %d (%x)\n", divider, divider); printf("actual = %d\n", actual); printf("error = %d\n", error); #endif *prescaler_io = prescaler_unit; return divider; } /* end of cyspeed() */ static void cd1400_channel_cmd(cy_addr base, u_char cmd) { /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, as the card is probed every round? Replaced delaycount with 8k. Either delaycount has to be implemented in FreeBSD or more sensible way of doing these should be implemented. DELAY isn't enough here. */ unsigned maxwait = 5 * 8 * 1024; /* approx. 5 ms */ /* wait for processing of previous command to complete */ while (*(base + CD1400_CCR) && maxwait--) ; if (!maxwait) log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n", 5 * 8 * 1024); *(base + CD1400_CCR) = cmd; } /* end of cd1400_channel_cmd() */ #ifdef CyDebug /* useful in ddb */ void cyclear(void) { /* clear the timeout request */ disable_intr(); timeout_scheduled = 0; enable_intr(); } void cyclearintr(void) { /* clear interrupts */ *(cyclom_base + CYCLOM_CLEAR_INTR) = (u_char)0; } int cyparam_dummy(struct tty *tp, struct termios *t) { return 0; } void cyset(int unit, int active) { if (unit < 0 || unit >= /* NCY *? */ PORTS_PER_CYCLOM) { printf("bad unit number %d\n", unit); return; } #ifdef __FreeBSD__ cy_tty[unit].t_param = active ? cyparam : cyparam_dummy; #else cy_tty[unit]->t_param = active ? cyparam : cyparam_dummy; #endif } /* useful in ddb */ void cystatus(int unit) { struct cy *infop = info[unit]; struct tty *tp = infop->tty; cy_addr base = infop->base_addr; printf("info for channel %d\n", unit); printf("------------------\n"); printf("cd1400 base address:\t0x%x\n", (int)infop->base_addr); /* select the port */ *(base + CD1400_CAR) = (u_char)unit; printf("saved channel_control:\t%02x\n", infop->channel_control); printf("saved cor1:\t\t%02x\n", infop->cor[0]); printf("service request enable reg:\t%02x (%02x cached)\n", (u_char)*(base + CD1400_SRER), infop->intr_enable); printf("service request register:\t%02x\n", (u_char)*(base + CD1400_SVRR)); printf("\n"); printf("modem status:\t\t\t%02x (%02x cached)\n", (u_char)*(base + CD1400_MSVR), infop->modem_sig); printf("rx/tx/mdm interrupt registers:\t%02x %02x %02x\n", (u_char)*(base + CD1400_RIR), (u_char)*(base + CD1400_TIR), (u_char)*(base + CD1400_MIR)); printf("\n"); if (tp) { printf("tty state:\t\t\t%04x\n", tp->t_state); printf("upper layer queue lengths:\t%d raw, %d canon, %d output\n", tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); } else printf("tty state:\t\t\tclosed\n"); printf("\n"); printf("calls to cystart():\t\t%d (%d useful)\n", infop->start_count, infop->start_real); printf("\n"); printf("total cyclom service probes:\t%d\n", cy_svrr_probes); printf("calls to upper layer:\t\t%d\n", cy_timeouts); printf("rx buffer chars free:\t\t%d\n", infop->rx_buf->free); #ifdef TxBuffer printf("tx buffer chars used:\t\t%d\n", infop->tx_buf.used); #endif printf("received chars:\t\t\t%d good, %d exception\n", infop->recv_normal, infop->recv_exception); printf("transmitted chars:\t\t%d\n", infop->xmit); printf("modem signal deltas:\t\t%d\n", infop->mdm); printf("\n"); } /* end of cystatus() */ #endif #endif /* NCY > 0 */ Index: head/sys/i386/isa/sio.c =================================================================== --- head/sys/i386/isa/sio.c (revision 6781) +++ head/sys/i386/isa/sio.c (revision 6782) @@ -1,2268 +1,2259 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.67 1995/02/25 20:09:14 pst Exp $ + * $Id: sio.c,v 1.68 1995/02/26 02:30:18 bde Exp $ */ #include "sio.h" #if NSIO > 0 /* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. */ #include #include #include #include #define TTYDEFCHARS /* XXX TK2.0 */ #include #undef TTYDEFCHARS #include #include #include #include #include #include #include #include #include #include #include #include /* XXX just to get at `imen' */ #include #include #include #include /* * XXX temporary kludges for 2.0 (XXX TK2.0). */ #define TSA_CARR_ON(tp) ((void *)&(tp)->t_rawq) #define TSA_OCOMPLETE(tp) ((void *)&(tp)->t_outq) #define TSA_OLOWAT(tp) ((void *)&(tp)->t_outq) void termioschars(t) struct termios *t; { bcopy(ttydefchars, t->c_cc, sizeof t->c_cc); } #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE) #define RS_IBUFSIZE 256 #define TTY_BI TTY_FE /* XXX */ #define TTY_OE TTY_PE /* XXX */ #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ #define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) #define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) #define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) #endif /* COM_MULTIPORT */ #define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) #define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */ #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart() and comflush()) * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_int Port_t; /* hardware port */ typedef u_char bool_t; /* boolean */ /* com device structure */ struct com_s { u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ u_char ftl; /* current rx fifo trigger level */ u_char ftl_init; /* ftl_max for next open() */ u_char ftl_max; /* maximum ftl for curent open() */ bool_t hasfifo; /* nonzero for 16550 UARTs */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t poll; /* nonzero if polling is required */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ u_char *obufend; /* end of output buffer */ u_char *optr; /* next char to output */ Port_t data_port; /* i/o ports */ Port_t int_id_port; Port_t iobase; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; struct timeval timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */ #define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Output buffer. Someday we should avoid copying. Twice. */ u_char obuf[256]; }; /* * The public functions in the com module ought to be declared in a com-driver * system header. */ /* Interrupt handling entry points. */ void siointr __P((int unit)); void siopoll __P((void)); /* Device switch entry points. */ int sioopen __P((dev_t dev, int oflags, int devtype, struct proc *p)); int sioclose __P((dev_t dev, int fflag, int devtype, struct proc *p)); int sioread __P((dev_t dev, struct uio *uio, int ioflag)); int siowrite __P((dev_t dev, struct uio *uio, int ioflag)); int sioioctl __P((dev_t dev, int cmd, caddr_t data, int fflag, struct proc *p)); void siostop __P((struct tty *tp, int rw)); #define sioreset noreset int sioselect __P((dev_t dev, int rw, struct proc *p)); #define siommap nommap #define siostrategy nostrategy /* Console device entry points. */ int siocncheckc __P((dev_t dev)); int siocngetc __P((dev_t dev)); struct consdev; void siocninit __P((struct consdev *cp)); void siocnprobe __P((struct consdev *cp)); void siocnputc __P((dev_t dev, int c)); static int sioattach __P((struct isa_device *dev)); static timeout_t siodtrwakeup; static void comflush __P((struct com_s *com)); static void comhardclose __P((struct com_s *com)); static void siointr1 __P((struct com_s *com)); static void commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static int sioprobe __P((struct isa_device *dev)); static void sioregisterdev __P((struct isa_device *id)); static void comstart __P((struct tty *tp)); static timeout_t comwakeup; static int tiocm_xxx2mcr __P((int tiocm_xxx)); #ifdef DSI_SOFT_MODEM static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr)); #endif /* DSI_SOFT_MODEM */ /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) static struct timeval intr_timestamp; struct isa_driver siodriver = { sioprobe, sioattach, "sio" }; #ifdef COMCONSOLE #undef COMCONSOLE #define COMCONSOLE 1 #else #define COMCONSOLE 0 #endif static int comconsole = CONUNIT; static speed_t comdefaultrate = TTYDEF_SPEED; static u_int com_events; /* input chars + weighted output completions */ static int commajor; #if 0 /* XXX TK2.0 */ struct tty *sio_tty[NSIO]; #else struct tty sio_tty[NSIO]; #endif extern struct tty *constty; /* XXX */ #ifdef KGDB #include "machine/remote-sl.h" extern int kgdb_dev; extern int kgdb_rate; extern int kgdb_debug_init; #endif static struct speedtab comspeedtab[] = { 0, 0, 50, COMBRD(50), 75, COMBRD(75), 110, COMBRD(110), 134, COMBRD(134), 150, COMBRD(150), 200, COMBRD(200), 300, COMBRD(300), 600, COMBRD(600), 1200, COMBRD(1200), 1800, COMBRD(1800), 2400, COMBRD(2400), 4800, COMBRD(4800), 9600, COMBRD(9600), 19200, COMBRD(19200), 38400, COMBRD(38400), 57600, COMBRD(57600), 115200, COMBRD(115200), -1, -1 }; /* XXX - configure this list */ static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static int sioprobe(dev) struct isa_device *dev; { static bool_t already_init; Port_t *com_ptr; bool_t failures[10]; int fn; struct isa_device *idev; Port_t iobase; u_char mcr_image; int result; if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ for (com_ptr = likely_com_ports; com_ptr < &likely_com_ports[sizeof likely_com_ports / sizeof likely_com_ports[0]]; ++com_ptr) outb(*com_ptr + com_mcr, 0); already_init = TRUE; } /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(dev)) { idev = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(dev)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", dev->id_unit, COM_MPMASTER(dev)); return (0); } if (!COM_NOTAST4(dev)) { outb(idev->id_iobase + com_scr, idev->id_irq ? 0x80 : 0); mcr_image = 0; } } #endif /* COM_MULTIPORT */ if (idev->id_irq == 0) mcr_image = 0; bzero(failures, sizeof failures); iobase = dev->id_iobase; /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ disable_intr(); /* EXTRA DELAY? */ /* * XXX DELAY() reenables CPU interrupts. This is a problem for * shared interrupts after the first device using one has been * successfully probed - config_isadev() has enabled the interrupt * in the ICU. */ outb(IO_ICU1 + 1, 0xff); /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ outb(iobase + com_cfcr, CFCR_DLAB); outb(iobase + com_dlbl, COMBRD(9600) & 0xff); outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8); outb(iobase + com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (9600 / 10)); /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); outb(iobase + com_ier, 0); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ outb(iobase + com_ier, IER_ETXRDY); /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); DELAY((1 + 2) * 1000000 / (9600 / 10)); /* * Turn off loopback mode so that the interrupt gate works again * (MCR_IENABLE was hidden). This should leave the device driving * an interrupt line high. It doesn't matter if the interrupt * line oscillates while we are not looking at it, since interrupts * are disabled. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ /* EXTRA DELAY? */ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; failures[1] = inb(iobase + com_ier) - IER_ETXRDY; failures[2] = inb(iobase + com_mcr) - mcr_image; if (idev->id_irq != 0) failures[3] = isa_irq_pending(idev) ? 0 : 1; failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; if (idev->id_irq != 0) failures[5] = isa_irq_pending(idev) ? 1 : 0; failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; /* * Turn off all device interrupts and check that they go off properly. * Leave MCR_IENABLE alone. For ports without a master port, it gates * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving at) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ outb(iobase + com_ier, 0); outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = inb(iobase + com_ier); if (idev->id_irq != 0) failures[8] = isa_irq_pending(idev) ? 1 : 0; failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; outb(IO_ICU1 + 1, imen); /* XXX */ enable_intr(); result = IO_COMSIZE; for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) { outb(iobase + com_mcr, 0); result = 0; if (COM_VERBOSE(dev)) printf("sio%d: probe test %d failed\n", dev->id_unit, fn); } return (result); } static struct kern_devconf kdc_sio[NSIO] = { { 0, 0, 0, /* filled in by dev_attach */ "sio", 0, { MDDT_ISA, 0, "tty" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ 0, /* parentdata */ DC_UNCONFIGURED, "RS-232 serial port" } }; static void sioregisterdev(id) struct isa_device *id; { int unit; unit = id->id_unit; if (unit != 0) kdc_sio[unit] = kdc_sio[0]; kdc_sio[unit].kdc_unit = unit; kdc_sio[unit].kdc_isa = id; kdc_sio[unit].kdc_state = DC_IDLE; dev_attach(&kdc_sio[unit]); } static int sioattach(isdp) struct isa_device *isdp; { struct com_s *com; static bool_t comwakeup_started = FALSE; Port_t iobase; int s; int unit; isdp->id_ri_flags |= RI_FAST; iobase = isdp->id_iobase; unit = isdp->id_unit; com = malloc(sizeof *com, M_TTYS, M_NOWAIT); if (com == NULL) return (0); /* * sioprobe() has initialized the device registers as follows: * o cfcr = CFCR_8BITS. * It is most important that CFCR_DLAB is off, so that the * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->no_irq = isdp->id_irq == 0; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->iobase = iobase; com->data_port = iobase + com_data; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; } termioschars(&com->it_in); com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); #ifdef DSI_SOFT_MODEM if((inb(iobase+7) ^ inb(iobase+7)) & 0x80) { printf(" Digicom Systems, Inc. SoftModem"); goto determined_type; } #endif /* DSI_SOFT_MODEM */ #ifdef COM_MULTIPORT if (!COM_ISMULTIPORT(isdp)) #endif { u_char scr; u_char scr1; u_char scr2; scr = inb(iobase + com_scr); outb(iobase + com_scr, 0xa5); scr1 = inb(iobase + com_scr); outb(iobase + com_scr, 0x5a); scr2 = inb(iobase + com_scr); outb(iobase + com_scr, scr); if (scr1 != 0xa5 || scr2 != 0x5a) { printf(" 8250"); goto determined_type; } } outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14); DELAY(100); switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_TRIGGER_1: printf(" 16450"); break; case FIFO_TRIGGER_4: printf(" 16450?"); break; case FIFO_TRIGGER_8: printf(" 16550?"); break; case FIFO_TRIGGER_14: printf(" 16550A"); if (COM_NOFIFO(isdp)) printf(" fifo disabled"); else { com->hasfifo = TRUE; com->ftl_init = FIFO_TRIGGER_14; com->tx_fifo_size = 16; } break; } outb(iobase + com_fifo, 0); determined_type: ; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(isdp)) { com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(isdp)) printf(" master"); printf(")"); com->no_irq = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(isdp))->id_irq == 0; } #endif /* COM_MULTIPORT */ printf("\n"); sioregisterdev(isdp); #ifdef KGDB if (kgdb_dev == makedev(commajor, unit)) { if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) kgdb_dev = -1; /* can't debug over console port */ else { int divisor; /* * XXX now unfinished and broken. Need to do * something more like a full open(). There's no * suitable interrupt handler so don't enable device * interrupts. Watch out for null tp's. */ outb(iobase + com_cfcr, CFCR_DLAB); divisor = ttspeedtab(kgdb_rate, comspeedtab); outb(iobase + com_dlbl, divisor & 0xFF); outb(iobase + com_dlbh, (u_int) divisor >> 8); outb(iobase + com_cfcr, CFCR_8BITS); outb(com->modem_status_port, com->mcr_image |= MCR_DTR | MCR_RTS); if (kgdb_debug_init) { /* * Print prefix of device name, * let kgdb_connect print the rest. */ printf("sio%d: ", unit); kgdb_connect(1); } else printf("sio%d: kgdb enabled\n", unit); } } #endif s = spltty(); com_addr(unit) = com; splx(s); if (!comwakeup_started) { comwakeup((void *)NULL); comwakeup_started = TRUE; } return (1); } /* ARGSUSED */ int sioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); if (mynor & CONTROL_MASK) return (0); #if 0 /* XXX TK2.0 */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else tp = com->tp = &sio_tty[unit]; #endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); if (error != 0) goto out; } kdc_sio[unit].kdc_state = DC_BUSY; if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "siobi", 0); if (error != 0) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; commctl(com, MCR_DTR | MCR_RTS, DMSET); com->ftl_max = com->ftl_init; com->poll = com->no_irq; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; /* * XXX we should goto open_top if comparam() slept. */ ttsetwater(tp); iobase = com->iobase; if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | FIFO_ENABLE | com->ftl); DELAY(100); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(100); (void) inb(com->data_port); } } disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); enable_intr(); /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "siobi" * instead of "siodcd". */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); --com->wopeners; if (error != 0) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } /*ARGSUSED*/ int sioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); splx(s); return (0); } static void comhardclose(com) struct com_s *com; { Port_t iobase; int s; struct tty *tp; int unit; unit = DEV_TO_UNIT(com->tp->t_dev); iobase = com->iobase; s = spltty(); com->poll = FALSE; com->do_timestamp = 0; outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); #ifdef KGDB /* do not disable interrupts or hang up if debugging */ if (kgdb_dev != makedev(commajor, unit)) #endif { outb(iobase + com_ier, 0); tp = com->tp; if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { commctl(com, MCR_RTS, DMSET); if (com->dtr_wait != 0) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ if (!(com->state & CS_DTR_OFF)) kdc_sio[unit].kdc_state = DC_IDLE; splx(s); } int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); tp = com_addr(MINOR_TO_UNIT(mynor))->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty && unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) constty = NULL; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; kdc_sio[DEV_TO_UNIT(com->tp->t_dev)].kdc_state = DC_IDLE; wakeup(&com->dtr_wait); } /* Interrupt routine for timekeeping purposes */ void siointrts(unit) int unit; { /* * XXX microtime() reenables CPU interrupts. We can't afford to * be interrupted and don't want to slow down microtime(), so lock * out interrupts in another way. */ outb(IO_ICU1 + 1, 0xff); microtime(&intr_timestamp); disable_intr(); outb(IO_ICU1 + 1, imen); siointr(unit); } void siointr(unit) int unit; { #ifndef COM_MULTIPORT siointr1(com_addr(unit)); #else /* COM_MULTIPORT */ struct com_s *com; bool_t possibly_more_intrs; /* * Loop until there is no activity on any port. This is necessary * to get an interrupt edge more than to avoid another interrupt. * If the IRQ signal is just an OR of the IRQ signals from several * devices, then the edge from one may be lost because another is * on. */ do { possibly_more_intrs = FALSE; for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && (inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { siointr1(com); possibly_more_intrs = TRUE; } } } while (possibly_more_intrs); #endif /* COM_MULTIPORT */ } static void siointr1(com) struct com_s *com; { u_char line_status; u_char modem_status; u_char *ioptr; u_char recv_data; if (com->do_timestamp) /* XXX a little bloat here... */ com->timestamp = intr_timestamp; while (TRUE) { line_status = inb(com->line_status_port); /* input event? (check first to help avoid overruns) */ while (line_status & LSR_RCV_MASK) { /* break/unnattached error bits or real input? */ if (!(line_status & LSR_RXRDY)) recv_data = 0; else recv_data = inb(com->data_port); ++com->bytes_in; if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #ifdef KGDB /* trap into kgdb? (XXX - needs testing and optim) */ if (recv_data == FRAME_END && !(com->tp->t_state & TS_ISOPEN) && kgdb_dev == makedev(commajor, unit)) { kgdb_connect(0); continue; } #endif /* KGDB */ ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { ++com_events; #if 0 /* for testing input latency vs efficiency */ if (com->iptr - com->ibuf == 8) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); /* XXX - move this out of isr */ if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } /* * "& 0x7F" is to avoid the gcc-1.40 generating a slow * jump from the top of the loop to here */ line_status = inb(com->line_status_port) & 0x7F; } /* modem status change? (always check before doing output) */ modem_status = inb(com->modem_status_port); if (modem_status != com->last_modem_status) { /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; setsofttty(); } /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } } /* output queued and everything ready? */ if (line_status & LSR_TXRDY && com->state >= (CS_ODEVREADY | CS_BUSY | CS_TTGO)) { ioptr = com->optr; if (com->tx_fifo_size > 1) { u_int ocount; ocount = com->obufend - ioptr; if (ocount > com->tx_fifo_size) ocount = com->tx_fifo_size; com->bytes_out += ocount; do outb(com->data_port, *ioptr++); while (--ocount != 0); } else { outb(com->data_port, *ioptr++); ++com->bytes_out; } com->optr = ioptr; if (ioptr >= com->obufend) { /* output just completed */ com_events += LOTS_OF_EVENTS; com->state ^= (CS_ODONE | CS_BUSY); setsofttty(); /* handle at high level ASAP */ } } /* finished? */ #ifndef COM_MULTIPORT if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } } static int tiocm_xxx2mcr(tiocm_xxx) int tiocm_xxx; { int mcr; mcr = 0; if (tiocm_xxx & TIOCM_DTR) mcr |= MCR_DTR; if (tiocm_xxx & TIOCM_RTS) mcr |= MCR_RTS; return (mcr); } int sioioctl(dev, cmd, data, flag, p) dev_t dev; int cmd; caddr_t data; int flag; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mcr; int msr; int mynor; int s; int tiocm_xxx; struct tty *tp; mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); iobase = com->iobase; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); #ifdef DSI_SOFT_MODEM /* * Download micro-code to Digicom modem. */ case TIOCDSIMICROCODE: { u_long l; u_char *p,*pi; pi = (u_char*)(*(caddr_t*)data); error = copyin(pi,&l,sizeof l); if(error) {return error;}; pi += sizeof l; p = malloc(l,M_TEMP,M_NOWAIT); if(!p) {return ENOBUFS;} error = copyin(pi,p,l); if(error) {free(p,M_TEMP); return error;}; if(error = LoadSoftModem( MINOR_TO_UNIT(mynor),iobase,l,p)) {free(p,M_TEMP); return error;} free(p,M_TEMP); return(0); } #endif /* DSI_SOFT_MODEM */ default: return (ENOTTY); } } tp = com->tp; if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); error = ttioctl(tp, cmd, data, flag); if (error >= 0) return (error); s = spltty(); switch (cmd) { case TIOCSBRK: outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: commctl(com, MCR_DTR, DMBIS); break; case TIOCCDTR: commctl(com, MCR_DTR, DMBIC); break; case TIOCMSET: commctl(com, tiocm_xxx2mcr(*(int *)data), DMSET); break; case TIOCMBIS: commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIS); break; case TIOCMBIC: commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIC); break; case TIOCMGET: tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */ mcr = com->mcr_image; if (mcr & MCR_DTR) tiocm_xxx |= TIOCM_DTR; if (mcr & MCR_RTS) tiocm_xxx |= TIOCM_RTS; msr = com->prev_modem_status; if (msr & MSR_CTS) tiocm_xxx |= TIOCM_CTS; if (msr & MSR_DCD) tiocm_xxx |= TIOCM_CD; if (msr & MSR_DSR) tiocm_xxx |= TIOCM_DSR; /* * XXX - MSR_RI is naturally volatile, and we make MSR_TERI * more volatile by reading the modem status a lot. Perhaps * we should latch both bits until the status is read here. */ if (msr & (MSR_RI | MSR_TERI)) tiocm_xxx |= TIOCM_RI; *(int *)data = tiocm_xxx; break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; default: splx(s); return (ENOTTY); } splx(s); return (0); } /* cancel pending output */ static void comflush(com) struct com_s *com; { disable_intr(); if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); enable_intr(); com->tp->t_state &= ~TS_BUSY; } void siopoll() { int unit; if (com_events == 0) return; repeat: for (unit = 0; unit < NSIO; ++unit) { u_char *buf; struct com_s *com; u_char *ibuf; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL) { /* * XXX forget any events related to closed devices * (actually never opened devices) so that we don't * loop. */ disable_intr(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; enable_intr(); if (incc != 0) log(LOG_DEBUG, "sio%d: %d events for device with no tp\n", unit, incc); continue; } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { /* * Prepare to reduce input latency for packet * discplines with a end of packet character. * XXX should be elsewhere. */ if (tp->t_line == SLIPDISC) com->hotchar = 0xc0; else if (tp->t_line == PPPDISC) com->hotchar = 0x7e; else com->hotchar = 0; buf = ibuf; disable_intr(); incc = com->iptr - buf; com_events -= incc; if (ibuf == com->ibuf1) ibuf = com->ibuf2; else ibuf = com->ibuf1; com->ibufend = ibuf + RS_IBUFSIZE; com->ihighwater = ibuf + RS_IHIGHWATER; com->iptr = ibuf; /* * There is now room for another low-level buffer full * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ /* * XXX this used not to look at CS_RTS_IFLOW. The * change is to allow full control of MCR_RTS via * ioctls after turning CS_RTS_IFLOW off. Check * for races. We shouldn't allow the ioctls while * CS_RTS_IFLOW is on. */ if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_TBLOCK)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); enable_intr(); com->ibuf = ibuf; } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; disable_intr(); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); if (delta_modem_status & MSR_DCD) (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } if (com->state & CS_ODONE) { comflush(com); /* XXX - why isn't the table used for t_line == 0? */ if (tp->t_line != 0) (*linesw[tp->t_line].l_start)(tp); else comstart(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) continue; if (((com->state & CS_RTS_IFLOW) || (tp->t_iflag & IXOFF)) && !(tp->t_state & TS_TBLOCK) && tp->t_rawq.c_cc + incc >= RB_I_HIGH_WATER /* * XXX - need flow control for all line disciplines. * Only have it in standard one now. */ && linesw[tp->t_line].l_rint == ttyinput) { if ((tp->t_iflag & IXOFF) && tp->t_cc[VSTOP] != _POSIX_VDISABLE && putc(tp->t_cc[VSTOP], &tp->t_outq) == 0 || (com->state & CS_RTS_IFLOW)) { tp->t_state |= TS_TBLOCK; ttstart(tp); } } /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ if (!(tp->t_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXOFF | IXON)) && !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG | PENDIN)) && !(tp->t_state & (TS_CNTTB | TS_LNCH)) && linesw[tp->t_line].l_rint == ttyinput) { tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; ttstart(tp); } } else { do { u_char line_status; int recv_data; line_status = (u_char) buf[CE_INPUT_OFFSET]; recv_data = (u_char) *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } (*linesw[tp->t_line].l_rint)(recv_data, tp); } while (--incc > 0); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { u_int cfcr; int cflag; struct com_s *com; int divisor; int error; Port_t iobase; int s; int unit; /* check requested parameters */ divisor = ttspeedtab(t->c_ospeed, comspeedtab); if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; s = spltty(); if (divisor == 0) commctl(com, MCR_DTR, DMBIC); /* hang up line */ else commctl(com, MCR_DTR, DMBIS); cflag = t->c_cflag; switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; default: cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if (!(cflag & PARODD)) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; if (com->hasfifo) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and * the total latency is less than about 30 msec. These * latencies are reasonable for humans. Serial comms * protocols shouldn't expect anything better since modem * latencies are larger. */ com->ftl = t->c_ospeed <= 4800 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_14; if (com->ftl > com->ftl_max) com->ftl = com->ftl_max; outb(iobase + com_fifo, FIFO_ENABLE | com->ftl); } /* * Some UARTs lock up if the divisor latch registers are selected * while the UART is doing output (they refuse to transmit anything * more until given a hard reset). Fix this by stopping filling * the device buffers and waiting for them to drain. Reading the * line status port outside of siointr1() might lose some receiver * error bits, but that is acceptable here. */ disable_intr(); retry: com->state &= ~CS_TTGO; enable_intr(); while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) { error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH, "siotx", hz / 100); if (error != 0 && error != EAGAIN) { if (!(tp->t_state & TS_TTSTOP)) { disable_intr(); com->state |= CS_TTGO; enable_intr(); } splx(s); return (error); } } disable_intr(); /* very important while com_data is hidden */ /* * XXX - clearing CS_TTGO is not sufficient to stop further output, * because siopoll() calls comstart() which usually sets it again * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be * sufficient, for similar reasons. */ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) goto retry; if (divisor != 0) { outb(iobase + com_cfcr, cfcr | CFCR_DLAB); outb(iobase + com_dlbl, divisor & 0xFF); outb(iobase + com_dlbh, (u_int) divisor >> 8); } outb(iobase + com_cfcr, com->cfcr_image = cfcr); if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */ else com->state &= ~CS_RTS_IFLOW; /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state &= ~CS_CTS_OFLOW; com->state |= CS_ODEVREADY; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; } /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); enable_intr(); splx(s); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); s = spltty(); disable_intr(); if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; if (tp->t_state & TS_TBLOCK) { if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } else { /* * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it * appropriately in comparam() if RTS-flow is being changed. * Check for races. */ if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) goto out; #if 0 /* XXX TK2.0 */ if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) ttwwakeup(tp); #else if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } selwakeup(&tp->t_wsel); } #endif if (tp->t_state & TS_BUSY) { disable_intr(); siointr1(com); enable_intr(); } else if (tp->t_outq.c_cc != 0) { u_int ocount; tp->t_state |= TS_BUSY; ocount = q_to_b(&tp->t_outq, com->obuf, sizeof com->obuf); disable_intr(); com->obufend = (com->optr = com->obuf) + ocount; com->state |= CS_BUSY; siointr1(com); /* fake interrupt to start output */ enable_intr(); } out: splx(s); } void siostop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (rw & FWRITE) comflush(com); disable_intr(); if (rw & FREAD) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; enable_intr(); } struct tty * siodevtotty(dev) dev_t dev; { int mynor; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO) return (NULL); return (&sio_tty[unit]); -} - -int -sioselect(dev, rw, p) - dev_t dev; - int rw; - struct proc *p; -{ - return (ttyselect(siodevtotty(dev), rw, p)); } static void commctl(com, bits, how) struct com_s *com; int bits; int how; { disable_intr(); switch (how) { case DMSET: outb(com->modem_ctl_port, com->mcr_image = bits | (com->mcr_image & MCR_IENABLE)); break; case DMBIS: outb(com->modem_ctl_port, com->mcr_image |= bits); break; case DMBIC: outb(com->modem_ctl_port, com->mcr_image &= ~bits); break; } enable_intr(); } static void comwakeup(chan) void *chan; { struct com_s *com; static int log_countdown = 1; int unit; timeout(comwakeup, (caddr_t)NULL, hz > 200 ? hz / 200 : 1); if (com_events != 0) { int s; s = splsofttty(); siopoll(); splx(s); } /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); } } /* * Check for and log errors, but not too often. */ if (--log_countdown > 0) return; log_countdown = hz > 200 ? 200 : hz; for (unit = 0; unit < NSIO; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; enable_intr(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); #if 0 /* * XXX if we resurrect this then we should move * the dropping of the ftl to somewhere with less * latency. */ if (errnum == CE_OVERRUN && com->hasfifo && com->ftl > FIFO_TRIGGER_1) { static u_char ftl_in_bytes[] = { 1, 4, 8, 14, }; com->ftl_init = FIFO_TRIGGER_8; #define FIFO_TRIGGER_DELTA FIFO_TRIGGER_4 com->ftl_max = com->ftl -= FIFO_TRIGGER_DELTA; outb(com->iobase + com_fifo, FIFO_ENABLE | com->ftl); log(LOG_DEBUG, "sio%d: reduced fifo trigger level to %d\n", unit, ftl_in_bytes[com->ftl / FIFO_TRIGGER_DELTA]); } #endif } } } /* * Following are all routines needed for SIO to act as console */ #include "i386/i386/cons.h" struct siocnstate { u_char dlbl; u_char dlbh; u_char ier; u_char cfcr; u_char mcr; }; static Port_t siocniobase; static void siocnclose __P((struct siocnstate *sp)); static void siocnopen __P((struct siocnstate *sp)); static void siocntxwait __P((void)); static void siocntxwait() { int timo; /* * Wait for any pending transmission to finish. Required to avoid * the UART lockup bug when the speed is changed, and for normal * transmits. */ timo = 100000; while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY) && --timo != 0) ; } static void siocnopen(sp) struct siocnstate *sp; { int divisor; Port_t iobase; /* * Save all the device control registers except the fifo register * and set our default ones (cs8 -parenb speed=comdefaultrate). * We can't save the fifo register since it is read-only. */ iobase = siocniobase; sp->ier = inb(iobase + com_ier); outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ siocntxwait(); sp->cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB); sp->dlbl = inb(iobase + com_dlbl); sp->dlbh = inb(iobase + com_dlbh); divisor = ttspeedtab(comdefaultrate, comspeedtab); outb(iobase + com_dlbl, divisor & 0xFF); outb(iobase + com_dlbh, (u_int) divisor >> 8); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); /* * We don't want interrupts, but must be careful not to "disable" * them by clearing the MCR_IENABLE bit, since that might cause * an interrupt by floating the IRQ line. */ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void siocnclose(sp) struct siocnstate *sp; { Port_t iobase; /* * Restore the device control registers. */ siocntxwait(); iobase = siocniobase; outb(iobase + com_cfcr, CFCR_DLAB); outb(iobase + com_dlbl, sp->dlbl); outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); } void siocnprobe(cp) struct consdev *cp; { int unit; /* locate the major number */ /* XXX - should be elsewhere since KGDB uses it */ for (commajor = 0; commajor < nchrdev; commajor++) if (cdevsw[commajor].d_open == sioopen) break; /* XXX: ick */ unit = DEV_TO_UNIT(CONUNIT); siocniobase = CONADDR; /* make sure hardware exists? XXX */ /* initialize required fields */ cp->cn_dev = makedev(commajor, unit); if (COMCONSOLE || boothowto & RB_SERIAL) cp->cn_pri = CN_REMOTE; /* Force a serial port console */ else cp->cn_pri = CN_NORMAL; } void siocninit(cp) struct consdev *cp; { /* * XXX can delete more comconsole stuff now that i/o routines are * fairly reentrant. */ comconsole = DEV_TO_UNIT(cp->cn_dev); } int siocncheckc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); if (inb(iobase + com_lsr) & LSR_RXRDY) c = inb(iobase + com_data); else c = 0; siocnclose(&sp); splx(s); return (c); } int siocngetc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp); splx(s); return (c); } void siocnputc(dev, c) dev_t dev; int c; { int s; struct siocnstate sp; s = spltty(); siocnopen(&sp); siocntxwait(); outb(siocniobase + com_data, c); siocnclose(&sp); splx(s); } #ifdef DSI_SOFT_MODEM /* * The magic code to download microcode to a "Connection 14.4+Fax" * modem from Digicom Systems Inc. Very magic. */ #define DSI_ERROR(str) { ptr = str; goto error; } static int LoadSoftModem(int unit, int base_io, u_long size, u_char *ptr) { int int_c,int_k; int data_0188, data_0187; /* * First see if it is a DSI SoftModem */ if(!((inb(base_io+7) ^ inb(base_io+7) & 0x80))) return ENODEV; data_0188 = inb(base_io+4); data_0187 = inb(base_io+3); outb(base_io+3,0x80); outb(base_io+4,0x0C); outb(base_io+0,0x31); outb(base_io+1,0x8C); outb(base_io+7,0x10); outb(base_io+7,0x19); if(0x18 != (inb(base_io+7) & 0x1A)) DSI_ERROR("dsp bus not granted"); if(0x01 != (inb(base_io+7) & 0x01)) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x01 != (inb(base_io+7) & 0x01)) DSI_ERROR("program mem not granted"); } int_c = 0; while(1) { if(int_c >= 7 || size <= 0x1800) break; for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; int_c++; } if(size > 0x1800) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; while(size > 0x1800) { for(int_k = 0 ; int_k < 0xC00; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; } if(size < 0x1800) { for(int_k=0;int_k 0) { if(int_c == 7) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } else { for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } } outb(base_io+7,0x11); outb(base_io+7,3); outb(base_io+4,data_0188 & 0xfb); outb(base_io+3,data_0187); return 0; error: printf("sio%d: DSI SoftModem microcode load failed: <%s>\n",ptr); outb(base_io+7,0x00); \ outb(base_io+3,data_0187); \ outb(base_io+4,data_0188); \ return EIO; } #endif /* DSI_SOFT_MODEM */ #endif /* NSIO > 0 */ Index: head/sys/i386/isa/syscons.c =================================================================== --- head/sys/i386/isa/syscons.c (revision 6781) +++ head/sys/i386/isa/syscons.c (revision 6782) @@ -1,2978 +1,2967 @@ /*- * Copyright (c) 1992-1995 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software withough specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: syscons.c,v 1.104 1995/02/22 13:40:19 sos Exp $ + * $Id: syscons.c,v 1.105 1995/02/25 20:09:16 pst Exp $ */ #include "sc.h" #include "apm.h" #if NSC > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(MAXCONS) #define MAXCONS 16 #endif /* this may break on older VGA's but is usefull on real 32 bit systems */ #define bcopyw bcopy static default_attr user_default = { (FG_LIGHTGREY | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; static default_attr kernel_default = { (FG_WHITE | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; static scr_stat main_console; static scr_stat *console[MAXCONS]; scr_stat *cur_console; static scr_stat *new_scp, *old_scp; static term_stat kernel_console; static default_attr *current_default; static char init_done = FALSE; static char switch_in_progress = FALSE; static char blink_in_progress = FALSE; static char write_in_progress = FALSE; u_int crtc_addr = MONO_BASE; static char crtc_vga = FALSE; static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0, metas = 0; static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0; static char *font_8 = NULL, *font_14 = NULL, *font_16 = NULL; static int fonts_loaded = 0; char palette[3*256]; static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab); #if ASYNCH static u_char kbd_reply = 0; #endif static int delayed_next_scr = FALSE; static int configuration = 0; /* current setup */ static long scrn_blank_time = 0; /* screen saver timeout value */ int scrn_blanked = FALSE; /* screen saver active flag */ static int scrn_saver = 0; /* screen saver routine */ static long scrn_time_stamp; u_char scr_map[256]; static char *video_mode_ptr = NULL; static u_short mouse_and_mask[16] = { 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80, 0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000 }; static u_short mouse_or_mask[16] = { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800, 0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000 }; void none_saver(int blank) { } void (*current_saver)() = none_saver; /* OS specific stuff */ #ifdef not_yet_done #define VIRTUAL_TTY(x) (sccons[x] = ttymalloc(sccons[x])) struct CONSOLE_TTY (sccons[MAXCONS] = ttymalloc(sccons[MAXCONS])) struct tty *sccons[MAXCONS+1]; #else #define VIRTUAL_TTY(x) &sccons[x] #define CONSOLE_TTY &sccons[MAXCONS] struct tty sccons[MAXCONS+1]; #endif #define MONO_BUF pa_to_va(0xB0000) #define CGA_BUF pa_to_va(0xB8000) u_short *Crtat = (u_short *)MONO_BUF; #define WRAPHIST(scp, pointer, offset)\ ((scp->history) + ((((pointer) - (scp->history)) + (scp->history_size)\ + (offset)) % (scp->history_size))) struct isa_driver scdriver = { scprobe, scattach, "sc", 1 }; int scprobe(struct isa_device *dev) { int i, retries = 5; unsigned char val; /* Enable interrupts and keyboard controller */ kbd_wait(); outb(KB_STAT, KB_WRITE); kbd_wait(); outb(KB_DATA, KB_MODE); /* flush any noise in the buffer */ while (inb(KB_STAT) & KB_BUF_FULL) { DELAY(10); (void) inb(KB_DATA); } /* Reset keyboard hardware */ while (retries--) { kbd_wait(); outb(KB_DATA, KB_RESET); for (i=0; i<100000; i++) { DELAY(10); val = inb(KB_DATA); if (val == KB_ACK || val == KB_ECHO) goto gotres; if (val == KB_RESEND) break; } } gotres: if (!retries) printf("scprobe: keyboard won't accept RESET command\n"); else { gotack: DELAY(10); while ((inb(KB_STAT) & KB_BUF_FULL) == 0) DELAY(10); DELAY(10); val = inb(KB_DATA); if (val == KB_ACK) goto gotack; if (val != KB_RESET_DONE) printf("scprobe: keyboard RESET failed %02x\n", val); } #ifdef XT_KEYBOARD kbd_wait(); outb(KB_DATA, 0xF0); kbd_wait(); outb(KB_DATA, 1) kbd_wait(); #endif /* XT_KEYBOARD */ return (IO_KBDSIZE); } static struct kern_devconf kdc_sc[NSC] = { 0, 0, 0, /* filled in by dev_attach */ "sc", 0, { MDDT_ISA, 0, "tty" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ 0, /* parentdata */ DC_BUSY, /* the console is almost always busy */ "Graphics console" }; static inline void sc_registerdev(struct isa_device *id) { if(id->id_unit) kdc_sc[id->id_unit] = kdc_sc[0]; kdc_sc[id->id_unit].kdc_unit = id->id_unit; kdc_sc[id->id_unit].kdc_isa = id; dev_attach(&kdc_sc[id->id_unit]); } #if NAPM > 0 static int scresume(void *dummy) { shfts = 0; ctls = 0; alts = 0; agrs = 0; metas = 0; return 0; } #endif int scattach(struct isa_device *dev) { scr_stat *scp; scinit(); configuration = dev->id_flags; printf("sc%d: ", dev->id_unit); if (crtc_vga) if (crtc_addr == MONO_BASE) printf("VGA mono"); else printf("VGA color"); else if (crtc_addr == MONO_BASE) printf("MDA/hercules"); else printf("CGA/EGA"); printf(" <%d virtual consoles, flags=0x%x>\n", MAXCONS, configuration); scp = console[0]; scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_NOWAIT); /* copy screen to buffer */ bcopyw(Crtat, scp->scr_buf, scp->xsize * scp->ysize * sizeof(u_short)); scp->cursor_pos = scp->scr_buf + scp->xpos + scp->ypos * scp->xsize; scp->mouse_pos = scp->scr_buf; /* initialize history buffer & pointers */ scp->history_head = scp->history_pos = scp->history = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_NOWAIT); bzero(scp->history_head, scp->history_size*sizeof(u_short)); if (crtc_vga) { font_8 = (char *)malloc(8*256, M_DEVBUF, M_NOWAIT); font_14 = (char *)malloc(14*256, M_DEVBUF, M_NOWAIT); font_16 = (char *)malloc(16*256, M_DEVBUF, M_NOWAIT); copy_font(SAVE, FONT_16, font_16); fonts_loaded = FONT_16; scp->font = FONT_16; save_palette(); } /* get screen update going */ scrn_timer(); update_leds(scp->status); sc_registerdev(dev); #if NAPM > 0 scp->r_hook.ah_fun = scresume; scp->r_hook.ah_arg = NULL; scp->r_hook.ah_name = "system keyboard"; scp->r_hook.ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_RESUME , &scp->r_hook); #endif return 0; } struct tty *scdevtotty(dev_t dev) { int unit = minor(dev); if (unit > MAXCONS || unit < 0) return(NULL); if (unit == MAXCONS) return CONSOLE_TTY; return VIRTUAL_TTY(unit); -} - -int -scselect(dev_t dev, int rw, struct proc *p) -{ - struct tty *tp = scdevtotty(dev); - - if (tp == NULL) - return(ENXIO); - - return (ttyselect(tp, rw, p)); } static scr_stat *get_scr_stat(dev_t dev) { int unit = minor(dev); if (unit > MAXCONS || unit < 0) return(NULL); if (unit == MAXCONS) return console[0]; return console[unit]; } static int get_scr_num() { int i = 0; while ((i < MAXCONS) && (cur_console != console[i])) i++; return i < MAXCONS ? i : 0; } int scopen(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); tp->t_oproc = scstart; tp->t_param = scparam; tp->t_dev = dev; if (!(tp->t_state & TS_ISOPEN)) { ttychars(tp); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; scparam(tp, &tp->t_termios); ttsetwater(tp); } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) return(EBUSY); tp->t_state |= TS_CARR_ON; tp->t_cflag |= CLOCAL; if (!console[minor(dev)]) console[minor(dev)] = alloc_scp(); return((*linesw[tp->t_line].l_open)(dev, tp)); } int scclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); struct scr_stat *scp; if (!tp) return(ENXIO); if (minor(dev) < MAXCONS) { scp = get_scr_stat(tp->t_dev); if (scp->status & SWITCH_WAIT_ACQ) wakeup((caddr_t)&scp->smode); #if not_yet_done if (scp == &main_console) { scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; } else { free(scp->scr_buf, M_DEVBUF); free(scp->history, M_DEVBUF); free(scp, M_DEVBUF); console[minor(dev)] = NULL; } #else scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; #endif } (*linesw[tp->t_line].l_close)(tp, flag); ttyclose(tp); return(0); } int scread(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); return((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int scwrite(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); return((*linesw[tp->t_line].l_write)(tp, uio, flag)); } void scintr(int unit) { static struct tty *cur_tty; int c, len; u_char *cp; /* make screensaver happy */ scrn_time_stamp = time.tv_sec; if (scrn_blanked) { (*current_saver)(FALSE); cur_console->status |= UPDATE_SCREEN; } c = scgetc(1); cur_tty = VIRTUAL_TTY(get_scr_num()); if (!(cur_tty->t_state & TS_ISOPEN)) if (!((cur_tty = CONSOLE_TTY)->t_state & TS_ISOPEN)) return; switch (c & 0xff00) { case 0x0000: /* normal key */ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); break; case NOKEY: /* nothing there */ break; case FKEY: /* function key, return string */ if (cp = get_fstr((u_int)c, (u_int *)&len)) { while (len-- > 0) (*linesw[cur_tty->t_line].l_rint)(*cp++ & 0xFF, cur_tty); } break; case MKEY: /* meta is active, prepend ESC */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); break; case BKEY: /* backtab fixed sequence (esc [ Z) */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)('[', cur_tty); (*linesw[cur_tty->t_line].l_rint)('Z', cur_tty); break; } } int scparam(struct tty *tp, struct termios *t) { tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; return 0; } int scioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { int i, error; struct tty *tp; struct trapframe *fp; scr_stat *scp; tp = scdevtotty(dev); if (!tp) return ENXIO; scp = get_scr_stat(tp->t_dev); switch (cmd) { /* process console hardware related ioctl's */ case GIO_ATTR: /* get current attributes */ *(int*)data = scp->term.cur_attr; return 0; case GIO_COLOR: /* is this a color console ? */ if (crtc_addr == COLOR_BASE) *(int*)data = 1; else *(int*)data = 0; return 0; case CONS_CURRENT: /* get current adapter type */ if (crtc_vga) *(int*)data = KD_VGA; else if (crtc_addr == MONO_BASE) *(int*)data = KD_MONO; else *(int*)data = KD_CGA; return 0; case CONS_GET: /* get current video mode */ *(int*)data = scp->mode; return 0; case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */ scrn_blank_time = *(int*)data; return 0; case CONS_CURSORTYPE: /* set cursor type blink/noblink */ if ((*(int*)data) & 0x01) configuration |= BLINK_CURSOR; else configuration &= ~BLINK_CURSOR; if ((*(int*)data) & 0x02) configuration |= CHAR_CURSOR; else configuration &= ~CHAR_CURSOR; return 0; case CONS_BELLTYPE: /* set bell type sound/visual */ if (*data) configuration |= VISUAL_BELL; else configuration &= ~VISUAL_BELL; return 0; case CONS_HISTORY: /* set history size */ if (*data) { free(scp->history, M_DEVBUF); scp->history_size = *(int*)data; if (scp->history_size < scp->ysize) scp->history = NULL; else { scp->history_size *= scp->xsize; scp->history_head = scp->history_pos = scp->history = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_NOWAIT); bzero(scp->history_head, scp->history_size*sizeof(u_short)); } return 0; } else return EINVAL; case CONS_MOUSECTL: /* control mouse arrow */ { mouse_info_t *mouse = (mouse_info_t*)data; int fontsize; switch (scp->font) { default: case FONT_8: fontsize = 8; break; case FONT_14: fontsize = 14; break; case FONT_16: fontsize = 16; break; } switch (mouse->operation) { case MOUSE_SHOW: if (!(scp->status & MOUSE_ENABLED)) { scp->mouse_oldpos = Crtat + (scp->mouse_pos - scp->scr_buf); scp->status |= (UPDATE_MOUSE | MOUSE_ENABLED); } else return EINVAL; break; case MOUSE_HIDE: if (scp->status & MOUSE_ENABLED) { scp->status &= ~MOUSE_ENABLED; scp->status |= UPDATE_MOUSE; } else return EINVAL; break; case MOUSE_MOVEABS: scp->mouse_xpos = mouse->x; scp->mouse_ypos = mouse->y; goto set_mouse_pos; case MOUSE_MOVEREL: scp->mouse_xpos += mouse->x; scp->mouse_ypos += mouse->y; set_mouse_pos: if (scp->mouse_xpos < 0) scp->mouse_xpos = 0; if (scp->mouse_ypos < 0) scp->mouse_ypos = 0; if (scp->mouse_xpos >= scp->xsize*8) scp->mouse_xpos = (scp->xsize*8)-1; if (scp->mouse_ypos >= scp->ysize*fontsize) scp->mouse_ypos = (scp->ysize*fontsize)-1; scp->mouse_pos = scp->scr_buf + (scp->mouse_ypos/fontsize)*scp->xsize + scp->mouse_xpos/8; if (scp->status & MOUSE_ENABLED) scp->status |= UPDATE_MOUSE; break; case MOUSE_GETPOS: mouse->x = scp->mouse_xpos; mouse->y = scp->mouse_ypos; return 0; default: return EINVAL; } /* make screensaver happy */ if (scp == cur_console) { scrn_time_stamp = time.tv_sec; if (scrn_blanked) { (*current_saver)(FALSE); scp->status |= UPDATE_SCREEN; } } return 0; } case CONS_GETINFO: /* get current (virtual) console info */ { vid_info_t *ptr = (vid_info_t*)data; if (ptr->size == sizeof(struct vid_info)) { ptr->m_num = get_scr_num(); ptr->mv_col = scp->xpos; ptr->mv_row = scp->ypos; ptr->mv_csz = scp->xsize; ptr->mv_rsz = scp->ysize; ptr->mv_norm.fore = (scp->term.std_attr & 0x0f00)>>8; ptr->mv_norm.back = (scp->term.std_attr & 0xf000)>>12; ptr->mv_rev.fore = (scp->term.rev_attr & 0x0f00)>>8; ptr->mv_rev.back = (scp->term.rev_attr & 0xf000)>>12; ptr->mv_grfc.fore = 0; /* not supported */ ptr->mv_grfc.back = 0; /* not supported */ ptr->mv_ovscan = scp->border; ptr->mk_keylock = scp->status & LOCK_KEY_MASK; return 0; } return EINVAL; } case CONS_GETVERS: /* get version number */ *(int*)data = 0x200; /* version 2.0 */ return 0; /* VGA TEXT MODES */ case SW_VGA_C40x25: case SW_VGA_C80x25: case SW_VGA_M80x25: case SW_VGA_C80x30: case SW_VGA_M80x30: case SW_VGA_C80x50: case SW_VGA_M80x50: case SW_VGA_C80x60: case SW_VGA_M80x60: case SW_B40x25: case SW_C40x25: case SW_B80x25: case SW_C80x25: case SW_ENH_B40x25: case SW_ENH_C40x25: case SW_ENH_B80x25: case SW_ENH_C80x25: case SW_ENH_B80x43: case SW_ENH_C80x43: if (!crtc_vga || video_mode_ptr == NULL) return ENXIO; switch (cmd & 0xff) { case M_VGA_C80x60: case M_VGA_M80x60: if (!(fonts_loaded & FONT_8)) return EINVAL; scp->xsize = 80; scp->ysize = 60; break; case M_VGA_C80x50: case M_VGA_M80x50: if (!(fonts_loaded & FONT_8)) return EINVAL; scp->xsize = 80; scp->ysize = 50; break; case M_ENH_B80x43: case M_ENH_C80x43: if (!(fonts_loaded & FONT_8)) return EINVAL; scp->xsize = 80; scp->ysize = 43; break; case M_VGA_C80x30: case M_VGA_M80x30: scp->xsize = 80; scp->ysize = 30; break; default: if ((cmd & 0xff) > M_VGA_CG320) return EINVAL; else scp->xsize = *(video_mode_ptr+((cmd&0xff)*64)); scp->ysize = *(video_mode_ptr+((cmd&0xff)*64)+1)+1; break; } scp->mode = cmd & 0xff; scp->status &= ~UNKNOWN_MODE; /* text mode */ free(scp->scr_buf, M_DEVBUF); scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_NOWAIT); if (scp == cur_console) set_mode(scp); clear_screen(scp); if (tp->t_winsize.ws_col != scp->xsize || tp->t_winsize.ws_row != scp->ysize) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; /* GRAPHICS MODES */ case SW_BG320: case SW_BG640: case SW_CG320: case SW_CG320_D: case SW_CG640_E: case SW_CG640x350: case SW_ENH_CG640: case SW_BG640x480: case SW_CG640x480: case SW_VGA_CG320: if (!crtc_vga || video_mode_ptr == NULL) return ENXIO; scp->mode = cmd & 0xFF; scp->status |= UNKNOWN_MODE; /* graphics mode */ scp->xsize = (*(video_mode_ptr + (scp->mode*64))) * 8; scp->ysize = (*(video_mode_ptr + (scp->mode*64) + 1) + 1) * (*(video_mode_ptr + (scp->mode*64) + 2)); set_mode(scp); /* clear_graphics();*/ if (tp->t_winsize.ws_xpixel != scp->xsize || tp->t_winsize.ws_ypixel != scp->ysize) { tp->t_winsize.ws_xpixel = scp->xsize; tp->t_winsize.ws_ypixel = scp->ysize; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; case VT_SETMODE: /* set screen switcher mode */ bcopy(data, &scp->smode, sizeof(struct vt_mode)); if (scp->smode.mode == VT_PROCESS) { scp->proc = p; scp->pid = scp->proc->p_pid; } return 0; case VT_GETMODE: /* get screen switcher mode */ bcopy(&scp->smode, data, sizeof(struct vt_mode)); return 0; case VT_RELDISP: /* screen switcher ioctl */ switch(*data) { case VT_FALSE: /* user refuses to release screen, abort */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { old_scp->status &= ~SWITCH_WAIT_REL; switch_in_progress = FALSE; return 0; } return EINVAL; case VT_TRUE: /* user has released screen, go on */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { scp->status &= ~SWITCH_WAIT_REL; exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; return 0; } return EINVAL; case VT_ACKACQ: /* acquire acknowledged, switch completed */ if (scp == new_scp && (scp->status & SWITCH_WAIT_ACQ)) { scp->status &= ~SWITCH_WAIT_ACQ; switch_in_progress = FALSE; return 0; } return EINVAL; default: return EINVAL; } /* NOT REACHED */ case VT_OPENQRY: /* return free virtual console */ for (i = 0; i < MAXCONS; i++) { tp = VIRTUAL_TTY(i); if (!(tp->t_state & TS_ISOPEN)) { *data = i + 1; return 0; } } return EINVAL; case VT_ACTIVATE: /* switch to screen *data */ return switch_scr(scp, (*data) - 1); case VT_WAITACTIVE: /* wait for switch to occur */ if (*data > MAXCONS || *data < 0) return EINVAL; if (minor(dev) == (*data) - 1) return 0; if (*data == 0) { if (scp == cur_console) return 0; } else scp = console[(*data) - 1]; while ((error=tsleep((caddr_t)&scp->smode, PZERO|PCATCH, "waitvt", 0)) == ERESTART) ; return error; case VT_GETACTIVE: *data = get_scr_num()+1; return 0; case KDENABIO: /* allow io operations */ fp = (struct trapframe *)p->p_md.md_regs; fp->tf_eflags |= PSL_IOPL; return 0; case KDDISABIO: /* disallow io operations (default) */ fp = (struct trapframe *)p->p_md.md_regs; fp->tf_eflags &= ~PSL_IOPL; return 0; case KDSETMODE: /* set current mode of this (virtual) console */ switch (*data) { case KD_TEXT: /* switch to TEXT (known) mode */ /* restore fonts & palette ! */ if (crtc_vga) { if (fonts_loaded & FONT_8) copy_font(LOAD, FONT_8, font_8); if (fonts_loaded & FONT_14) copy_font(LOAD, FONT_14, font_14); if (fonts_loaded & FONT_16) copy_font(LOAD, FONT_16, font_16); load_palette(); } /* FALL THROUGH */ case KD_TEXT1: /* switch to TEXT (known) mode */ /* no restore fonts & palette */ scp->status &= ~UNKNOWN_MODE; if (crtc_vga && video_mode_ptr) set_mode(scp); clear_screen(scp); return 0; case KD_GRAPHICS: /* switch to GRAPHICS (unknown) mode */ scp->status |= UNKNOWN_MODE; return 0; default: return EINVAL; } /* NOT REACHED */ case KDGETMODE: /* get current mode of this (virtual) console */ *data = (scp->status & UNKNOWN_MODE) ? KD_GRAPHICS : KD_TEXT; return 0; case KDSBORDER: /* set border color of this (virtual) console */ if (!crtc_vga) return ENXIO; scp->border = *data; if (scp == cur_console) set_border(scp->border); return 0; case KDSKBSTATE: /* set keyboard state (locks) */ if (*data >= 0 && *data <= LOCK_KEY_MASK) { scp->status &= ~LOCK_KEY_MASK; scp->status |= *data; if (scp == cur_console) update_leds(scp->status); return 0; } return EINVAL; case KDGKBSTATE: /* get keyboard state (locks) */ *data = scp->status & LOCK_KEY_MASK; return 0; case KDSETRAD: /* set keyboard repeat & delay rates */ if (*data & 0x80) return EINVAL; i = spltty(); kbd_cmd(KB_SETRAD); kbd_cmd(*data); splx(i); return 0; case KDSKBMODE: /* set keyboard mode */ switch (*data) { case K_RAW: /* switch to RAW scancode mode */ scp->status |= KBD_RAW_MODE; return 0; case K_XLATE: /* switch to XLT ascii mode */ if (scp == cur_console && scp->status == KBD_RAW_MODE) shfts = ctls = alts = agrs = metas = 0; scp->status &= ~KBD_RAW_MODE; return 0; default: return EINVAL; } /* NOT REACHED */ case KDGKBMODE: /* get keyboard mode */ *data = (scp->status & KBD_RAW_MODE) ? K_RAW : K_XLATE; return 0; case KDMKTONE: /* sound the bell */ if (*(int*)data) do_bell(scp, (*(int*)data)&0xffff, (((*(int*)data)>>16)&0xffff)*hz/1000); else do_bell(scp, scp->bell_pitch, scp->bell_duration); return 0; case KIOCSOUND: /* make tone (*data) hz */ if (scp == cur_console) { if (*(int*)data) { int pitch = TIMER_FREQ/(*(int*)data); /* set command for counter 2, 2 byte write */ if (acquire_timer2(TIMER_16BIT|TIMER_SQWAVE)) return EBUSY; /* set pitch */ outb(TIMER_CNTR2, pitch); outb(TIMER_CNTR2, (pitch>>8)); /* enable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) | 3); } else { /* disable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) & 0xFC); release_timer2(); } } return 0; case KDGKBTYPE: /* get keyboard type */ *data = 0; /* type not known (yet) */ return 0; case KDSETLED: /* set keyboard LED status */ if (*data >= 0 && *data <= LED_MASK) { scp->status &= ~LED_MASK; scp->status |= *data; if (scp == cur_console) update_leds(scp->status); return 0; } return EINVAL; case KDGETLED: /* get keyboard LED status */ *data = scp->status & LED_MASK; return 0; case GETFKEY: /* get functionkey string */ if (*(u_short*)data < n_fkey_tab) { fkeyarg_t *ptr = (fkeyarg_t*)data; bcopy(&fkey_tab[ptr->keynum].str, ptr->keydef, fkey_tab[ptr->keynum].len); ptr->flen = fkey_tab[ptr->keynum].len; return 0; } else return EINVAL; case SETFKEY: /* set functionkey string */ if (*(u_short*)data < n_fkey_tab) { fkeyarg_t *ptr = (fkeyarg_t*)data; bcopy(ptr->keydef, &fkey_tab[ptr->keynum].str, min(ptr->flen, MAXFK)); fkey_tab[ptr->keynum].len = min(ptr->flen, MAXFK); return 0; } else return EINVAL; case GIO_SCRNMAP: /* get output translation table */ bcopy(&scr_map, data, sizeof(scr_map)); return 0; case PIO_SCRNMAP: /* set output translation table */ bcopy(data, &scr_map, sizeof(scr_map)); return 0; case GIO_KEYMAP: /* get keyboard translation table */ bcopy(&key_map, data, sizeof(key_map)); return 0; case PIO_KEYMAP: /* set keyboard translation table */ bcopy(data, &key_map, sizeof(key_map)); return 0; case PIO_FONT8x8: /* set 8x8 dot font */ if (!crtc_vga) return ENXIO; bcopy(data, font_8, 8*256); fonts_loaded |= FONT_8; copy_font(LOAD, FONT_8, font_8); return 0; case GIO_FONT8x8: /* get 8x8 dot font */ if (!crtc_vga) return ENXIO; if (fonts_loaded & FONT_8) { bcopy(font_8, data, 8*256); return 0; } else return ENXIO; case PIO_FONT8x14: /* set 8x14 dot font */ if (!crtc_vga) return ENXIO; bcopy(data, font_14, 14*256); fonts_loaded |= FONT_14; copy_font(LOAD, FONT_14, font_14); return 0; case GIO_FONT8x14: /* get 8x14 dot font */ if (!crtc_vga) return ENXIO; if (fonts_loaded & FONT_14) { bcopy(font_14, data, 14*256); return 0; } else return ENXIO; case PIO_FONT8x16: /* set 8x16 dot font */ if (!crtc_vga) return ENXIO; bcopy(data, font_16, 16*256); fonts_loaded |= FONT_16; copy_font(LOAD, FONT_16, font_16); return 0; case GIO_FONT8x16: /* get 8x16 dot font */ if (!crtc_vga) return ENXIO; if (fonts_loaded & FONT_16) { bcopy(font_16, data, 16*256); return 0; } else return ENXIO; default: break; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return(error); error = ttioctl(tp, cmd, data, flag); if (error >= 0) return(error); return(ENOTTY); } void scxint(dev_t dev) { struct tty *tp = scdevtotty(dev); if (!tp) return; tp->t_state &= ~TS_BUSY; if (tp->t_line) (*linesw[tp->t_line].l_start)(tp); else scstart(tp); } void scstart(struct tty *tp) { struct clist *rbp; int i, s, len; u_char buf[PCBURST]; scr_stat *scp = get_scr_stat(tp->t_dev); if (scp->status & SLKED || blink_in_progress) return; s = spltty(); if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) { tp->t_state |= TS_BUSY; splx(s); rbp = &tp->t_outq; while (rbp->c_cc) { len = q_to_b(rbp, buf, PCBURST); ansi_put(scp, buf, len); } scp->status |= UPDATE_SCREEN; s = spltty(); tp->t_state &= ~TS_BUSY; if (rbp->c_cc <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)rbp); } selwakeup(&tp->t_wsel); } } splx(s); } void pccnprobe(struct consdev *cp) { int maj; /* locate the major number */ for (maj = 0; maj < nchrdev; maj++) if ((void*)cdevsw[maj].d_open == (void*)scopen) break; /* initialize required fields */ cp->cn_dev = makedev(maj, MAXCONS); cp->cn_pri = CN_INTERNAL; } void pccninit(struct consdev *cp) { scinit(); } void pccnputc(dev_t dev, char c) { scr_stat *scp = console[0]; term_stat save = scp->term; scp->term = kernel_console; current_default = &kernel_default; if (scp->scr_buf == Crtat) draw_cursor(scp, FALSE); if (c == '\n') ansi_put(scp, "\r\n", 2); else ansi_put(scp, &c, 1); scp->status |= UPDATE_SCREEN; kernel_console = scp->term; current_default = &user_default; scp->term = save; if (scp == cur_console /* && scrn_timer not running */) { if (scp->scr_buf != Crtat) { bcopyw(scp->scr_buf, Crtat, (scp->xsize*scp->ysize)*sizeof(u_short)); scp->status &= ~CURSOR_SHOWN; } draw_cursor(scp, TRUE); scp->status &= ~UPDATE_SCREEN; } } int pccngetc(dev_t dev) { int s = spltty(); /* block scintr while we poll */ int c = scgetc(0); splx(s); return(c); } int pccncheckc(dev_t dev) { return (scgetc(1) & 0xff); } static void scrn_timer() { static int cursor_blinkrate; scr_stat *scp = cur_console; /* should we just return ? */ if ((scp->status&UNKNOWN_MODE) || blink_in_progress || switch_in_progress) { timeout((timeout_func_t)scrn_timer, 0, hz/10); return; } if (!scrn_blanked) { /* update entire screen image */ if (scp->status & UPDATE_SCREEN) { bcopyw(scp->scr_buf, Crtat, scp->xsize*scp->ysize*sizeof(u_short)); scp->status &= ~CURSOR_SHOWN; } /* update "pseudo" mouse arrow */ if ((scp->status & MOUSE_ENABLED) && ((scp->status & UPDATE_MOUSE) || (scp->status & UPDATE_SCREEN))) draw_mouse_image(scp); /* update cursor image */ if (scp->status & CURSOR_ENABLED) draw_cursor(scp, !(configuration&BLINK_CURSOR) || !(cursor_blinkrate++&0x04)); /* signal update done */ scp->status &= ~UPDATE_SCREEN; } if (scrn_blank_time && (time.tv_sec>scrn_time_stamp+scrn_blank_time)) (*current_saver)(TRUE); timeout((timeout_func_t)scrn_timer, 0, hz/25); } static void clear_screen(scr_stat *scp) { move_crsr(scp, 0, 0); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf, scp->xsize * scp->ysize); } static int switch_scr(scr_stat *scp, u_int next_scr) { if (switch_in_progress && (cur_console->proc != pfind(cur_console->pid))) switch_in_progress = FALSE; if (next_scr >= MAXCONS || switch_in_progress || (cur_console->smode.mode == VT_AUTO && cur_console->status & UNKNOWN_MODE)) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } /* is the wanted virtual console open ? */ if (next_scr) { struct tty *tp = VIRTUAL_TTY(next_scr); if (!(tp->t_state & TS_ISOPEN)) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } } /* delay switch if actively updating screen */ if (write_in_progress || blink_in_progress) { delayed_next_scr = next_scr+1; return 0; } switch_in_progress = TRUE; old_scp = cur_console; new_scp = console[next_scr]; wakeup((caddr_t)&new_scp->smode); if (new_scp == old_scp) { switch_in_progress = FALSE; delayed_next_scr = FALSE; return 0; } /* has controlling process died? */ if (old_scp->proc && (old_scp->proc != pfind(old_scp->pid))) old_scp->smode.mode = VT_AUTO; if (new_scp->proc && (new_scp->proc != pfind(new_scp->pid))) new_scp->smode.mode = VT_AUTO; /* check the modes and switch approbiatly */ if (old_scp->smode.mode == VT_PROCESS) { old_scp->status |= SWITCH_WAIT_REL; psignal(old_scp->proc, old_scp->smode.relsig); } else { exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; } return 0; } static void exchange_scr(void) { move_crsr(old_scp, old_scp->xpos, old_scp->ypos); cur_console = new_scp; if (old_scp->mode != new_scp->mode || (old_scp->status & UNKNOWN_MODE)){ if (crtc_vga && video_mode_ptr) set_mode(new_scp); } move_crsr(new_scp, new_scp->xpos, new_scp->ypos); if ((old_scp->status & UNKNOWN_MODE) && crtc_vga) { if (fonts_loaded & FONT_8) copy_font(LOAD, FONT_8, font_8); if (fonts_loaded & FONT_14) copy_font(LOAD, FONT_14, font_14); if (fonts_loaded & FONT_16) copy_font(LOAD, FONT_16, font_16); load_palette(); } if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE) shfts = ctls = alts = agrs = metas = 0; update_leds(new_scp->status); delayed_next_scr = FALSE; bcopyw(new_scp->scr_buf, Crtat, (new_scp->xsize*new_scp->ysize)*sizeof(u_short)); new_scp->status &= ~CURSOR_SHOWN; } static inline void move_crsr(scr_stat *scp, int x, int y) { if (x < 0 || y < 0 || x >= scp->xsize || y >= scp->ysize) return; scp->xpos = x; scp->ypos = y; scp->cursor_pos = scp->scr_buf + scp->ypos * scp->xsize + scp->xpos; } static void scan_esc(scr_stat *scp, u_char c) { static u_char ansi_col[16] = {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15}; int i, n; u_short *src, *dst, count; if (scp->term.esc == 1) { switch (c) { case '[': /* Start ESC [ sequence */ scp->term.esc = 2; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; case 'M': /* Move cursor up 1 line, scroll if at top */ if (scp->ypos > 0) move_crsr(scp, scp->xpos, scp->ypos - 1); else { bcopyw(scp->scr_buf, scp->scr_buf + scp->xsize, (scp->ysize - 1) * scp->xsize * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf, scp->xsize); } break; #if notyet case 'Q': scp->term.esc = 4; break; #endif case 'c': /* Clear screen & home */ clear_screen(scp); break; } } else if (scp->term.esc == 2) { if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case '=': scp->term.esc = 3; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; case 'A': /* up n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos - n); break; case 'B': /* down n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'C': /* right n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'D': /* left n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos - n, scp->ypos); break; case 'E': /* cursor to start of line n lines down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos + n); break; case 'F': /* cursor to start of line n lines up */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos - n); break; case 'f': /* Cursor move */ case 'H': if (scp->term.num_param == 0) move_crsr(scp, 0, 0); else if (scp->term.num_param == 2) move_crsr(scp, scp->term.param[1] - 1, scp->term.param[0] - 1); break; case 'J': /* Clear all or part of display */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of display */ fillw(scp->term.cur_attr | scr_map[0x20], scp->cursor_pos, scp->scr_buf + scp->xsize * scp->ysize - scp->cursor_pos); break; case 1: /* clear from beginning of display to cursor */ fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf, scp->cursor_pos - scp->scr_buf); break; case 2: /* clear entire display */ clear_screen(scp); break; } break; case 'K': /* Clear all or part of line */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of line */ fillw(scp->term.cur_attr | scr_map[0x20], scp->cursor_pos, scp->xsize - scp->xpos); break; case 1: /* clear from beginning of line to cursor */ fillw(scp->term.cur_attr|scr_map[0x20], scp->cursor_pos - (scp->xsize - scp->xpos), (scp->xsize - scp->xpos) + 1); break; case 2: /* clear entire line */ fillw(scp->term.cur_attr|scr_map[0x20], scp->cursor_pos - (scp->xsize - scp->xpos), scp->xsize); break; } break; case 'L': /* Insert n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; src = scp->scr_buf + scp->ypos * scp->xsize; dst = src + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopyw(src, dst, count * scp->xsize * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], src, n * scp->xsize); break; case 'M': /* Delete n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; dst = scp->scr_buf + scp->ypos * scp->xsize; src = dst + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopyw(src, dst, count * scp->xsize * sizeof(u_short)); src = dst + count * scp->xsize; fillw(scp->term.cur_attr | scr_map[0x20], src, n * scp->xsize); break; case 'P': /* Delete n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; dst = scp->cursor_pos; src = dst + n; count = scp->xsize - (scp->xpos + n); bcopyw(src, dst, count * sizeof(u_short)); src = dst + count; fillw(scp->term.cur_attr | scr_map[0x20], src, n); break; case '@': /* Insert n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; src = scp->cursor_pos; dst = src + n; count = scp->xsize - (scp->xpos + n); bcopyw(src, dst, count * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], src, n); break; case 'S': /* scroll up n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopyw(scp->scr_buf + (scp->xsize * n), scp->scr_buf, scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - n), scp->xsize * n); break; case 'T': /* scroll down n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopyw(scp->scr_buf, scp->scr_buf + (scp->xsize * n), scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf, scp->xsize * n); break; case 'X': /* delete n characters in line */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf + scp->xpos + ((scp->xsize*scp->ypos) * sizeof(u_short)), n); break; case 'Z': /* move n tabs backwards */ n = scp->term.param[0]; if (n < 1) n = 1; if ((i = scp->xpos & 0xf8) == scp->xpos) i -= 8*n; else i -= 8*(n-1); if (i < 0) i = 0; move_crsr(scp, i, scp->ypos); break; case '`': /* move cursor to column n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, n - 1, scp->ypos); break; case 'a': /* move cursor n columns to the right */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'd': /* move cursor to row n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, n - 1); break; case 'e': /* move cursor n rows down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'm': /* change attribute */ if (scp->term.num_param == 0) { scp->term.cur_attr = scp->term.std_attr; break; } for (i = 0; i < scp->term.num_param; i++) { switch (n = scp->term.param[i]) { case 0: /* back to normal */ scp->term.cur_attr = scp->term.std_attr; break; case 1: /* highlight (bold) */ scp->term.cur_attr &= 0xFF00; scp->term.cur_attr |= 0x0800; break; case 4: /* highlight (underline) */ scp->term.cur_attr &= 0xFF00; scp->term.cur_attr |= 0x0800; break; case 5: /* blink */ scp->term.cur_attr &= 0xFF00; scp->term.cur_attr |= 0x8000; break; case 7: /* reverse video */ scp->term.cur_attr = scp->term.rev_attr; break; case 30: case 31: /* set fg color */ case 32: case 33: case 34: case 35: case 36: case 37: scp->term.cur_attr = (scp->term.cur_attr&0xF8FF) | (ansi_col[(n-30)&7]<<8); break; case 40: case 41: /* set bg color */ case 42: case 43: case 44: case 45: case 46: case 47: scp->term.cur_attr = (scp->term.cur_attr&0x8FFF) | (ansi_col[(n-40)&7]<<12); break; } } break; case 'x': if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* reset attributes */ scp->term.cur_attr = scp->term.std_attr = current_default->std_attr; scp->term.rev_attr = current_default->rev_attr; break; case 1: /* set ansi background */ scp->term.cur_attr = scp->term.std_attr = (scp->term.std_attr & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); break; case 2: /* set ansi foreground */ scp->term.cur_attr = scp->term.std_attr = (scp->term.std_attr & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); break; case 3: /* set ansi attribute directly */ scp->term.cur_attr = scp->term.std_attr = (scp->term.param[1]&0xFF)<<8; break; case 5: /* set ansi reverse video background */ scp->term.rev_attr = (scp->term.rev_attr & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); break; case 6: /* set ansi reverse video foreground */ scp->term.rev_attr = (scp->term.rev_attr & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); break; case 7: /* set ansi reverse video directly */ scp->term.rev_attr = (scp->term.param[1]&0xFF)<<8; break; } break; case 'z': /* switch to (virtual) console n */ if (scp->term.num_param == 1) switch_scr(scp, scp->term.param[0]); break; } } else if (scp->term.esc == 3) { if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case 'A': /* set display border color */ if (scp->term.num_param == 1) scp->border=scp->term.param[0] & 0xff; if (scp == cur_console) set_border(scp->border); break; case 'B': /* set bell pitch and duration */ if (scp->term.num_param == 2) { scp->bell_pitch = scp->term.param[0]; scp->bell_duration = scp->term.param[1]*10; } break; case 'C': /* set cursor type & shape */ if (scp->term.num_param == 1) { if (scp->term.param[0] & 0x01) configuration |= BLINK_CURSOR; else configuration &= ~BLINK_CURSOR; if (scp->term.param[0] & 0x02) configuration |= CHAR_CURSOR; else configuration &= ~CHAR_CURSOR; } else if (scp->term.num_param == 2) { scp->cursor_start = scp->term.param[0] & 0x1F; scp->cursor_end = scp->term.param[1] & 0x1F; } break; case 'F': /* set ansi foreground */ if (scp->term.num_param == 1) scp->term.cur_attr = scp->term.std_attr = (scp->term.std_attr & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); break; case 'G': /* set ansi background */ if (scp->term.num_param == 1) scp->term.cur_attr = scp->term.std_attr = (scp->term.std_attr & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); break; case 'H': /* set ansi reverse video foreground */ if (scp->term.num_param == 1) scp->term.rev_attr = (scp->term.rev_attr & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); break; case 'I': /* set ansi reverse video background */ if (scp->term.num_param == 1) scp->term.rev_attr = (scp->term.rev_attr & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); break; } } scp->term.esc = 0; } static inline void draw_cursor(scr_stat *scp, int show) { if (show && !(scp->status & CURSOR_SHOWN)) { u_short cursor_image = *(Crtat + (scp->cursor_pos - scp->scr_buf)); scp->cursor_saveunder = cursor_image; if (configuration & CHAR_CURSOR) cursor_image = (cursor_image & 0xff00) | '_'; else { if ((cursor_image & 0x7000) == 0x7000) { cursor_image &= 0x8fff; if(!(cursor_image & 0x0700)) cursor_image |= 0x0f00; } else { cursor_image |= 0x7000; if ((cursor_image & 0x0f00) == 0x0f00) cursor_image &= 0xf0ff; } } *(Crtat + (scp->cursor_pos - scp->scr_buf)) = cursor_image; scp->status |= CURSOR_SHOWN; } if (!show && (scp->status & CURSOR_SHOWN)) { *(Crtat+(scp->cursor_pos-scp->scr_buf)) = scp->cursor_saveunder; scp->status &= ~CURSOR_SHOWN; } } static void ansi_put(scr_stat *scp, u_char *buf, int len) { u_char *ptr = buf; if (scp->status & UNKNOWN_MODE) return; /* make screensaver happy */ if (scp == cur_console) { scrn_time_stamp = time.tv_sec; if (scrn_blanked) (*current_saver)(FALSE); } write_in_progress++; outloop: if (scp->term.esc) { scan_esc(scp, *ptr++); len--; } else if (PRINTABLE(*ptr)) { /* Print only printables */ do { *scp->cursor_pos++ = (scp->term.cur_attr | scr_map[*ptr++]); scp->xpos++; len--; } while (len && PRINTABLE(*ptr) && (scp->xpos < scp->xsize)); if (scp->xpos >= scp->xsize) { scp->xpos = 0; scp->ypos++; } } else { switch(*ptr) { case 0x07: do_bell(scp, scp->bell_pitch, scp->bell_duration); break; case 0x08: /* non-destructive backspace */ if (scp->cursor_pos > scp->scr_buf) { scp->cursor_pos--; if (scp->xpos > 0) scp->xpos--; else { scp->xpos += scp->xsize - 1; scp->ypos--; } } break; case 0x09: /* non-destructive tab */ { int i = 8 - scp->xpos % 8u; scp->cursor_pos += i; if ((scp->xpos += i) >= scp->xsize) { scp->xpos = 0; scp->ypos++; } } break; case 0x0a: /* newline, same pos */ scp->cursor_pos += scp->xsize; scp->ypos++; break; case 0x0c: /* form feed, clears screen */ clear_screen(scp); break; case 0x0d: /* return, return to pos 0 */ scp->cursor_pos -= scp->xpos; scp->xpos = 0; break; case 0x1b: /* start escape sequence */ scp->term.esc = 1; scp->term.num_param = 0; break; } ptr++; len--; } /* do we have to scroll ?? */ if (scp->cursor_pos >= scp->scr_buf + scp->ysize * scp->xsize) { if (scp->history) { bcopyw(scp->scr_buf, scp->history_head, scp->xsize * sizeof(u_short)); scp->history_head += scp->xsize; if (scp->history_head + scp->xsize > scp->history + scp->history_size) scp->history_head = scp->history; } bcopyw(scp->scr_buf + scp->xsize, scp->scr_buf, scp->xsize * (scp->ysize - 1) * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - 1), scp->xsize); scp->cursor_pos -= scp->xsize; scp->ypos--; } if (len) goto outloop; write_in_progress--; if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } static void scinit(void) { u_short volatile *cp = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short), was; unsigned hw_cursor; int i; if (init_done) return; init_done = TRUE; /* * Crtat initialized to point to MONO buffer, if not present change * to CGA_BUF offset. ONLY add the difference since locore.s adds * in the remapped offset at the "right" time */ was = *cp; *cp = (u_short) 0xA55A; if (*cp != 0xA55A) crtc_addr = MONO_BASE; else { *cp = was; crtc_addr = COLOR_BASE; Crtat = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short); } /* extract cursor location */ outb(crtc_addr,14); hw_cursor = inb(crtc_addr+1)<<8 ; outb(crtc_addr,15); hw_cursor |= inb(crtc_addr+1); /* move hardware cursor out of the way */ outb(crtc_addr,14); outb(crtc_addr+1, 0xff); outb(crtc_addr,15); outb(crtc_addr+1, 0xff); /* is this a VGA or higher ? */ outb(crtc_addr, 7); if (inb(crtc_addr) == 7) { u_long pa; u_long segoff; crtc_vga = TRUE; /* * Get the BIOS video mode pointer. */ segoff = *(u_long *)pa_to_va(0x4a8); pa = (((segoff & 0xffff0000) >> 12) + (segoff & 0xffff)); if (ISMAPPED(pa, sizeof(u_long))) { segoff = *(u_long *)pa_to_va(pa); pa = (((segoff & 0xffff0000) >> 12) + (segoff & 0xffff)); if (ISMAPPED(pa, 64)) video_mode_ptr = (char *)pa_to_va(pa); } } current_default = &user_default; console[0] = &main_console; init_scp(console[0]); console[0]->scr_buf = console[0]->mouse_pos = Crtat; console[0]->cursor_pos = Crtat + hw_cursor; console[0]->xpos = hw_cursor % COL; console[0]->ypos = hw_cursor / COL; cur_console = console[0]; for (i=1; iscr_buf = scp->cursor_pos = scp->scr_buf = scp->mouse_pos = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_NOWAIT); scp->history_head = scp->history_pos = scp->history = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_NOWAIT); bzero(scp->history_head, scp->history_size*sizeof(u_short)); if (crtc_vga && video_mode_ptr) set_mode(scp); clear_screen(scp); return scp; } static void init_scp(scr_stat *scp) { scp->mode = M_VGA_C80x25; scp->font = FONT_16; scp->xsize = COL; scp->ysize = ROW; scp->term.esc = 0; scp->term.std_attr = current_default->std_attr; scp->term.rev_attr = current_default->rev_attr; scp->term.cur_attr = scp->term.std_attr; scp->border = BG_BLACK; scp->cursor_start = -1; scp->cursor_end = -1; scp->mouse_xpos = scp->mouse_ypos = 0; scp->bell_pitch = BELL_PITCH; scp->bell_duration = BELL_DURATION; scp->status = (*(char *)pa_to_va(0x417) & 0x20) ? NLKED : 0; scp->status |= CURSOR_ENABLED; scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; scp->history_head = scp->history_pos = scp->history = NULL; scp->history_size = HISTORY_SIZE; } static u_char *get_fstr(u_int c, u_int *len) { u_int i; if (!(c & FKEY)) return(NULL); i = (c & 0xFF) - F_FN; if (i > n_fkey_tab) return(NULL); *len = fkey_tab[i].len; return(fkey_tab[i].str); } static void update_leds(int which) { int s; static u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* replace CAPS led with ALTGR led for ALTGR keyboards */ if (key_map.n_keys > ALTGR_OFFSET) { if (which & ALKED) which |= CLKED; else which &= ~CLKED; } s = spltty(); kbd_cmd(KB_SETLEDS); kbd_cmd(xlate_leds[which & LED_MASK]); splx(s); } static void history_to_screen(scr_stat *scp) { int i; scp->status &= ~UPDATE_SCREEN; for (i=0; iysize; i++) bcopyw(scp->history + (((scp->history_pos - scp->history) + scp->history_size-((i+1)*scp->xsize))%scp->history_size), scp->scr_buf + (scp->xsize * (scp->ysize-1 - i)), scp->xsize * sizeof(u_short)); scp->status |= UPDATE_SCREEN; } static int history_up_line(scr_stat *scp) { if (WRAPHIST(scp, scp->history_pos, -(scp->xsize*scp->ysize)) != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, -scp->xsize); history_to_screen(scp); return 0; } else return -1; } static int history_down_line(scr_stat *scp) { if (scp->history_pos != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, scp->xsize); history_to_screen(scp); return 0; } else return -1; } /* * scgetc(noblock) - get character from keyboard. * If noblock = 0 wait until a key is pressed. * Else return NOKEY. */ u_int scgetc(int noblock) { u_char scancode, keycode; u_int state, action; struct key_t *key; static u_char esc_flag = 0, compose = 0; static u_int chr = 0; next_code: kbd_wait(); /* First see if there is something in the keyboard port */ if (inb(KB_STAT) & KB_BUF_FULL) scancode = inb(KB_DATA); else if (noblock) return(NOKEY); else goto next_code; if (cur_console->status & KBD_RAW_MODE) return scancode; #if ASYNCH if (scancode == KB_ACK || scancode == KB_RESEND) { kbd_reply = scancode; if (noblock) return(NOKEY); goto next_code; } #endif keycode = scancode & 0x7F; switch (esc_flag) { case 0x00: /* normal scancode */ switch(scancode) { case 0xB8: /* left alt (compose key) */ if (compose) { compose = 0; if (chr > 255) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); chr = 0; } } break; case 0x38: if (!compose) { compose = 1; chr = 0; } break; case 0xE0: case 0xE1: esc_flag = scancode; goto next_code; } break; case 0xE0: /* 0xE0 prefix */ esc_flag = 0; switch (keycode) { case 0x1C: /* right enter key */ keycode = 0x59; break; case 0x1D: /* right ctrl key */ keycode = 0x5A; break; case 0x35: /* keypad divide key */ keycode = 0x5B; break; case 0x37: /* print scrn key */ keycode = 0x5C; break; case 0x38: /* right alt key (alt gr) */ keycode = 0x5D; break; case 0x47: /* grey home key */ keycode = 0x5E; break; case 0x48: /* grey up arrow key */ keycode = 0x5F; break; case 0x49: /* grey page up key */ keycode = 0x60; break; case 0x4B: /* grey left arrow key */ keycode = 0x61; break; case 0x4D: /* grey right arrow key */ keycode = 0x62; break; case 0x4F: /* grey end key */ keycode = 0x63; break; case 0x50: /* grey down arrow key */ keycode = 0x64; break; case 0x51: /* grey page down key */ keycode = 0x65; break; case 0x52: /* grey insert key */ keycode = 0x66; break; case 0x53: /* grey delete key */ keycode = 0x67; break; /* the following 3 are only used on the MS "Natural" keyboard */ case 0x5b: /* left Window key */ keycode = 0x69; break; case 0x5c: /* right Window key */ keycode = 0x6a; break; case 0x5d: /* menu key */ keycode = 0x6b; break; default: /* ignore everything else */ goto next_code; } break; case 0xE1: /* 0xE1 prefix */ esc_flag = 0; if (keycode == 0x1D) esc_flag = 0x1D; goto next_code; /* NOT REACHED */ case 0x1D: /* pause / break */ esc_flag = 0; if (keycode != 0x45) goto next_code; keycode = 0x68; break; } /* if scroll-lock pressed allow history browsing */ if (cur_console->history && cur_console->status & SLKED) { int i; cur_console->status &= ~CURSOR_ENABLED; if (!(cur_console->status & BUFFER_SAVED)) { cur_console->status |= BUFFER_SAVED; cur_console->history_save = cur_console->history_head; /* copy screen into top of history buffer */ for (i=0; iysize; i++) { bcopyw(cur_console->scr_buf + (cur_console->xsize * i), cur_console->history_head, cur_console->xsize * sizeof(u_short)); cur_console->history_head += cur_console->xsize; if (cur_console->history_head + cur_console->xsize > cur_console->history + cur_console->history_size) cur_console->history_head=cur_console->history; } cur_console->history_pos = cur_console->history_head; history_to_screen(cur_console); } switch (scancode) { case 0x47: /* home key */ cur_console->history_pos = cur_console->history_head; history_to_screen(cur_console); goto next_code; case 0x4F: /* end key */ cur_console->history_pos = WRAPHIST(cur_console, cur_console->history_head, cur_console->xsize*cur_console->ysize); history_to_screen(cur_console); goto next_code; case 0x48: /* up arrow key */ if (history_up_line(cur_console)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; case 0x50: /* down arrow key */ if (history_down_line(cur_console)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; case 0x49: /* page up key */ for (i=0; iysize; i++) if (history_up_line(cur_console)) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; case 0x51: /* page down key */ for (i=0; iysize; i++) if (history_down_line(cur_console)) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; } } if (compose) { switch (scancode) { /* key pressed process it */ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ chr = (scancode - 0x40) + chr*10; goto next_code; case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ chr = (scancode - 0x47) + chr*10; goto next_code; case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ chr = (scancode - 0x4E) + chr*10; goto next_code; case 0x52: /* keypad 0 */ chr *= 10; goto next_code; /* key release, no interest here */ case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */ case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */ case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */ case 0xD2: /* keypad 0 */ goto next_code; case 0x38: /* left alt key */ break; default: if (chr) { compose = chr = 0; do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; } break; } } state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0)); if ((!agrs && (cur_console->status & ALKED)) || (agrs && !(cur_console->status & ALKED))) keycode += ALTGR_OFFSET; key = &key_map.key[keycode]; if ( ((key->flgs & FLAG_LOCK_C) && (cur_console->status & CLKED)) || ((key->flgs & FLAG_LOCK_N) && (cur_console->status & NLKED)) ) state ^= 1; /* Check for make/break */ action = key->map[state]; if (scancode & 0x80) { /* key released */ if (key->spcl & 0x80) { switch (action) { case LSH: shfts &= ~1; break; case RSH: shfts &= ~2; break; case LCTR: ctls &= ~1; break; case RCTR: ctls &= ~2; break; case LALT: alts &= ~1; break; case RALT: alts &= ~2; break; case NLK: nlkcnt = 0; break; case CLK: clkcnt = 0; break; case SLK: slkcnt = 0; break; case ASH: agrs = 0; break; case ALK: alkcnt = 0; break; case META: metas = 0; break; } } if (chr && !compose) { action = chr; chr = 0; return(action); } } else { /* key pressed */ if (key->spcl & (0x80>>state)) { switch (action) { /* LOCKING KEYS */ case NLK: if (!nlkcnt) { nlkcnt++; if (cur_console->status & NLKED) cur_console->status &= ~NLKED; else cur_console->status |= NLKED; update_leds(cur_console->status); } break; case CLK: if (!clkcnt) { clkcnt++; if (cur_console->status & CLKED) cur_console->status &= ~CLKED; else cur_console->status |= CLKED; update_leds(cur_console->status); } break; case SLK: if (!slkcnt) { slkcnt++; if (cur_console->status & SLKED) { cur_console->status &= ~SLKED; if (cur_console->status & BUFFER_SAVED){ int i; u_short *ptr = cur_console->history_save; for (i=0; iysize; i++) { bcopyw(ptr, cur_console->scr_buf + (cur_console->xsize*i), cur_console->xsize * sizeof(u_short)); ptr += cur_console->xsize; if (ptr + cur_console->xsize > cur_console->history + cur_console->history_size) ptr = cur_console->history; } cur_console->status&=~BUFFER_SAVED; cur_console->history_head=cur_console->history_save; cur_console->status|=(CURSOR_ENABLED|UPDATE_SCREEN); } scstart(VIRTUAL_TTY(get_scr_num())); } else cur_console->status |= SLKED; update_leds(cur_console->status); } break; case ALK: if (!alkcnt) { alkcnt++; if (cur_console->status & ALKED) cur_console->status &= ~ALKED; else cur_console->status |= ALKED; update_leds(cur_console->status); } break; /* NON-LOCKING KEYS */ case NOP: break; case RBT: shutdown_nice(); break; case SUSP: #if NAPM > 0 apm_suspend(); #endif break; case DBG: #ifdef DDB /* try to switch to console 0 */ if (cur_console->smode.mode == VT_AUTO && console[0]->smode.mode == VT_AUTO) switch_scr(cur_console, 0); Debugger("manual escape to debugger"); return(NOKEY); #else printf("No debugger in kernel\n"); #endif break; case LSH: shfts |= 1; break; case RSH: shfts |= 2; break; case LCTR: ctls |= 1; break; case RCTR: ctls |= 2; break; case LALT: alts |= 1; break; case RALT: alts |= 2; break; case ASH: agrs = 1; break; case META: metas = 1; break; case NEXT: switch_scr(cur_console, (get_scr_num() + 1) % MAXCONS); break; case BTAB: return(BKEY); default: if (action >= F_SCR && action <= L_SCR) { switch_scr(cur_console, action - F_SCR); break; } if (action >= F_FN && action <= L_FN) action |= FKEY; return(action); } } else { if (metas) action |= MKEY; return(action); } } goto next_code; } int scmmap(dev_t dev, int offset, int nprot) { if (offset > 0x20000 - PAGE_SIZE) return -1; return i386_btop((VIDEOMEM + offset)); } static void kbd_wait(void) { int i = 1000; while (i--) { if ((inb(KB_STAT) & KB_READY) == 0) break; DELAY (10); } } static void kbd_cmd(u_char command) { int retry = 5; do { int i = 100000; kbd_wait(); #if ASYNCH kbd_reply = 0; outb(KB_DATA, command); while (i--) { if (kbd_reply == KB_ACK) return; if (kbd_reply == KB_RESEND) break; } #else outb(KB_DATA, command); while (i--) { if (inb(KB_STAT) & KB_BUF_FULL) { int val; DELAY(10); val = inb(KB_DATA); if (val == KB_ACK) return; if (val == KB_RESEND) break; } } #endif } while (retry--); } static void set_mode(scr_stat *scp) { char *modetable; char special_modetable[64]; int mode, font_size; if (scp != cur_console) return; /* setup video hardware for the given mode */ switch (scp->mode) { case M_VGA_M80x60: bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64); goto special_80x60; case M_VGA_C80x60: bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64); special_80x60: special_modetable[2] = 0x08; special_modetable[19] = 0x47; goto special_480l; case M_VGA_M80x30: bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64); goto special_80x30; case M_VGA_C80x30: bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64); special_80x30: special_modetable[19] = 0x4f; special_480l: special_modetable[9] |= 0xc0; special_modetable[16] = 0x08; special_modetable[17] = 0x3e; special_modetable[26] = 0xea; special_modetable[28] = 0xdf; special_modetable[31] = 0xe7; special_modetable[32] = 0x04; modetable = special_modetable; goto setup_mode; case M_ENH_B80x43: bcopyw(video_mode_ptr+(64*M_ENH_B80x25),&special_modetable, 64); goto special_80x43; case M_ENH_C80x43: bcopyw(video_mode_ptr+(64*M_ENH_C80x25),&special_modetable, 64); special_80x43: special_modetable[28] = 87; goto special_80x50; case M_VGA_M80x50: bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64); goto special_80x50; case M_VGA_C80x50: bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64); special_80x50: special_modetable[2] = 8; special_modetable[19] = 7; modetable = special_modetable; goto setup_mode; case M_VGA_C40x25: case M_VGA_C80x25: case M_VGA_M80x25: case M_B40x25: case M_C40x25: case M_B80x25: case M_C80x25: case M_ENH_B40x25: case M_ENH_C40x25: case M_ENH_B80x25: case M_ENH_C80x25: modetable = video_mode_ptr + (scp->mode * 64); setup_mode: set_vgaregs(modetable); font_size = *(modetable + 2); /* set font type (size) */ switch (font_size) { case 0x10: outb(TSIDX, 0x03); outb(TSREG, 0x00); /* font 0 */ scp->font = FONT_16; break; case 0x0E: outb(TSIDX, 0x03); outb(TSREG, 0x05); /* font 1 */ scp->font = FONT_14; break; default: case 0x08: outb(TSIDX, 0x03); outb(TSREG, 0x0A); /* font 2 */ scp->font = FONT_8; break; } break; case M_BG320: case M_CG320: case M_BG640: case M_CG320_D: case M_CG640_E: case M_CG640x350: case M_ENH_CG640: case M_BG640x480: case M_CG640x480: case M_VGA_CG320: set_vgaregs(video_mode_ptr + (scp->mode * 64)); break; default: /* call user defined function XXX */ break; } /* set border color for this (virtual) console */ set_border(scp->border); return; } void set_border(int color) { inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x11); outb(ATC, color); inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x20); /* enable Palette */ } static void set_vgaregs(char *modetable) { int i, s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */ outb(TSIDX, 0x07); outb(TSREG, 0x00); /* unlock registers */ for (i=0; i<4; i++) { /* program sequencer */ outb(TSIDX, i+1); outb(TSREG, modetable[i+5]); } outb(MISC, modetable[9]); /* set dot-clock */ outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */ outb(crtc_addr, 0x11); outb(crtc_addr+1, inb(crtc_addr+1) & 0x7F); for (i=0; i<25; i++) { /* program crtc */ outb(crtc_addr, i); if (i == 14 || i == 15) /* no hardware cursor */ outb(crtc_addr+1, 0xff); else outb(crtc_addr+1, modetable[i+10]); } inb(crtc_addr+6); /* reset flip-flop */ for (i=0; i<20; i++) { /* program attribute ctrl */ outb(ATC, i); outb(ATC, modetable[i+35]); } for (i=0; i<9; i++) { /* program graph data ctrl */ outb(GDCIDX, i); outb(GDCREG, modetable[i+55]); } inb(crtc_addr+6); /* reset flip-flop */ outb(ATC ,0x20); /* enable palette */ splx(s); } static void set_font_mode() { /* setup vga for loading fonts (graphics plane mode) */ inb(crtc_addr+6); outb(ATC, 0x30); outb(ATC, 0x01); #if SLOW_VGA outb(TSIDX, 0x02); outb(TSREG, 0x04); outb(TSIDX, 0x04); outb(TSREG, 0x06); outb(GDCIDX, 0x04); outb(GDCREG, 0x02); outb(GDCIDX, 0x05); outb(GDCREG, 0x00); outb(GDCIDX, 0x06); outb(GDCREG, 0x05); #else outw(TSIDX, 0x0402); outw(TSIDX, 0x0604); outw(GDCIDX, 0x0204); outw(GDCIDX, 0x0005); outw(GDCIDX, 0x0506); /* addr = a0000, 64kb */ #endif } static void set_normal_mode() { int s = splhigh(); /* setup vga for normal operation mode again */ inb(crtc_addr+6); outb(ATC, 0x30); outb(ATC, 0x0C); #if SLOW_VGA outb(TSIDX, 0x02); outb(TSREG, 0x03); outb(TSIDX, 0x04); outb(TSREG, 0x02); outb(GDCIDX, 0x04); outb(GDCREG, 0x00); outb(GDCIDX, 0x05); outb(GDCREG, 0x10); if (crtc_addr == MONO_BASE) { outb(GDCIDX, 0x06); outb(GDCREG, 0x0A); /* addr = b0000, 32kb */ } else { outb(GDCIDX, 0x06); outb(GDCREG, 0x0E); /* addr = b8000, 32kb */ } #else outw(TSIDX, 0x0302); outw(TSIDX, 0x0204); outw(GDCIDX, 0x0004); outw(GDCIDX, 0x1005); if (crtc_addr == MONO_BASE) outw(GDCIDX, 0x0A06); /* addr = b0000, 32kb */ else outw(GDCIDX, 0x0E06); /* addr = b8000, 32kb */ #endif splx(s); } static void copy_font(int operation, int font_type, char* font_image) { int ch, line, segment, fontsize; u_char val; switch (font_type) { default: case FONT_8: segment = 0x8000; fontsize = 8; break; case FONT_14: segment = 0x4000; fontsize = 14; break; case FONT_16: segment = 0x0000; fontsize = 16; break; } outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */ outb(TSIDX, 0x01); outb(TSREG, val | 0x20); set_font_mode(); for (ch=0; ch < 256; ch++) for (line=0; line < fontsize; line++) if (operation) *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line) = font_image[(ch*fontsize)+line]; else font_image[(ch*fontsize)+line] = *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line); set_normal_mode(); outb(TSIDX, 0x01); outb(TSREG, val & 0xDF); /* enable screen */ } static void draw_mouse_image(scr_stat *scp) { caddr_t address; int i, font_size; char *font_buffer; u_short buffer[32]; u_short xoffset, yoffset; u_short *crt_pos = Crtat + (scp->mouse_pos - scp->scr_buf); xoffset = scp->mouse_xpos % 8; switch (scp->font) { default: case FONT_8: font_size = 8; font_buffer = font_8; yoffset = scp->mouse_ypos % 8; address = (caddr_t)VIDEOMEM+0x8000; break; case FONT_14: font_size = 14; font_buffer = font_14; yoffset = scp->mouse_ypos % 14; address = (caddr_t)VIDEOMEM+0x4000; break; case FONT_16: font_size = 16; font_buffer = font_16; yoffset = scp->mouse_ypos % 16; address = (caddr_t)VIDEOMEM; break; } bcopyw(font_buffer+((*(scp->mouse_pos) & 0xff)*font_size), &scp->mouse_cursor[0], font_size); bcopyw(font_buffer+((*(scp->mouse_pos+1) & 0xff)*font_size), &scp->mouse_cursor[32], font_size); bcopyw(font_buffer+((*(scp->mouse_pos+scp->xsize) & 0xff)*font_size), &scp->mouse_cursor[64], font_size); bcopyw(font_buffer+((*(scp->mouse_pos+scp->xsize+1) & 0xff)*font_size), &scp->mouse_cursor[96], font_size); for (i=0; imouse_cursor[i]<<8 | scp->mouse_cursor[i+32]; buffer[i+font_size]=scp->mouse_cursor[i+64]<<8|scp->mouse_cursor[i+96]; } for (i=0; i<16; i++) { buffer[i+yoffset] = ( buffer[i+yoffset] & ~(mouse_and_mask[i] >> xoffset)) | (mouse_or_mask[i] >> xoffset); } for (i=0; imouse_cursor[i] = (buffer[i] & 0xff00) >> 8; scp->mouse_cursor[i+32] = buffer[i] & 0xff; scp->mouse_cursor[i+64] = (buffer[i+font_size] & 0xff00) >> 8; scp->mouse_cursor[i+96] = buffer[i+font_size] & 0xff; } /* * if we didn't update entire screen, restore old mouse position * and check if we overwrote the cursor location.. */ if ((scp->status & UPDATE_MOUSE) && !(scp->status & UPDATE_SCREEN)) { u_short *ptr = scp->scr_buf + (scp->mouse_oldpos - Crtat); if (crt_pos != scp->mouse_oldpos) { *(scp->mouse_oldpos) = scp->mouse_saveunder[0]; *(scp->mouse_oldpos+1) = scp->mouse_saveunder[1]; *(scp->mouse_oldpos+scp->xsize) = scp->mouse_saveunder[2]; *(scp->mouse_oldpos+scp->xsize+1) = scp->mouse_saveunder[3]; } scp->mouse_saveunder[0] = *(scp->mouse_pos); scp->mouse_saveunder[1] = *(scp->mouse_pos+1); scp->mouse_saveunder[2] = *(scp->mouse_pos+scp->xsize); scp->mouse_saveunder[3] = *(scp->mouse_pos+scp->xsize+1); if ((scp->cursor_pos == (ptr)) || (scp->cursor_pos == (ptr+1)) || (scp->cursor_pos == (ptr+scp->xsize)) || (scp->cursor_pos == (ptr+scp->xsize+1)) || (scp->cursor_pos == (scp->mouse_pos)) || (scp->cursor_pos == (scp->mouse_pos+1)) || (scp->cursor_pos == (scp->mouse_pos+scp->xsize)) || (scp->cursor_pos == (scp->mouse_pos+scp->xsize+1))) scp->status &= ~CURSOR_SHOWN; } scp->mouse_oldpos = crt_pos; while (!(inb(crtc_addr+6) & 0x08)) /* wait for vertical retrace */ ; *(crt_pos) = *(scp->mouse_pos)&0xff00|0xd0; *(crt_pos+1) = *(scp->mouse_pos+1)&0xff00|0xd1; *(crt_pos+scp->xsize) = *(scp->mouse_pos+scp->xsize)&0xff00|0xd2; *(crt_pos+scp->xsize+1) = *(scp->mouse_pos+scp->xsize+1)&0xff00|0xd3; set_font_mode(); bcopy(scp->mouse_cursor, (char *)pa_to_va(address) + 0xd0 * 32, 128); set_normal_mode(); } static void save_palette(void) { int i; outb(PALRADR, 0x00); for (i=0x00; i<0x300; i++) palette[i] = inb(PALDATA); inb(crtc_addr+6); /* reset flip/flop */ } void load_palette(void) { int i; outb(PIXMASK, 0xFF); /* no pixelmask */ outb(PALWADR, 0x00); for (i=0x00; i<0x300; i++) outb(PALDATA, palette[i]); inb(crtc_addr+6); /* reset flip/flop */ outb(ATC, 0x20); /* enable palette */ } static void do_bell(scr_stat *scp, int pitch, int duration) { if (scp == cur_console) { if (configuration & VISUAL_BELL) { if (blink_in_progress) return; blink_in_progress = 4; blink_screen(scp); timeout((timeout_func_t)blink_screen, scp, hz/10); } else sysbeep(pitch, duration); } } static void blink_screen(scr_stat *scp) { if (blink_in_progress > 1) { if (blink_in_progress & 1) fillw(kernel_default.std_attr | scr_map[0x20], Crtat, scp->xsize * scp->ysize); else fillw(kernel_default.rev_attr | scr_map[0x20], Crtat, scp->xsize * scp->ysize); blink_in_progress--; timeout((timeout_func_t)blink_screen, scp, hz/10); } else { scp->status |= UPDATE_SCREEN; blink_in_progress = FALSE; if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } } #endif /* NSC */ Index: head/sys/i386/isa/syscons.h =================================================================== --- head/sys/i386/isa/syscons.h (revision 6781) +++ head/sys/i386/isa/syscons.h (revision 6782) @@ -1,208 +1,208 @@ /*- * Copyright (c) 1995 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software withough specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: syscons.h,v 1.1 1995/02/22 13:40:21 sos Exp $ + * $Id: syscons.h,v 1.2 1995/02/25 20:09:21 pst Exp $ */ /* * The APM stuff is -not- under conditional compilation because we don't want * the size of the scr_stat structure to vary depending upon if APM has been * compiled in or not, that can cause utilities and lkms to crash! */ #include /* vm things */ #define ISMAPPED(pa, width) \ (((pa) <= (u_long)0x1000 - (width)) \ || ((pa) >= 0xa0000 && (pa) <= 0x100000 - (width))) #define pa_to_va(pa) (KERNBASE + (pa)) /* works if ISMAPPED(pa...) */ /* printable chars */ #define PRINTABLE(ch) (ch>0x1B || (ch>0x0d && ch<0x1b) || ch<0x07) /* status flags */ #define LOCK_KEY_MASK 0x0000F #define LED_MASK 0x00007 #define UNKNOWN_MODE 0x00010 #define KBD_RAW_MODE 0x00020 #define SWITCH_WAIT_REL 0x00040 #define SWITCH_WAIT_ACQ 0x00080 #define BUFFER_SAVED 0x00100 #define CURSOR_ENABLED 0x00200 #define CURSOR_SHOWN 0x00400 #define MOUSE_ENABLED 0x00800 #define UPDATE_MOUSE 0x01000 #define UPDATE_SCREEN 0x02000 /* configuration flags */ #define VISUAL_BELL 0x00001 #define BLINK_CURSOR 0x00002 #define CHAR_CURSOR 0x00004 /* video hardware memory addresses */ #define VIDEOMEM 0x000A0000 /* misc defines */ #define FALSE 0 #define TRUE 1 #define MAX_ESC_PAR 5 #define LOAD 1 #define SAVE 0 #define COL 80 #define ROW 25 #define BELL_DURATION 5 #define BELL_PITCH 800 #define TIMER_FREQ 1193182 /* should be in isa.h */ #define CONSOLE_BUFSIZE 1024 #define PCBURST 128 #define FONT_8 0x001 #define FONT_14 0x002 #define FONT_16 0x004 #define HISTORY_SIZE 100*80 /* defines related to hardware addresses */ #define MONO_BASE 0x3B4 /* crt controller base mono */ #define COLOR_BASE 0x3D4 /* crt controller base color */ #define MISC 0x3C2 /* misc output register */ #define ATC IO_VGA+0x00 /* attribute controller */ #define TSIDX IO_VGA+0x04 /* timing sequencer idx */ #define TSREG IO_VGA+0x05 /* timing sequencer data */ #define PIXMASK IO_VGA+0x06 /* pixel write mask */ #define PALRADR IO_VGA+0x07 /* palette read address */ #define PALWADR IO_VGA+0x08 /* palette write address */ #define PALDATA IO_VGA+0x09 /* palette data register */ #define GDCIDX IO_VGA+0x0E /* graph data controller idx */ #define GDCREG IO_VGA+0x0F /* graph data controller data */ /* special characters */ #define cntlc 0x03 #define cntld 0x04 #define bs 0x08 #define lf 0x0a #define cr 0x0d #define del 0x7f typedef struct term_stat { int esc; /* processing escape sequence */ int num_param; /* # of parameters to ESC */ int last_param; /* last parameter # */ int param[MAX_ESC_PAR]; /* contains ESC parameters */ int cur_attr; /* current attributes */ int std_attr; /* normal attributes */ int rev_attr; /* reverse attributes */ } term_stat; typedef struct scr_stat { u_short *scr_buf; /* buffer when off screen */ int xpos; /* current X position */ int ypos; /* current Y position */ int xsize; /* X size */ int ysize; /* Y size */ term_stat term; /* terminal emulation stuff */ int status; /* status (bitfield) */ u_short *cursor_pos; /* cursor buffer position */ u_short cursor_saveunder; /* saved chars under cursor */ char cursor_start; /* cursor start line # */ char cursor_end; /* cursor end line # */ u_short *mouse_pos; /* mouse buffer position */ u_short *mouse_oldpos; /* mouse old buffer position */ u_short mouse_saveunder[4]; /* saved chars under mouse */ short mouse_xpos; /* mouse x coordinate */ short mouse_ypos; /* mouse y coordinate */ u_char mouse_cursor[128]; /* mouse cursor bitmap store */ u_short bell_duration; u_short bell_pitch; u_char border; /* border color */ u_char mode; /* mode */ u_char font; /* font on this screen */ pid_t pid; /* pid of controlling proc */ struct proc *proc; /* proc* of controlling proc */ struct vt_mode smode; /* switch mode */ u_short *history; /* circular history buffer */ u_short *history_head; /* current head position */ u_short *history_pos; /* position shown on screen */ u_short *history_save; /* save area index */ int history_size; /* size of history buffer */ struct apmhook r_hook; /* reconfiguration support */ } scr_stat; typedef struct default_attr { int std_attr; /* normal attributes */ int rev_attr; /* reverse attributes */ } default_attr; /* function prototypes */ int scprobe(struct isa_device *dev); int scattach(struct isa_device *dev); int scopen(dev_t dev, int flag, int mode, struct proc *p); int scclose(dev_t dev, int flag, int mode, struct proc *p); int scread(dev_t dev, struct uio *uio, int flag); int scwrite(dev_t dev, struct uio *uio, int flag); int scparam(struct tty *tp, struct termios *t); int scioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p); void scxint(dev_t dev); void scstart(struct tty *tp); void pccnprobe(struct consdev *cp); void pccninit(struct consdev *cp); void pccnputc(dev_t dev, char c); int pccngetc(dev_t dev); int pccncheckc(dev_t dev); void scintr(int unit); int pcmmap(dev_t dev, int offset, int nprot); static void scinit(void); static u_int scgetc(int noblock); -static struct tty *get_tty_ptr(dev_t dev); +struct tty *scdevtotty(dev_t dev); static scr_stat *get_scr_stat(dev_t dev); static scr_stat *alloc_scp(); static void init_scp(scr_stat *scp); static int get_scr_num(); static void scrn_timer(); static void clear_screen(scr_stat *scp); static int switch_scr(scr_stat *scp, u_int next_scr); static void exchange_scr(void); static inline void move_crsr(scr_stat *scp, int x, int y); static void scan_esc(scr_stat *scp, u_char c); static inline void draw_cursor(scr_stat *scp, int show); static void ansi_put(scr_stat *scp, u_char *buf, int len); static u_char *get_fstr(u_int c, u_int *len); static void update_leds(int which); static void history_to_screen(scr_stat *scp); static int history_up_line(scr_stat *scp); static int history_down_line(scr_stat *scp); static void kbd_wait(void); static void kbd_cmd(u_char command); static void set_mode(scr_stat *scp); void set_border(int color); static void set_vgaregs(char *modetable); static void set_font_mode(); static void set_normal_mode(); static void copy_font(int operation, int font_type, char* font_image); static void draw_mouse_image(scr_stat *scp); static void save_palette(void); void load_palette(void); static void do_bell(scr_stat *scp, int pitch, int duration); static void blink_screen(scr_stat *scp); Index: head/sys/isa/sio.c =================================================================== --- head/sys/isa/sio.c (revision 6781) +++ head/sys/isa/sio.c (revision 6782) @@ -1,2268 +1,2259 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.67 1995/02/25 20:09:14 pst Exp $ + * $Id: sio.c,v 1.68 1995/02/26 02:30:18 bde Exp $ */ #include "sio.h" #if NSIO > 0 /* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. */ #include #include #include #include #define TTYDEFCHARS /* XXX TK2.0 */ #include #undef TTYDEFCHARS #include #include #include #include #include #include #include #include #include #include #include #include /* XXX just to get at `imen' */ #include #include #include #include /* * XXX temporary kludges for 2.0 (XXX TK2.0). */ #define TSA_CARR_ON(tp) ((void *)&(tp)->t_rawq) #define TSA_OCOMPLETE(tp) ((void *)&(tp)->t_outq) #define TSA_OLOWAT(tp) ((void *)&(tp)->t_outq) void termioschars(t) struct termios *t; { bcopy(ttydefchars, t->c_cc, sizeof t->c_cc); } #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE) #define RS_IBUFSIZE 256 #define TTY_BI TTY_FE /* XXX */ #define TTY_OE TTY_PE /* XXX */ #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ #define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) #define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) #define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) #endif /* COM_MULTIPORT */ #define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) #define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */ #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart() and comflush()) * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_int Port_t; /* hardware port */ typedef u_char bool_t; /* boolean */ /* com device structure */ struct com_s { u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ u_char ftl; /* current rx fifo trigger level */ u_char ftl_init; /* ftl_max for next open() */ u_char ftl_max; /* maximum ftl for curent open() */ bool_t hasfifo; /* nonzero for 16550 UARTs */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t poll; /* nonzero if polling is required */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ u_char *obufend; /* end of output buffer */ u_char *optr; /* next char to output */ Port_t data_port; /* i/o ports */ Port_t int_id_port; Port_t iobase; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; struct timeval timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */ #define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Output buffer. Someday we should avoid copying. Twice. */ u_char obuf[256]; }; /* * The public functions in the com module ought to be declared in a com-driver * system header. */ /* Interrupt handling entry points. */ void siointr __P((int unit)); void siopoll __P((void)); /* Device switch entry points. */ int sioopen __P((dev_t dev, int oflags, int devtype, struct proc *p)); int sioclose __P((dev_t dev, int fflag, int devtype, struct proc *p)); int sioread __P((dev_t dev, struct uio *uio, int ioflag)); int siowrite __P((dev_t dev, struct uio *uio, int ioflag)); int sioioctl __P((dev_t dev, int cmd, caddr_t data, int fflag, struct proc *p)); void siostop __P((struct tty *tp, int rw)); #define sioreset noreset int sioselect __P((dev_t dev, int rw, struct proc *p)); #define siommap nommap #define siostrategy nostrategy /* Console device entry points. */ int siocncheckc __P((dev_t dev)); int siocngetc __P((dev_t dev)); struct consdev; void siocninit __P((struct consdev *cp)); void siocnprobe __P((struct consdev *cp)); void siocnputc __P((dev_t dev, int c)); static int sioattach __P((struct isa_device *dev)); static timeout_t siodtrwakeup; static void comflush __P((struct com_s *com)); static void comhardclose __P((struct com_s *com)); static void siointr1 __P((struct com_s *com)); static void commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static int sioprobe __P((struct isa_device *dev)); static void sioregisterdev __P((struct isa_device *id)); static void comstart __P((struct tty *tp)); static timeout_t comwakeup; static int tiocm_xxx2mcr __P((int tiocm_xxx)); #ifdef DSI_SOFT_MODEM static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr)); #endif /* DSI_SOFT_MODEM */ /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) static struct timeval intr_timestamp; struct isa_driver siodriver = { sioprobe, sioattach, "sio" }; #ifdef COMCONSOLE #undef COMCONSOLE #define COMCONSOLE 1 #else #define COMCONSOLE 0 #endif static int comconsole = CONUNIT; static speed_t comdefaultrate = TTYDEF_SPEED; static u_int com_events; /* input chars + weighted output completions */ static int commajor; #if 0 /* XXX TK2.0 */ struct tty *sio_tty[NSIO]; #else struct tty sio_tty[NSIO]; #endif extern struct tty *constty; /* XXX */ #ifdef KGDB #include "machine/remote-sl.h" extern int kgdb_dev; extern int kgdb_rate; extern int kgdb_debug_init; #endif static struct speedtab comspeedtab[] = { 0, 0, 50, COMBRD(50), 75, COMBRD(75), 110, COMBRD(110), 134, COMBRD(134), 150, COMBRD(150), 200, COMBRD(200), 300, COMBRD(300), 600, COMBRD(600), 1200, COMBRD(1200), 1800, COMBRD(1800), 2400, COMBRD(2400), 4800, COMBRD(4800), 9600, COMBRD(9600), 19200, COMBRD(19200), 38400, COMBRD(38400), 57600, COMBRD(57600), 115200, COMBRD(115200), -1, -1 }; /* XXX - configure this list */ static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static int sioprobe(dev) struct isa_device *dev; { static bool_t already_init; Port_t *com_ptr; bool_t failures[10]; int fn; struct isa_device *idev; Port_t iobase; u_char mcr_image; int result; if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ for (com_ptr = likely_com_ports; com_ptr < &likely_com_ports[sizeof likely_com_ports / sizeof likely_com_ports[0]]; ++com_ptr) outb(*com_ptr + com_mcr, 0); already_init = TRUE; } /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(dev)) { idev = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(dev)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", dev->id_unit, COM_MPMASTER(dev)); return (0); } if (!COM_NOTAST4(dev)) { outb(idev->id_iobase + com_scr, idev->id_irq ? 0x80 : 0); mcr_image = 0; } } #endif /* COM_MULTIPORT */ if (idev->id_irq == 0) mcr_image = 0; bzero(failures, sizeof failures); iobase = dev->id_iobase; /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ disable_intr(); /* EXTRA DELAY? */ /* * XXX DELAY() reenables CPU interrupts. This is a problem for * shared interrupts after the first device using one has been * successfully probed - config_isadev() has enabled the interrupt * in the ICU. */ outb(IO_ICU1 + 1, 0xff); /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ outb(iobase + com_cfcr, CFCR_DLAB); outb(iobase + com_dlbl, COMBRD(9600) & 0xff); outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8); outb(iobase + com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (9600 / 10)); /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); outb(iobase + com_ier, 0); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ outb(iobase + com_ier, IER_ETXRDY); /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); DELAY((1 + 2) * 1000000 / (9600 / 10)); /* * Turn off loopback mode so that the interrupt gate works again * (MCR_IENABLE was hidden). This should leave the device driving * an interrupt line high. It doesn't matter if the interrupt * line oscillates while we are not looking at it, since interrupts * are disabled. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ /* EXTRA DELAY? */ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; failures[1] = inb(iobase + com_ier) - IER_ETXRDY; failures[2] = inb(iobase + com_mcr) - mcr_image; if (idev->id_irq != 0) failures[3] = isa_irq_pending(idev) ? 0 : 1; failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; if (idev->id_irq != 0) failures[5] = isa_irq_pending(idev) ? 1 : 0; failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; /* * Turn off all device interrupts and check that they go off properly. * Leave MCR_IENABLE alone. For ports without a master port, it gates * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving at) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ outb(iobase + com_ier, 0); outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = inb(iobase + com_ier); if (idev->id_irq != 0) failures[8] = isa_irq_pending(idev) ? 1 : 0; failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; outb(IO_ICU1 + 1, imen); /* XXX */ enable_intr(); result = IO_COMSIZE; for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) { outb(iobase + com_mcr, 0); result = 0; if (COM_VERBOSE(dev)) printf("sio%d: probe test %d failed\n", dev->id_unit, fn); } return (result); } static struct kern_devconf kdc_sio[NSIO] = { { 0, 0, 0, /* filled in by dev_attach */ "sio", 0, { MDDT_ISA, 0, "tty" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ 0, /* parentdata */ DC_UNCONFIGURED, "RS-232 serial port" } }; static void sioregisterdev(id) struct isa_device *id; { int unit; unit = id->id_unit; if (unit != 0) kdc_sio[unit] = kdc_sio[0]; kdc_sio[unit].kdc_unit = unit; kdc_sio[unit].kdc_isa = id; kdc_sio[unit].kdc_state = DC_IDLE; dev_attach(&kdc_sio[unit]); } static int sioattach(isdp) struct isa_device *isdp; { struct com_s *com; static bool_t comwakeup_started = FALSE; Port_t iobase; int s; int unit; isdp->id_ri_flags |= RI_FAST; iobase = isdp->id_iobase; unit = isdp->id_unit; com = malloc(sizeof *com, M_TTYS, M_NOWAIT); if (com == NULL) return (0); /* * sioprobe() has initialized the device registers as follows: * o cfcr = CFCR_8BITS. * It is most important that CFCR_DLAB is off, so that the * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->no_irq = isdp->id_irq == 0; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->iobase = iobase; com->data_port = iobase + com_data; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; } termioschars(&com->it_in); com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); #ifdef DSI_SOFT_MODEM if((inb(iobase+7) ^ inb(iobase+7)) & 0x80) { printf(" Digicom Systems, Inc. SoftModem"); goto determined_type; } #endif /* DSI_SOFT_MODEM */ #ifdef COM_MULTIPORT if (!COM_ISMULTIPORT(isdp)) #endif { u_char scr; u_char scr1; u_char scr2; scr = inb(iobase + com_scr); outb(iobase + com_scr, 0xa5); scr1 = inb(iobase + com_scr); outb(iobase + com_scr, 0x5a); scr2 = inb(iobase + com_scr); outb(iobase + com_scr, scr); if (scr1 != 0xa5 || scr2 != 0x5a) { printf(" 8250"); goto determined_type; } } outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14); DELAY(100); switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_TRIGGER_1: printf(" 16450"); break; case FIFO_TRIGGER_4: printf(" 16450?"); break; case FIFO_TRIGGER_8: printf(" 16550?"); break; case FIFO_TRIGGER_14: printf(" 16550A"); if (COM_NOFIFO(isdp)) printf(" fifo disabled"); else { com->hasfifo = TRUE; com->ftl_init = FIFO_TRIGGER_14; com->tx_fifo_size = 16; } break; } outb(iobase + com_fifo, 0); determined_type: ; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(isdp)) { com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(isdp)) printf(" master"); printf(")"); com->no_irq = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(isdp))->id_irq == 0; } #endif /* COM_MULTIPORT */ printf("\n"); sioregisterdev(isdp); #ifdef KGDB if (kgdb_dev == makedev(commajor, unit)) { if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) kgdb_dev = -1; /* can't debug over console port */ else { int divisor; /* * XXX now unfinished and broken. Need to do * something more like a full open(). There's no * suitable interrupt handler so don't enable device * interrupts. Watch out for null tp's. */ outb(iobase + com_cfcr, CFCR_DLAB); divisor = ttspeedtab(kgdb_rate, comspeedtab); outb(iobase + com_dlbl, divisor & 0xFF); outb(iobase + com_dlbh, (u_int) divisor >> 8); outb(iobase + com_cfcr, CFCR_8BITS); outb(com->modem_status_port, com->mcr_image |= MCR_DTR | MCR_RTS); if (kgdb_debug_init) { /* * Print prefix of device name, * let kgdb_connect print the rest. */ printf("sio%d: ", unit); kgdb_connect(1); } else printf("sio%d: kgdb enabled\n", unit); } } #endif s = spltty(); com_addr(unit) = com; splx(s); if (!comwakeup_started) { comwakeup((void *)NULL); comwakeup_started = TRUE; } return (1); } /* ARGSUSED */ int sioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); if (mynor & CONTROL_MASK) return (0); #if 0 /* XXX TK2.0 */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else tp = com->tp = &sio_tty[unit]; #endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); if (error != 0) goto out; } kdc_sio[unit].kdc_state = DC_BUSY; if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "siobi", 0); if (error != 0) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; commctl(com, MCR_DTR | MCR_RTS, DMSET); com->ftl_max = com->ftl_init; com->poll = com->no_irq; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; /* * XXX we should goto open_top if comparam() slept. */ ttsetwater(tp); iobase = com->iobase; if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | FIFO_ENABLE | com->ftl); DELAY(100); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(100); (void) inb(com->data_port); } } disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); enable_intr(); /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "siobi" * instead of "siodcd". */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); --com->wopeners; if (error != 0) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } /*ARGSUSED*/ int sioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); splx(s); return (0); } static void comhardclose(com) struct com_s *com; { Port_t iobase; int s; struct tty *tp; int unit; unit = DEV_TO_UNIT(com->tp->t_dev); iobase = com->iobase; s = spltty(); com->poll = FALSE; com->do_timestamp = 0; outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); #ifdef KGDB /* do not disable interrupts or hang up if debugging */ if (kgdb_dev != makedev(commajor, unit)) #endif { outb(iobase + com_ier, 0); tp = com->tp; if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { commctl(com, MCR_RTS, DMSET); if (com->dtr_wait != 0) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ if (!(com->state & CS_DTR_OFF)) kdc_sio[unit].kdc_state = DC_IDLE; splx(s); } int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); tp = com_addr(MINOR_TO_UNIT(mynor))->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty && unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) constty = NULL; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; kdc_sio[DEV_TO_UNIT(com->tp->t_dev)].kdc_state = DC_IDLE; wakeup(&com->dtr_wait); } /* Interrupt routine for timekeeping purposes */ void siointrts(unit) int unit; { /* * XXX microtime() reenables CPU interrupts. We can't afford to * be interrupted and don't want to slow down microtime(), so lock * out interrupts in another way. */ outb(IO_ICU1 + 1, 0xff); microtime(&intr_timestamp); disable_intr(); outb(IO_ICU1 + 1, imen); siointr(unit); } void siointr(unit) int unit; { #ifndef COM_MULTIPORT siointr1(com_addr(unit)); #else /* COM_MULTIPORT */ struct com_s *com; bool_t possibly_more_intrs; /* * Loop until there is no activity on any port. This is necessary * to get an interrupt edge more than to avoid another interrupt. * If the IRQ signal is just an OR of the IRQ signals from several * devices, then the edge from one may be lost because another is * on. */ do { possibly_more_intrs = FALSE; for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && (inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { siointr1(com); possibly_more_intrs = TRUE; } } } while (possibly_more_intrs); #endif /* COM_MULTIPORT */ } static void siointr1(com) struct com_s *com; { u_char line_status; u_char modem_status; u_char *ioptr; u_char recv_data; if (com->do_timestamp) /* XXX a little bloat here... */ com->timestamp = intr_timestamp; while (TRUE) { line_status = inb(com->line_status_port); /* input event? (check first to help avoid overruns) */ while (line_status & LSR_RCV_MASK) { /* break/unnattached error bits or real input? */ if (!(line_status & LSR_RXRDY)) recv_data = 0; else recv_data = inb(com->data_port); ++com->bytes_in; if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #ifdef KGDB /* trap into kgdb? (XXX - needs testing and optim) */ if (recv_data == FRAME_END && !(com->tp->t_state & TS_ISOPEN) && kgdb_dev == makedev(commajor, unit)) { kgdb_connect(0); continue; } #endif /* KGDB */ ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { ++com_events; #if 0 /* for testing input latency vs efficiency */ if (com->iptr - com->ibuf == 8) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); /* XXX - move this out of isr */ if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } /* * "& 0x7F" is to avoid the gcc-1.40 generating a slow * jump from the top of the loop to here */ line_status = inb(com->line_status_port) & 0x7F; } /* modem status change? (always check before doing output) */ modem_status = inb(com->modem_status_port); if (modem_status != com->last_modem_status) { /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; setsofttty(); } /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } } /* output queued and everything ready? */ if (line_status & LSR_TXRDY && com->state >= (CS_ODEVREADY | CS_BUSY | CS_TTGO)) { ioptr = com->optr; if (com->tx_fifo_size > 1) { u_int ocount; ocount = com->obufend - ioptr; if (ocount > com->tx_fifo_size) ocount = com->tx_fifo_size; com->bytes_out += ocount; do outb(com->data_port, *ioptr++); while (--ocount != 0); } else { outb(com->data_port, *ioptr++); ++com->bytes_out; } com->optr = ioptr; if (ioptr >= com->obufend) { /* output just completed */ com_events += LOTS_OF_EVENTS; com->state ^= (CS_ODONE | CS_BUSY); setsofttty(); /* handle at high level ASAP */ } } /* finished? */ #ifndef COM_MULTIPORT if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } } static int tiocm_xxx2mcr(tiocm_xxx) int tiocm_xxx; { int mcr; mcr = 0; if (tiocm_xxx & TIOCM_DTR) mcr |= MCR_DTR; if (tiocm_xxx & TIOCM_RTS) mcr |= MCR_RTS; return (mcr); } int sioioctl(dev, cmd, data, flag, p) dev_t dev; int cmd; caddr_t data; int flag; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mcr; int msr; int mynor; int s; int tiocm_xxx; struct tty *tp; mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); iobase = com->iobase; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); #ifdef DSI_SOFT_MODEM /* * Download micro-code to Digicom modem. */ case TIOCDSIMICROCODE: { u_long l; u_char *p,*pi; pi = (u_char*)(*(caddr_t*)data); error = copyin(pi,&l,sizeof l); if(error) {return error;}; pi += sizeof l; p = malloc(l,M_TEMP,M_NOWAIT); if(!p) {return ENOBUFS;} error = copyin(pi,p,l); if(error) {free(p,M_TEMP); return error;}; if(error = LoadSoftModem( MINOR_TO_UNIT(mynor),iobase,l,p)) {free(p,M_TEMP); return error;} free(p,M_TEMP); return(0); } #endif /* DSI_SOFT_MODEM */ default: return (ENOTTY); } } tp = com->tp; if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); error = ttioctl(tp, cmd, data, flag); if (error >= 0) return (error); s = spltty(); switch (cmd) { case TIOCSBRK: outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: commctl(com, MCR_DTR, DMBIS); break; case TIOCCDTR: commctl(com, MCR_DTR, DMBIC); break; case TIOCMSET: commctl(com, tiocm_xxx2mcr(*(int *)data), DMSET); break; case TIOCMBIS: commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIS); break; case TIOCMBIC: commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIC); break; case TIOCMGET: tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */ mcr = com->mcr_image; if (mcr & MCR_DTR) tiocm_xxx |= TIOCM_DTR; if (mcr & MCR_RTS) tiocm_xxx |= TIOCM_RTS; msr = com->prev_modem_status; if (msr & MSR_CTS) tiocm_xxx |= TIOCM_CTS; if (msr & MSR_DCD) tiocm_xxx |= TIOCM_CD; if (msr & MSR_DSR) tiocm_xxx |= TIOCM_DSR; /* * XXX - MSR_RI is naturally volatile, and we make MSR_TERI * more volatile by reading the modem status a lot. Perhaps * we should latch both bits until the status is read here. */ if (msr & (MSR_RI | MSR_TERI)) tiocm_xxx |= TIOCM_RI; *(int *)data = tiocm_xxx; break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; default: splx(s); return (ENOTTY); } splx(s); return (0); } /* cancel pending output */ static void comflush(com) struct com_s *com; { disable_intr(); if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); enable_intr(); com->tp->t_state &= ~TS_BUSY; } void siopoll() { int unit; if (com_events == 0) return; repeat: for (unit = 0; unit < NSIO; ++unit) { u_char *buf; struct com_s *com; u_char *ibuf; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL) { /* * XXX forget any events related to closed devices * (actually never opened devices) so that we don't * loop. */ disable_intr(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; enable_intr(); if (incc != 0) log(LOG_DEBUG, "sio%d: %d events for device with no tp\n", unit, incc); continue; } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { /* * Prepare to reduce input latency for packet * discplines with a end of packet character. * XXX should be elsewhere. */ if (tp->t_line == SLIPDISC) com->hotchar = 0xc0; else if (tp->t_line == PPPDISC) com->hotchar = 0x7e; else com->hotchar = 0; buf = ibuf; disable_intr(); incc = com->iptr - buf; com_events -= incc; if (ibuf == com->ibuf1) ibuf = com->ibuf2; else ibuf = com->ibuf1; com->ibufend = ibuf + RS_IBUFSIZE; com->ihighwater = ibuf + RS_IHIGHWATER; com->iptr = ibuf; /* * There is now room for another low-level buffer full * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ /* * XXX this used not to look at CS_RTS_IFLOW. The * change is to allow full control of MCR_RTS via * ioctls after turning CS_RTS_IFLOW off. Check * for races. We shouldn't allow the ioctls while * CS_RTS_IFLOW is on. */ if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_TBLOCK)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); enable_intr(); com->ibuf = ibuf; } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; disable_intr(); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); if (delta_modem_status & MSR_DCD) (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } if (com->state & CS_ODONE) { comflush(com); /* XXX - why isn't the table used for t_line == 0? */ if (tp->t_line != 0) (*linesw[tp->t_line].l_start)(tp); else comstart(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) continue; if (((com->state & CS_RTS_IFLOW) || (tp->t_iflag & IXOFF)) && !(tp->t_state & TS_TBLOCK) && tp->t_rawq.c_cc + incc >= RB_I_HIGH_WATER /* * XXX - need flow control for all line disciplines. * Only have it in standard one now. */ && linesw[tp->t_line].l_rint == ttyinput) { if ((tp->t_iflag & IXOFF) && tp->t_cc[VSTOP] != _POSIX_VDISABLE && putc(tp->t_cc[VSTOP], &tp->t_outq) == 0 || (com->state & CS_RTS_IFLOW)) { tp->t_state |= TS_TBLOCK; ttstart(tp); } } /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ if (!(tp->t_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXOFF | IXON)) && !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG | PENDIN)) && !(tp->t_state & (TS_CNTTB | TS_LNCH)) && linesw[tp->t_line].l_rint == ttyinput) { tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; ttstart(tp); } } else { do { u_char line_status; int recv_data; line_status = (u_char) buf[CE_INPUT_OFFSET]; recv_data = (u_char) *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } (*linesw[tp->t_line].l_rint)(recv_data, tp); } while (--incc > 0); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { u_int cfcr; int cflag; struct com_s *com; int divisor; int error; Port_t iobase; int s; int unit; /* check requested parameters */ divisor = ttspeedtab(t->c_ospeed, comspeedtab); if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; s = spltty(); if (divisor == 0) commctl(com, MCR_DTR, DMBIC); /* hang up line */ else commctl(com, MCR_DTR, DMBIS); cflag = t->c_cflag; switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; default: cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if (!(cflag & PARODD)) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; if (com->hasfifo) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and * the total latency is less than about 30 msec. These * latencies are reasonable for humans. Serial comms * protocols shouldn't expect anything better since modem * latencies are larger. */ com->ftl = t->c_ospeed <= 4800 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_14; if (com->ftl > com->ftl_max) com->ftl = com->ftl_max; outb(iobase + com_fifo, FIFO_ENABLE | com->ftl); } /* * Some UARTs lock up if the divisor latch registers are selected * while the UART is doing output (they refuse to transmit anything * more until given a hard reset). Fix this by stopping filling * the device buffers and waiting for them to drain. Reading the * line status port outside of siointr1() might lose some receiver * error bits, but that is acceptable here. */ disable_intr(); retry: com->state &= ~CS_TTGO; enable_intr(); while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) { error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH, "siotx", hz / 100); if (error != 0 && error != EAGAIN) { if (!(tp->t_state & TS_TTSTOP)) { disable_intr(); com->state |= CS_TTGO; enable_intr(); } splx(s); return (error); } } disable_intr(); /* very important while com_data is hidden */ /* * XXX - clearing CS_TTGO is not sufficient to stop further output, * because siopoll() calls comstart() which usually sets it again * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be * sufficient, for similar reasons. */ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) goto retry; if (divisor != 0) { outb(iobase + com_cfcr, cfcr | CFCR_DLAB); outb(iobase + com_dlbl, divisor & 0xFF); outb(iobase + com_dlbh, (u_int) divisor >> 8); } outb(iobase + com_cfcr, com->cfcr_image = cfcr); if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */ else com->state &= ~CS_RTS_IFLOW; /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state &= ~CS_CTS_OFLOW; com->state |= CS_ODEVREADY; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; } /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); enable_intr(); splx(s); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); s = spltty(); disable_intr(); if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; if (tp->t_state & TS_TBLOCK) { if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } else { /* * XXX don't raise MCR_RTS if CTS_RTS_IFLOW is off. Set it * appropriately in comparam() if RTS-flow is being changed. * Check for races. */ if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) goto out; #if 0 /* XXX TK2.0 */ if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) ttwwakeup(tp); #else if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } selwakeup(&tp->t_wsel); } #endif if (tp->t_state & TS_BUSY) { disable_intr(); siointr1(com); enable_intr(); } else if (tp->t_outq.c_cc != 0) { u_int ocount; tp->t_state |= TS_BUSY; ocount = q_to_b(&tp->t_outq, com->obuf, sizeof com->obuf); disable_intr(); com->obufend = (com->optr = com->obuf) + ocount; com->state |= CS_BUSY; siointr1(com); /* fake interrupt to start output */ enable_intr(); } out: splx(s); } void siostop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (rw & FWRITE) comflush(com); disable_intr(); if (rw & FREAD) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; enable_intr(); } struct tty * siodevtotty(dev) dev_t dev; { int mynor; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO) return (NULL); return (&sio_tty[unit]); -} - -int -sioselect(dev, rw, p) - dev_t dev; - int rw; - struct proc *p; -{ - return (ttyselect(siodevtotty(dev), rw, p)); } static void commctl(com, bits, how) struct com_s *com; int bits; int how; { disable_intr(); switch (how) { case DMSET: outb(com->modem_ctl_port, com->mcr_image = bits | (com->mcr_image & MCR_IENABLE)); break; case DMBIS: outb(com->modem_ctl_port, com->mcr_image |= bits); break; case DMBIC: outb(com->modem_ctl_port, com->mcr_image &= ~bits); break; } enable_intr(); } static void comwakeup(chan) void *chan; { struct com_s *com; static int log_countdown = 1; int unit; timeout(comwakeup, (caddr_t)NULL, hz > 200 ? hz / 200 : 1); if (com_events != 0) { int s; s = splsofttty(); siopoll(); splx(s); } /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); } } /* * Check for and log errors, but not too often. */ if (--log_countdown > 0) return; log_countdown = hz > 200 ? 200 : hz; for (unit = 0; unit < NSIO; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; enable_intr(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); #if 0 /* * XXX if we resurrect this then we should move * the dropping of the ftl to somewhere with less * latency. */ if (errnum == CE_OVERRUN && com->hasfifo && com->ftl > FIFO_TRIGGER_1) { static u_char ftl_in_bytes[] = { 1, 4, 8, 14, }; com->ftl_init = FIFO_TRIGGER_8; #define FIFO_TRIGGER_DELTA FIFO_TRIGGER_4 com->ftl_max = com->ftl -= FIFO_TRIGGER_DELTA; outb(com->iobase + com_fifo, FIFO_ENABLE | com->ftl); log(LOG_DEBUG, "sio%d: reduced fifo trigger level to %d\n", unit, ftl_in_bytes[com->ftl / FIFO_TRIGGER_DELTA]); } #endif } } } /* * Following are all routines needed for SIO to act as console */ #include "i386/i386/cons.h" struct siocnstate { u_char dlbl; u_char dlbh; u_char ier; u_char cfcr; u_char mcr; }; static Port_t siocniobase; static void siocnclose __P((struct siocnstate *sp)); static void siocnopen __P((struct siocnstate *sp)); static void siocntxwait __P((void)); static void siocntxwait() { int timo; /* * Wait for any pending transmission to finish. Required to avoid * the UART lockup bug when the speed is changed, and for normal * transmits. */ timo = 100000; while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY) && --timo != 0) ; } static void siocnopen(sp) struct siocnstate *sp; { int divisor; Port_t iobase; /* * Save all the device control registers except the fifo register * and set our default ones (cs8 -parenb speed=comdefaultrate). * We can't save the fifo register since it is read-only. */ iobase = siocniobase; sp->ier = inb(iobase + com_ier); outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ siocntxwait(); sp->cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB); sp->dlbl = inb(iobase + com_dlbl); sp->dlbh = inb(iobase + com_dlbh); divisor = ttspeedtab(comdefaultrate, comspeedtab); outb(iobase + com_dlbl, divisor & 0xFF); outb(iobase + com_dlbh, (u_int) divisor >> 8); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); /* * We don't want interrupts, but must be careful not to "disable" * them by clearing the MCR_IENABLE bit, since that might cause * an interrupt by floating the IRQ line. */ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void siocnclose(sp) struct siocnstate *sp; { Port_t iobase; /* * Restore the device control registers. */ siocntxwait(); iobase = siocniobase; outb(iobase + com_cfcr, CFCR_DLAB); outb(iobase + com_dlbl, sp->dlbl); outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); } void siocnprobe(cp) struct consdev *cp; { int unit; /* locate the major number */ /* XXX - should be elsewhere since KGDB uses it */ for (commajor = 0; commajor < nchrdev; commajor++) if (cdevsw[commajor].d_open == sioopen) break; /* XXX: ick */ unit = DEV_TO_UNIT(CONUNIT); siocniobase = CONADDR; /* make sure hardware exists? XXX */ /* initialize required fields */ cp->cn_dev = makedev(commajor, unit); if (COMCONSOLE || boothowto & RB_SERIAL) cp->cn_pri = CN_REMOTE; /* Force a serial port console */ else cp->cn_pri = CN_NORMAL; } void siocninit(cp) struct consdev *cp; { /* * XXX can delete more comconsole stuff now that i/o routines are * fairly reentrant. */ comconsole = DEV_TO_UNIT(cp->cn_dev); } int siocncheckc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); if (inb(iobase + com_lsr) & LSR_RXRDY) c = inb(iobase + com_data); else c = 0; siocnclose(&sp); splx(s); return (c); } int siocngetc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp); splx(s); return (c); } void siocnputc(dev, c) dev_t dev; int c; { int s; struct siocnstate sp; s = spltty(); siocnopen(&sp); siocntxwait(); outb(siocniobase + com_data, c); siocnclose(&sp); splx(s); } #ifdef DSI_SOFT_MODEM /* * The magic code to download microcode to a "Connection 14.4+Fax" * modem from Digicom Systems Inc. Very magic. */ #define DSI_ERROR(str) { ptr = str; goto error; } static int LoadSoftModem(int unit, int base_io, u_long size, u_char *ptr) { int int_c,int_k; int data_0188, data_0187; /* * First see if it is a DSI SoftModem */ if(!((inb(base_io+7) ^ inb(base_io+7) & 0x80))) return ENODEV; data_0188 = inb(base_io+4); data_0187 = inb(base_io+3); outb(base_io+3,0x80); outb(base_io+4,0x0C); outb(base_io+0,0x31); outb(base_io+1,0x8C); outb(base_io+7,0x10); outb(base_io+7,0x19); if(0x18 != (inb(base_io+7) & 0x1A)) DSI_ERROR("dsp bus not granted"); if(0x01 != (inb(base_io+7) & 0x01)) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x01 != (inb(base_io+7) & 0x01)) DSI_ERROR("program mem not granted"); } int_c = 0; while(1) { if(int_c >= 7 || size <= 0x1800) break; for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; int_c++; } if(size > 0x1800) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; while(size > 0x1800) { for(int_k = 0 ; int_k < 0xC00; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; } if(size < 0x1800) { for(int_k=0;int_k 0) { if(int_c == 7) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } else { for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } } outb(base_io+7,0x11); outb(base_io+7,3); outb(base_io+4,data_0188 & 0xfb); outb(base_io+3,data_0187); return 0; error: printf("sio%d: DSI SoftModem microcode load failed: <%s>\n",ptr); outb(base_io+7,0x00); \ outb(base_io+3,data_0187); \ outb(base_io+4,data_0188); \ return EIO; } #endif /* DSI_SOFT_MODEM */ #endif /* NSIO > 0 */ Index: head/sys/isa/syscons.c =================================================================== --- head/sys/isa/syscons.c (revision 6781) +++ head/sys/isa/syscons.c (revision 6782) @@ -1,2978 +1,2967 @@ /*- * Copyright (c) 1992-1995 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software withough specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: syscons.c,v 1.104 1995/02/22 13:40:19 sos Exp $ + * $Id: syscons.c,v 1.105 1995/02/25 20:09:16 pst Exp $ */ #include "sc.h" #include "apm.h" #if NSC > 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(MAXCONS) #define MAXCONS 16 #endif /* this may break on older VGA's but is usefull on real 32 bit systems */ #define bcopyw bcopy static default_attr user_default = { (FG_LIGHTGREY | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; static default_attr kernel_default = { (FG_WHITE | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; static scr_stat main_console; static scr_stat *console[MAXCONS]; scr_stat *cur_console; static scr_stat *new_scp, *old_scp; static term_stat kernel_console; static default_attr *current_default; static char init_done = FALSE; static char switch_in_progress = FALSE; static char blink_in_progress = FALSE; static char write_in_progress = FALSE; u_int crtc_addr = MONO_BASE; static char crtc_vga = FALSE; static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0, metas = 0; static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0; static char *font_8 = NULL, *font_14 = NULL, *font_16 = NULL; static int fonts_loaded = 0; char palette[3*256]; static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab); #if ASYNCH static u_char kbd_reply = 0; #endif static int delayed_next_scr = FALSE; static int configuration = 0; /* current setup */ static long scrn_blank_time = 0; /* screen saver timeout value */ int scrn_blanked = FALSE; /* screen saver active flag */ static int scrn_saver = 0; /* screen saver routine */ static long scrn_time_stamp; u_char scr_map[256]; static char *video_mode_ptr = NULL; static u_short mouse_and_mask[16] = { 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80, 0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000 }; static u_short mouse_or_mask[16] = { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800, 0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000 }; void none_saver(int blank) { } void (*current_saver)() = none_saver; /* OS specific stuff */ #ifdef not_yet_done #define VIRTUAL_TTY(x) (sccons[x] = ttymalloc(sccons[x])) struct CONSOLE_TTY (sccons[MAXCONS] = ttymalloc(sccons[MAXCONS])) struct tty *sccons[MAXCONS+1]; #else #define VIRTUAL_TTY(x) &sccons[x] #define CONSOLE_TTY &sccons[MAXCONS] struct tty sccons[MAXCONS+1]; #endif #define MONO_BUF pa_to_va(0xB0000) #define CGA_BUF pa_to_va(0xB8000) u_short *Crtat = (u_short *)MONO_BUF; #define WRAPHIST(scp, pointer, offset)\ ((scp->history) + ((((pointer) - (scp->history)) + (scp->history_size)\ + (offset)) % (scp->history_size))) struct isa_driver scdriver = { scprobe, scattach, "sc", 1 }; int scprobe(struct isa_device *dev) { int i, retries = 5; unsigned char val; /* Enable interrupts and keyboard controller */ kbd_wait(); outb(KB_STAT, KB_WRITE); kbd_wait(); outb(KB_DATA, KB_MODE); /* flush any noise in the buffer */ while (inb(KB_STAT) & KB_BUF_FULL) { DELAY(10); (void) inb(KB_DATA); } /* Reset keyboard hardware */ while (retries--) { kbd_wait(); outb(KB_DATA, KB_RESET); for (i=0; i<100000; i++) { DELAY(10); val = inb(KB_DATA); if (val == KB_ACK || val == KB_ECHO) goto gotres; if (val == KB_RESEND) break; } } gotres: if (!retries) printf("scprobe: keyboard won't accept RESET command\n"); else { gotack: DELAY(10); while ((inb(KB_STAT) & KB_BUF_FULL) == 0) DELAY(10); DELAY(10); val = inb(KB_DATA); if (val == KB_ACK) goto gotack; if (val != KB_RESET_DONE) printf("scprobe: keyboard RESET failed %02x\n", val); } #ifdef XT_KEYBOARD kbd_wait(); outb(KB_DATA, 0xF0); kbd_wait(); outb(KB_DATA, 1) kbd_wait(); #endif /* XT_KEYBOARD */ return (IO_KBDSIZE); } static struct kern_devconf kdc_sc[NSC] = { 0, 0, 0, /* filled in by dev_attach */ "sc", 0, { MDDT_ISA, 0, "tty" }, isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, /* parent */ 0, /* parentdata */ DC_BUSY, /* the console is almost always busy */ "Graphics console" }; static inline void sc_registerdev(struct isa_device *id) { if(id->id_unit) kdc_sc[id->id_unit] = kdc_sc[0]; kdc_sc[id->id_unit].kdc_unit = id->id_unit; kdc_sc[id->id_unit].kdc_isa = id; dev_attach(&kdc_sc[id->id_unit]); } #if NAPM > 0 static int scresume(void *dummy) { shfts = 0; ctls = 0; alts = 0; agrs = 0; metas = 0; return 0; } #endif int scattach(struct isa_device *dev) { scr_stat *scp; scinit(); configuration = dev->id_flags; printf("sc%d: ", dev->id_unit); if (crtc_vga) if (crtc_addr == MONO_BASE) printf("VGA mono"); else printf("VGA color"); else if (crtc_addr == MONO_BASE) printf("MDA/hercules"); else printf("CGA/EGA"); printf(" <%d virtual consoles, flags=0x%x>\n", MAXCONS, configuration); scp = console[0]; scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_NOWAIT); /* copy screen to buffer */ bcopyw(Crtat, scp->scr_buf, scp->xsize * scp->ysize * sizeof(u_short)); scp->cursor_pos = scp->scr_buf + scp->xpos + scp->ypos * scp->xsize; scp->mouse_pos = scp->scr_buf; /* initialize history buffer & pointers */ scp->history_head = scp->history_pos = scp->history = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_NOWAIT); bzero(scp->history_head, scp->history_size*sizeof(u_short)); if (crtc_vga) { font_8 = (char *)malloc(8*256, M_DEVBUF, M_NOWAIT); font_14 = (char *)malloc(14*256, M_DEVBUF, M_NOWAIT); font_16 = (char *)malloc(16*256, M_DEVBUF, M_NOWAIT); copy_font(SAVE, FONT_16, font_16); fonts_loaded = FONT_16; scp->font = FONT_16; save_palette(); } /* get screen update going */ scrn_timer(); update_leds(scp->status); sc_registerdev(dev); #if NAPM > 0 scp->r_hook.ah_fun = scresume; scp->r_hook.ah_arg = NULL; scp->r_hook.ah_name = "system keyboard"; scp->r_hook.ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_RESUME , &scp->r_hook); #endif return 0; } struct tty *scdevtotty(dev_t dev) { int unit = minor(dev); if (unit > MAXCONS || unit < 0) return(NULL); if (unit == MAXCONS) return CONSOLE_TTY; return VIRTUAL_TTY(unit); -} - -int -scselect(dev_t dev, int rw, struct proc *p) -{ - struct tty *tp = scdevtotty(dev); - - if (tp == NULL) - return(ENXIO); - - return (ttyselect(tp, rw, p)); } static scr_stat *get_scr_stat(dev_t dev) { int unit = minor(dev); if (unit > MAXCONS || unit < 0) return(NULL); if (unit == MAXCONS) return console[0]; return console[unit]; } static int get_scr_num() { int i = 0; while ((i < MAXCONS) && (cur_console != console[i])) i++; return i < MAXCONS ? i : 0; } int scopen(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); tp->t_oproc = scstart; tp->t_param = scparam; tp->t_dev = dev; if (!(tp->t_state & TS_ISOPEN)) { ttychars(tp); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; scparam(tp, &tp->t_termios); ttsetwater(tp); } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) return(EBUSY); tp->t_state |= TS_CARR_ON; tp->t_cflag |= CLOCAL; if (!console[minor(dev)]) console[minor(dev)] = alloc_scp(); return((*linesw[tp->t_line].l_open)(dev, tp)); } int scclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); struct scr_stat *scp; if (!tp) return(ENXIO); if (minor(dev) < MAXCONS) { scp = get_scr_stat(tp->t_dev); if (scp->status & SWITCH_WAIT_ACQ) wakeup((caddr_t)&scp->smode); #if not_yet_done if (scp == &main_console) { scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; } else { free(scp->scr_buf, M_DEVBUF); free(scp->history, M_DEVBUF); free(scp, M_DEVBUF); console[minor(dev)] = NULL; } #else scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; #endif } (*linesw[tp->t_line].l_close)(tp, flag); ttyclose(tp); return(0); } int scread(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); return((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int scwrite(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); return((*linesw[tp->t_line].l_write)(tp, uio, flag)); } void scintr(int unit) { static struct tty *cur_tty; int c, len; u_char *cp; /* make screensaver happy */ scrn_time_stamp = time.tv_sec; if (scrn_blanked) { (*current_saver)(FALSE); cur_console->status |= UPDATE_SCREEN; } c = scgetc(1); cur_tty = VIRTUAL_TTY(get_scr_num()); if (!(cur_tty->t_state & TS_ISOPEN)) if (!((cur_tty = CONSOLE_TTY)->t_state & TS_ISOPEN)) return; switch (c & 0xff00) { case 0x0000: /* normal key */ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); break; case NOKEY: /* nothing there */ break; case FKEY: /* function key, return string */ if (cp = get_fstr((u_int)c, (u_int *)&len)) { while (len-- > 0) (*linesw[cur_tty->t_line].l_rint)(*cp++ & 0xFF, cur_tty); } break; case MKEY: /* meta is active, prepend ESC */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); break; case BKEY: /* backtab fixed sequence (esc [ Z) */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)('[', cur_tty); (*linesw[cur_tty->t_line].l_rint)('Z', cur_tty); break; } } int scparam(struct tty *tp, struct termios *t) { tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; return 0; } int scioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { int i, error; struct tty *tp; struct trapframe *fp; scr_stat *scp; tp = scdevtotty(dev); if (!tp) return ENXIO; scp = get_scr_stat(tp->t_dev); switch (cmd) { /* process console hardware related ioctl's */ case GIO_ATTR: /* get current attributes */ *(int*)data = scp->term.cur_attr; return 0; case GIO_COLOR: /* is this a color console ? */ if (crtc_addr == COLOR_BASE) *(int*)data = 1; else *(int*)data = 0; return 0; case CONS_CURRENT: /* get current adapter type */ if (crtc_vga) *(int*)data = KD_VGA; else if (crtc_addr == MONO_BASE) *(int*)data = KD_MONO; else *(int*)data = KD_CGA; return 0; case CONS_GET: /* get current video mode */ *(int*)data = scp->mode; return 0; case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */ scrn_blank_time = *(int*)data; return 0; case CONS_CURSORTYPE: /* set cursor type blink/noblink */ if ((*(int*)data) & 0x01) configuration |= BLINK_CURSOR; else configuration &= ~BLINK_CURSOR; if ((*(int*)data) & 0x02) configuration |= CHAR_CURSOR; else configuration &= ~CHAR_CURSOR; return 0; case CONS_BELLTYPE: /* set bell type sound/visual */ if (*data) configuration |= VISUAL_BELL; else configuration &= ~VISUAL_BELL; return 0; case CONS_HISTORY: /* set history size */ if (*data) { free(scp->history, M_DEVBUF); scp->history_size = *(int*)data; if (scp->history_size < scp->ysize) scp->history = NULL; else { scp->history_size *= scp->xsize; scp->history_head = scp->history_pos = scp->history = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_NOWAIT); bzero(scp->history_head, scp->history_size*sizeof(u_short)); } return 0; } else return EINVAL; case CONS_MOUSECTL: /* control mouse arrow */ { mouse_info_t *mouse = (mouse_info_t*)data; int fontsize; switch (scp->font) { default: case FONT_8: fontsize = 8; break; case FONT_14: fontsize = 14; break; case FONT_16: fontsize = 16; break; } switch (mouse->operation) { case MOUSE_SHOW: if (!(scp->status & MOUSE_ENABLED)) { scp->mouse_oldpos = Crtat + (scp->mouse_pos - scp->scr_buf); scp->status |= (UPDATE_MOUSE | MOUSE_ENABLED); } else return EINVAL; break; case MOUSE_HIDE: if (scp->status & MOUSE_ENABLED) { scp->status &= ~MOUSE_ENABLED; scp->status |= UPDATE_MOUSE; } else return EINVAL; break; case MOUSE_MOVEABS: scp->mouse_xpos = mouse->x; scp->mouse_ypos = mouse->y; goto set_mouse_pos; case MOUSE_MOVEREL: scp->mouse_xpos += mouse->x; scp->mouse_ypos += mouse->y; set_mouse_pos: if (scp->mouse_xpos < 0) scp->mouse_xpos = 0; if (scp->mouse_ypos < 0) scp->mouse_ypos = 0; if (scp->mouse_xpos >= scp->xsize*8) scp->mouse_xpos = (scp->xsize*8)-1; if (scp->mouse_ypos >= scp->ysize*fontsize) scp->mouse_ypos = (scp->ysize*fontsize)-1; scp->mouse_pos = scp->scr_buf + (scp->mouse_ypos/fontsize)*scp->xsize + scp->mouse_xpos/8; if (scp->status & MOUSE_ENABLED) scp->status |= UPDATE_MOUSE; break; case MOUSE_GETPOS: mouse->x = scp->mouse_xpos; mouse->y = scp->mouse_ypos; return 0; default: return EINVAL; } /* make screensaver happy */ if (scp == cur_console) { scrn_time_stamp = time.tv_sec; if (scrn_blanked) { (*current_saver)(FALSE); scp->status |= UPDATE_SCREEN; } } return 0; } case CONS_GETINFO: /* get current (virtual) console info */ { vid_info_t *ptr = (vid_info_t*)data; if (ptr->size == sizeof(struct vid_info)) { ptr->m_num = get_scr_num(); ptr->mv_col = scp->xpos; ptr->mv_row = scp->ypos; ptr->mv_csz = scp->xsize; ptr->mv_rsz = scp->ysize; ptr->mv_norm.fore = (scp->term.std_attr & 0x0f00)>>8; ptr->mv_norm.back = (scp->term.std_attr & 0xf000)>>12; ptr->mv_rev.fore = (scp->term.rev_attr & 0x0f00)>>8; ptr->mv_rev.back = (scp->term.rev_attr & 0xf000)>>12; ptr->mv_grfc.fore = 0; /* not supported */ ptr->mv_grfc.back = 0; /* not supported */ ptr->mv_ovscan = scp->border; ptr->mk_keylock = scp->status & LOCK_KEY_MASK; return 0; } return EINVAL; } case CONS_GETVERS: /* get version number */ *(int*)data = 0x200; /* version 2.0 */ return 0; /* VGA TEXT MODES */ case SW_VGA_C40x25: case SW_VGA_C80x25: case SW_VGA_M80x25: case SW_VGA_C80x30: case SW_VGA_M80x30: case SW_VGA_C80x50: case SW_VGA_M80x50: case SW_VGA_C80x60: case SW_VGA_M80x60: case SW_B40x25: case SW_C40x25: case SW_B80x25: case SW_C80x25: case SW_ENH_B40x25: case SW_ENH_C40x25: case SW_ENH_B80x25: case SW_ENH_C80x25: case SW_ENH_B80x43: case SW_ENH_C80x43: if (!crtc_vga || video_mode_ptr == NULL) return ENXIO; switch (cmd & 0xff) { case M_VGA_C80x60: case M_VGA_M80x60: if (!(fonts_loaded & FONT_8)) return EINVAL; scp->xsize = 80; scp->ysize = 60; break; case M_VGA_C80x50: case M_VGA_M80x50: if (!(fonts_loaded & FONT_8)) return EINVAL; scp->xsize = 80; scp->ysize = 50; break; case M_ENH_B80x43: case M_ENH_C80x43: if (!(fonts_loaded & FONT_8)) return EINVAL; scp->xsize = 80; scp->ysize = 43; break; case M_VGA_C80x30: case M_VGA_M80x30: scp->xsize = 80; scp->ysize = 30; break; default: if ((cmd & 0xff) > M_VGA_CG320) return EINVAL; else scp->xsize = *(video_mode_ptr+((cmd&0xff)*64)); scp->ysize = *(video_mode_ptr+((cmd&0xff)*64)+1)+1; break; } scp->mode = cmd & 0xff; scp->status &= ~UNKNOWN_MODE; /* text mode */ free(scp->scr_buf, M_DEVBUF); scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_NOWAIT); if (scp == cur_console) set_mode(scp); clear_screen(scp); if (tp->t_winsize.ws_col != scp->xsize || tp->t_winsize.ws_row != scp->ysize) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; /* GRAPHICS MODES */ case SW_BG320: case SW_BG640: case SW_CG320: case SW_CG320_D: case SW_CG640_E: case SW_CG640x350: case SW_ENH_CG640: case SW_BG640x480: case SW_CG640x480: case SW_VGA_CG320: if (!crtc_vga || video_mode_ptr == NULL) return ENXIO; scp->mode = cmd & 0xFF; scp->status |= UNKNOWN_MODE; /* graphics mode */ scp->xsize = (*(video_mode_ptr + (scp->mode*64))) * 8; scp->ysize = (*(video_mode_ptr + (scp->mode*64) + 1) + 1) * (*(video_mode_ptr + (scp->mode*64) + 2)); set_mode(scp); /* clear_graphics();*/ if (tp->t_winsize.ws_xpixel != scp->xsize || tp->t_winsize.ws_ypixel != scp->ysize) { tp->t_winsize.ws_xpixel = scp->xsize; tp->t_winsize.ws_ypixel = scp->ysize; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; case VT_SETMODE: /* set screen switcher mode */ bcopy(data, &scp->smode, sizeof(struct vt_mode)); if (scp->smode.mode == VT_PROCESS) { scp->proc = p; scp->pid = scp->proc->p_pid; } return 0; case VT_GETMODE: /* get screen switcher mode */ bcopy(&scp->smode, data, sizeof(struct vt_mode)); return 0; case VT_RELDISP: /* screen switcher ioctl */ switch(*data) { case VT_FALSE: /* user refuses to release screen, abort */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { old_scp->status &= ~SWITCH_WAIT_REL; switch_in_progress = FALSE; return 0; } return EINVAL; case VT_TRUE: /* user has released screen, go on */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { scp->status &= ~SWITCH_WAIT_REL; exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; return 0; } return EINVAL; case VT_ACKACQ: /* acquire acknowledged, switch completed */ if (scp == new_scp && (scp->status & SWITCH_WAIT_ACQ)) { scp->status &= ~SWITCH_WAIT_ACQ; switch_in_progress = FALSE; return 0; } return EINVAL; default: return EINVAL; } /* NOT REACHED */ case VT_OPENQRY: /* return free virtual console */ for (i = 0; i < MAXCONS; i++) { tp = VIRTUAL_TTY(i); if (!(tp->t_state & TS_ISOPEN)) { *data = i + 1; return 0; } } return EINVAL; case VT_ACTIVATE: /* switch to screen *data */ return switch_scr(scp, (*data) - 1); case VT_WAITACTIVE: /* wait for switch to occur */ if (*data > MAXCONS || *data < 0) return EINVAL; if (minor(dev) == (*data) - 1) return 0; if (*data == 0) { if (scp == cur_console) return 0; } else scp = console[(*data) - 1]; while ((error=tsleep((caddr_t)&scp->smode, PZERO|PCATCH, "waitvt", 0)) == ERESTART) ; return error; case VT_GETACTIVE: *data = get_scr_num()+1; return 0; case KDENABIO: /* allow io operations */ fp = (struct trapframe *)p->p_md.md_regs; fp->tf_eflags |= PSL_IOPL; return 0; case KDDISABIO: /* disallow io operations (default) */ fp = (struct trapframe *)p->p_md.md_regs; fp->tf_eflags &= ~PSL_IOPL; return 0; case KDSETMODE: /* set current mode of this (virtual) console */ switch (*data) { case KD_TEXT: /* switch to TEXT (known) mode */ /* restore fonts & palette ! */ if (crtc_vga) { if (fonts_loaded & FONT_8) copy_font(LOAD, FONT_8, font_8); if (fonts_loaded & FONT_14) copy_font(LOAD, FONT_14, font_14); if (fonts_loaded & FONT_16) copy_font(LOAD, FONT_16, font_16); load_palette(); } /* FALL THROUGH */ case KD_TEXT1: /* switch to TEXT (known) mode */ /* no restore fonts & palette */ scp->status &= ~UNKNOWN_MODE; if (crtc_vga && video_mode_ptr) set_mode(scp); clear_screen(scp); return 0; case KD_GRAPHICS: /* switch to GRAPHICS (unknown) mode */ scp->status |= UNKNOWN_MODE; return 0; default: return EINVAL; } /* NOT REACHED */ case KDGETMODE: /* get current mode of this (virtual) console */ *data = (scp->status & UNKNOWN_MODE) ? KD_GRAPHICS : KD_TEXT; return 0; case KDSBORDER: /* set border color of this (virtual) console */ if (!crtc_vga) return ENXIO; scp->border = *data; if (scp == cur_console) set_border(scp->border); return 0; case KDSKBSTATE: /* set keyboard state (locks) */ if (*data >= 0 && *data <= LOCK_KEY_MASK) { scp->status &= ~LOCK_KEY_MASK; scp->status |= *data; if (scp == cur_console) update_leds(scp->status); return 0; } return EINVAL; case KDGKBSTATE: /* get keyboard state (locks) */ *data = scp->status & LOCK_KEY_MASK; return 0; case KDSETRAD: /* set keyboard repeat & delay rates */ if (*data & 0x80) return EINVAL; i = spltty(); kbd_cmd(KB_SETRAD); kbd_cmd(*data); splx(i); return 0; case KDSKBMODE: /* set keyboard mode */ switch (*data) { case K_RAW: /* switch to RAW scancode mode */ scp->status |= KBD_RAW_MODE; return 0; case K_XLATE: /* switch to XLT ascii mode */ if (scp == cur_console && scp->status == KBD_RAW_MODE) shfts = ctls = alts = agrs = metas = 0; scp->status &= ~KBD_RAW_MODE; return 0; default: return EINVAL; } /* NOT REACHED */ case KDGKBMODE: /* get keyboard mode */ *data = (scp->status & KBD_RAW_MODE) ? K_RAW : K_XLATE; return 0; case KDMKTONE: /* sound the bell */ if (*(int*)data) do_bell(scp, (*(int*)data)&0xffff, (((*(int*)data)>>16)&0xffff)*hz/1000); else do_bell(scp, scp->bell_pitch, scp->bell_duration); return 0; case KIOCSOUND: /* make tone (*data) hz */ if (scp == cur_console) { if (*(int*)data) { int pitch = TIMER_FREQ/(*(int*)data); /* set command for counter 2, 2 byte write */ if (acquire_timer2(TIMER_16BIT|TIMER_SQWAVE)) return EBUSY; /* set pitch */ outb(TIMER_CNTR2, pitch); outb(TIMER_CNTR2, (pitch>>8)); /* enable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) | 3); } else { /* disable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) & 0xFC); release_timer2(); } } return 0; case KDGKBTYPE: /* get keyboard type */ *data = 0; /* type not known (yet) */ return 0; case KDSETLED: /* set keyboard LED status */ if (*data >= 0 && *data <= LED_MASK) { scp->status &= ~LED_MASK; scp->status |= *data; if (scp == cur_console) update_leds(scp->status); return 0; } return EINVAL; case KDGETLED: /* get keyboard LED status */ *data = scp->status & LED_MASK; return 0; case GETFKEY: /* get functionkey string */ if (*(u_short*)data < n_fkey_tab) { fkeyarg_t *ptr = (fkeyarg_t*)data; bcopy(&fkey_tab[ptr->keynum].str, ptr->keydef, fkey_tab[ptr->keynum].len); ptr->flen = fkey_tab[ptr->keynum].len; return 0; } else return EINVAL; case SETFKEY: /* set functionkey string */ if (*(u_short*)data < n_fkey_tab) { fkeyarg_t *ptr = (fkeyarg_t*)data; bcopy(ptr->keydef, &fkey_tab[ptr->keynum].str, min(ptr->flen, MAXFK)); fkey_tab[ptr->keynum].len = min(ptr->flen, MAXFK); return 0; } else return EINVAL; case GIO_SCRNMAP: /* get output translation table */ bcopy(&scr_map, data, sizeof(scr_map)); return 0; case PIO_SCRNMAP: /* set output translation table */ bcopy(data, &scr_map, sizeof(scr_map)); return 0; case GIO_KEYMAP: /* get keyboard translation table */ bcopy(&key_map, data, sizeof(key_map)); return 0; case PIO_KEYMAP: /* set keyboard translation table */ bcopy(data, &key_map, sizeof(key_map)); return 0; case PIO_FONT8x8: /* set 8x8 dot font */ if (!crtc_vga) return ENXIO; bcopy(data, font_8, 8*256); fonts_loaded |= FONT_8; copy_font(LOAD, FONT_8, font_8); return 0; case GIO_FONT8x8: /* get 8x8 dot font */ if (!crtc_vga) return ENXIO; if (fonts_loaded & FONT_8) { bcopy(font_8, data, 8*256); return 0; } else return ENXIO; case PIO_FONT8x14: /* set 8x14 dot font */ if (!crtc_vga) return ENXIO; bcopy(data, font_14, 14*256); fonts_loaded |= FONT_14; copy_font(LOAD, FONT_14, font_14); return 0; case GIO_FONT8x14: /* get 8x14 dot font */ if (!crtc_vga) return ENXIO; if (fonts_loaded & FONT_14) { bcopy(font_14, data, 14*256); return 0; } else return ENXIO; case PIO_FONT8x16: /* set 8x16 dot font */ if (!crtc_vga) return ENXIO; bcopy(data, font_16, 16*256); fonts_loaded |= FONT_16; copy_font(LOAD, FONT_16, font_16); return 0; case GIO_FONT8x16: /* get 8x16 dot font */ if (!crtc_vga) return ENXIO; if (fonts_loaded & FONT_16) { bcopy(font_16, data, 16*256); return 0; } else return ENXIO; default: break; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return(error); error = ttioctl(tp, cmd, data, flag); if (error >= 0) return(error); return(ENOTTY); } void scxint(dev_t dev) { struct tty *tp = scdevtotty(dev); if (!tp) return; tp->t_state &= ~TS_BUSY; if (tp->t_line) (*linesw[tp->t_line].l_start)(tp); else scstart(tp); } void scstart(struct tty *tp) { struct clist *rbp; int i, s, len; u_char buf[PCBURST]; scr_stat *scp = get_scr_stat(tp->t_dev); if (scp->status & SLKED || blink_in_progress) return; s = spltty(); if (!(tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) { tp->t_state |= TS_BUSY; splx(s); rbp = &tp->t_outq; while (rbp->c_cc) { len = q_to_b(rbp, buf, PCBURST); ansi_put(scp, buf, len); } scp->status |= UPDATE_SCREEN; s = spltty(); tp->t_state &= ~TS_BUSY; if (rbp->c_cc <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)rbp); } selwakeup(&tp->t_wsel); } } splx(s); } void pccnprobe(struct consdev *cp) { int maj; /* locate the major number */ for (maj = 0; maj < nchrdev; maj++) if ((void*)cdevsw[maj].d_open == (void*)scopen) break; /* initialize required fields */ cp->cn_dev = makedev(maj, MAXCONS); cp->cn_pri = CN_INTERNAL; } void pccninit(struct consdev *cp) { scinit(); } void pccnputc(dev_t dev, char c) { scr_stat *scp = console[0]; term_stat save = scp->term; scp->term = kernel_console; current_default = &kernel_default; if (scp->scr_buf == Crtat) draw_cursor(scp, FALSE); if (c == '\n') ansi_put(scp, "\r\n", 2); else ansi_put(scp, &c, 1); scp->status |= UPDATE_SCREEN; kernel_console = scp->term; current_default = &user_default; scp->term = save; if (scp == cur_console /* && scrn_timer not running */) { if (scp->scr_buf != Crtat) { bcopyw(scp->scr_buf, Crtat, (scp->xsize*scp->ysize)*sizeof(u_short)); scp->status &= ~CURSOR_SHOWN; } draw_cursor(scp, TRUE); scp->status &= ~UPDATE_SCREEN; } } int pccngetc(dev_t dev) { int s = spltty(); /* block scintr while we poll */ int c = scgetc(0); splx(s); return(c); } int pccncheckc(dev_t dev) { return (scgetc(1) & 0xff); } static void scrn_timer() { static int cursor_blinkrate; scr_stat *scp = cur_console; /* should we just return ? */ if ((scp->status&UNKNOWN_MODE) || blink_in_progress || switch_in_progress) { timeout((timeout_func_t)scrn_timer, 0, hz/10); return; } if (!scrn_blanked) { /* update entire screen image */ if (scp->status & UPDATE_SCREEN) { bcopyw(scp->scr_buf, Crtat, scp->xsize*scp->ysize*sizeof(u_short)); scp->status &= ~CURSOR_SHOWN; } /* update "pseudo" mouse arrow */ if ((scp->status & MOUSE_ENABLED) && ((scp->status & UPDATE_MOUSE) || (scp->status & UPDATE_SCREEN))) draw_mouse_image(scp); /* update cursor image */ if (scp->status & CURSOR_ENABLED) draw_cursor(scp, !(configuration&BLINK_CURSOR) || !(cursor_blinkrate++&0x04)); /* signal update done */ scp->status &= ~UPDATE_SCREEN; } if (scrn_blank_time && (time.tv_sec>scrn_time_stamp+scrn_blank_time)) (*current_saver)(TRUE); timeout((timeout_func_t)scrn_timer, 0, hz/25); } static void clear_screen(scr_stat *scp) { move_crsr(scp, 0, 0); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf, scp->xsize * scp->ysize); } static int switch_scr(scr_stat *scp, u_int next_scr) { if (switch_in_progress && (cur_console->proc != pfind(cur_console->pid))) switch_in_progress = FALSE; if (next_scr >= MAXCONS || switch_in_progress || (cur_console->smode.mode == VT_AUTO && cur_console->status & UNKNOWN_MODE)) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } /* is the wanted virtual console open ? */ if (next_scr) { struct tty *tp = VIRTUAL_TTY(next_scr); if (!(tp->t_state & TS_ISOPEN)) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } } /* delay switch if actively updating screen */ if (write_in_progress || blink_in_progress) { delayed_next_scr = next_scr+1; return 0; } switch_in_progress = TRUE; old_scp = cur_console; new_scp = console[next_scr]; wakeup((caddr_t)&new_scp->smode); if (new_scp == old_scp) { switch_in_progress = FALSE; delayed_next_scr = FALSE; return 0; } /* has controlling process died? */ if (old_scp->proc && (old_scp->proc != pfind(old_scp->pid))) old_scp->smode.mode = VT_AUTO; if (new_scp->proc && (new_scp->proc != pfind(new_scp->pid))) new_scp->smode.mode = VT_AUTO; /* check the modes and switch approbiatly */ if (old_scp->smode.mode == VT_PROCESS) { old_scp->status |= SWITCH_WAIT_REL; psignal(old_scp->proc, old_scp->smode.relsig); } else { exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; } return 0; } static void exchange_scr(void) { move_crsr(old_scp, old_scp->xpos, old_scp->ypos); cur_console = new_scp; if (old_scp->mode != new_scp->mode || (old_scp->status & UNKNOWN_MODE)){ if (crtc_vga && video_mode_ptr) set_mode(new_scp); } move_crsr(new_scp, new_scp->xpos, new_scp->ypos); if ((old_scp->status & UNKNOWN_MODE) && crtc_vga) { if (fonts_loaded & FONT_8) copy_font(LOAD, FONT_8, font_8); if (fonts_loaded & FONT_14) copy_font(LOAD, FONT_14, font_14); if (fonts_loaded & FONT_16) copy_font(LOAD, FONT_16, font_16); load_palette(); } if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE) shfts = ctls = alts = agrs = metas = 0; update_leds(new_scp->status); delayed_next_scr = FALSE; bcopyw(new_scp->scr_buf, Crtat, (new_scp->xsize*new_scp->ysize)*sizeof(u_short)); new_scp->status &= ~CURSOR_SHOWN; } static inline void move_crsr(scr_stat *scp, int x, int y) { if (x < 0 || y < 0 || x >= scp->xsize || y >= scp->ysize) return; scp->xpos = x; scp->ypos = y; scp->cursor_pos = scp->scr_buf + scp->ypos * scp->xsize + scp->xpos; } static void scan_esc(scr_stat *scp, u_char c) { static u_char ansi_col[16] = {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15}; int i, n; u_short *src, *dst, count; if (scp->term.esc == 1) { switch (c) { case '[': /* Start ESC [ sequence */ scp->term.esc = 2; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; case 'M': /* Move cursor up 1 line, scroll if at top */ if (scp->ypos > 0) move_crsr(scp, scp->xpos, scp->ypos - 1); else { bcopyw(scp->scr_buf, scp->scr_buf + scp->xsize, (scp->ysize - 1) * scp->xsize * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf, scp->xsize); } break; #if notyet case 'Q': scp->term.esc = 4; break; #endif case 'c': /* Clear screen & home */ clear_screen(scp); break; } } else if (scp->term.esc == 2) { if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case '=': scp->term.esc = 3; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; case 'A': /* up n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos - n); break; case 'B': /* down n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'C': /* right n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'D': /* left n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos - n, scp->ypos); break; case 'E': /* cursor to start of line n lines down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos + n); break; case 'F': /* cursor to start of line n lines up */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos - n); break; case 'f': /* Cursor move */ case 'H': if (scp->term.num_param == 0) move_crsr(scp, 0, 0); else if (scp->term.num_param == 2) move_crsr(scp, scp->term.param[1] - 1, scp->term.param[0] - 1); break; case 'J': /* Clear all or part of display */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of display */ fillw(scp->term.cur_attr | scr_map[0x20], scp->cursor_pos, scp->scr_buf + scp->xsize * scp->ysize - scp->cursor_pos); break; case 1: /* clear from beginning of display to cursor */ fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf, scp->cursor_pos - scp->scr_buf); break; case 2: /* clear entire display */ clear_screen(scp); break; } break; case 'K': /* Clear all or part of line */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of line */ fillw(scp->term.cur_attr | scr_map[0x20], scp->cursor_pos, scp->xsize - scp->xpos); break; case 1: /* clear from beginning of line to cursor */ fillw(scp->term.cur_attr|scr_map[0x20], scp->cursor_pos - (scp->xsize - scp->xpos), (scp->xsize - scp->xpos) + 1); break; case 2: /* clear entire line */ fillw(scp->term.cur_attr|scr_map[0x20], scp->cursor_pos - (scp->xsize - scp->xpos), scp->xsize); break; } break; case 'L': /* Insert n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; src = scp->scr_buf + scp->ypos * scp->xsize; dst = src + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopyw(src, dst, count * scp->xsize * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], src, n * scp->xsize); break; case 'M': /* Delete n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; dst = scp->scr_buf + scp->ypos * scp->xsize; src = dst + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopyw(src, dst, count * scp->xsize * sizeof(u_short)); src = dst + count * scp->xsize; fillw(scp->term.cur_attr | scr_map[0x20], src, n * scp->xsize); break; case 'P': /* Delete n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; dst = scp->cursor_pos; src = dst + n; count = scp->xsize - (scp->xpos + n); bcopyw(src, dst, count * sizeof(u_short)); src = dst + count; fillw(scp->term.cur_attr | scr_map[0x20], src, n); break; case '@': /* Insert n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; src = scp->cursor_pos; dst = src + n; count = scp->xsize - (scp->xpos + n); bcopyw(src, dst, count * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], src, n); break; case 'S': /* scroll up n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopyw(scp->scr_buf + (scp->xsize * n), scp->scr_buf, scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - n), scp->xsize * n); break; case 'T': /* scroll down n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopyw(scp->scr_buf, scp->scr_buf + (scp->xsize * n), scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf, scp->xsize * n); break; case 'X': /* delete n characters in line */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf + scp->xpos + ((scp->xsize*scp->ypos) * sizeof(u_short)), n); break; case 'Z': /* move n tabs backwards */ n = scp->term.param[0]; if (n < 1) n = 1; if ((i = scp->xpos & 0xf8) == scp->xpos) i -= 8*n; else i -= 8*(n-1); if (i < 0) i = 0; move_crsr(scp, i, scp->ypos); break; case '`': /* move cursor to column n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, n - 1, scp->ypos); break; case 'a': /* move cursor n columns to the right */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'd': /* move cursor to row n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, n - 1); break; case 'e': /* move cursor n rows down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'm': /* change attribute */ if (scp->term.num_param == 0) { scp->term.cur_attr = scp->term.std_attr; break; } for (i = 0; i < scp->term.num_param; i++) { switch (n = scp->term.param[i]) { case 0: /* back to normal */ scp->term.cur_attr = scp->term.std_attr; break; case 1: /* highlight (bold) */ scp->term.cur_attr &= 0xFF00; scp->term.cur_attr |= 0x0800; break; case 4: /* highlight (underline) */ scp->term.cur_attr &= 0xFF00; scp->term.cur_attr |= 0x0800; break; case 5: /* blink */ scp->term.cur_attr &= 0xFF00; scp->term.cur_attr |= 0x8000; break; case 7: /* reverse video */ scp->term.cur_attr = scp->term.rev_attr; break; case 30: case 31: /* set fg color */ case 32: case 33: case 34: case 35: case 36: case 37: scp->term.cur_attr = (scp->term.cur_attr&0xF8FF) | (ansi_col[(n-30)&7]<<8); break; case 40: case 41: /* set bg color */ case 42: case 43: case 44: case 45: case 46: case 47: scp->term.cur_attr = (scp->term.cur_attr&0x8FFF) | (ansi_col[(n-40)&7]<<12); break; } } break; case 'x': if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* reset attributes */ scp->term.cur_attr = scp->term.std_attr = current_default->std_attr; scp->term.rev_attr = current_default->rev_attr; break; case 1: /* set ansi background */ scp->term.cur_attr = scp->term.std_attr = (scp->term.std_attr & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); break; case 2: /* set ansi foreground */ scp->term.cur_attr = scp->term.std_attr = (scp->term.std_attr & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); break; case 3: /* set ansi attribute directly */ scp->term.cur_attr = scp->term.std_attr = (scp->term.param[1]&0xFF)<<8; break; case 5: /* set ansi reverse video background */ scp->term.rev_attr = (scp->term.rev_attr & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); break; case 6: /* set ansi reverse video foreground */ scp->term.rev_attr = (scp->term.rev_attr & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); break; case 7: /* set ansi reverse video directly */ scp->term.rev_attr = (scp->term.param[1]&0xFF)<<8; break; } break; case 'z': /* switch to (virtual) console n */ if (scp->term.num_param == 1) switch_scr(scp, scp->term.param[0]); break; } } else if (scp->term.esc == 3) { if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case 'A': /* set display border color */ if (scp->term.num_param == 1) scp->border=scp->term.param[0] & 0xff; if (scp == cur_console) set_border(scp->border); break; case 'B': /* set bell pitch and duration */ if (scp->term.num_param == 2) { scp->bell_pitch = scp->term.param[0]; scp->bell_duration = scp->term.param[1]*10; } break; case 'C': /* set cursor type & shape */ if (scp->term.num_param == 1) { if (scp->term.param[0] & 0x01) configuration |= BLINK_CURSOR; else configuration &= ~BLINK_CURSOR; if (scp->term.param[0] & 0x02) configuration |= CHAR_CURSOR; else configuration &= ~CHAR_CURSOR; } else if (scp->term.num_param == 2) { scp->cursor_start = scp->term.param[0] & 0x1F; scp->cursor_end = scp->term.param[1] & 0x1F; } break; case 'F': /* set ansi foreground */ if (scp->term.num_param == 1) scp->term.cur_attr = scp->term.std_attr = (scp->term.std_attr & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); break; case 'G': /* set ansi background */ if (scp->term.num_param == 1) scp->term.cur_attr = scp->term.std_attr = (scp->term.std_attr & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); break; case 'H': /* set ansi reverse video foreground */ if (scp->term.num_param == 1) scp->term.rev_attr = (scp->term.rev_attr & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); break; case 'I': /* set ansi reverse video background */ if (scp->term.num_param == 1) scp->term.rev_attr = (scp->term.rev_attr & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); break; } } scp->term.esc = 0; } static inline void draw_cursor(scr_stat *scp, int show) { if (show && !(scp->status & CURSOR_SHOWN)) { u_short cursor_image = *(Crtat + (scp->cursor_pos - scp->scr_buf)); scp->cursor_saveunder = cursor_image; if (configuration & CHAR_CURSOR) cursor_image = (cursor_image & 0xff00) | '_'; else { if ((cursor_image & 0x7000) == 0x7000) { cursor_image &= 0x8fff; if(!(cursor_image & 0x0700)) cursor_image |= 0x0f00; } else { cursor_image |= 0x7000; if ((cursor_image & 0x0f00) == 0x0f00) cursor_image &= 0xf0ff; } } *(Crtat + (scp->cursor_pos - scp->scr_buf)) = cursor_image; scp->status |= CURSOR_SHOWN; } if (!show && (scp->status & CURSOR_SHOWN)) { *(Crtat+(scp->cursor_pos-scp->scr_buf)) = scp->cursor_saveunder; scp->status &= ~CURSOR_SHOWN; } } static void ansi_put(scr_stat *scp, u_char *buf, int len) { u_char *ptr = buf; if (scp->status & UNKNOWN_MODE) return; /* make screensaver happy */ if (scp == cur_console) { scrn_time_stamp = time.tv_sec; if (scrn_blanked) (*current_saver)(FALSE); } write_in_progress++; outloop: if (scp->term.esc) { scan_esc(scp, *ptr++); len--; } else if (PRINTABLE(*ptr)) { /* Print only printables */ do { *scp->cursor_pos++ = (scp->term.cur_attr | scr_map[*ptr++]); scp->xpos++; len--; } while (len && PRINTABLE(*ptr) && (scp->xpos < scp->xsize)); if (scp->xpos >= scp->xsize) { scp->xpos = 0; scp->ypos++; } } else { switch(*ptr) { case 0x07: do_bell(scp, scp->bell_pitch, scp->bell_duration); break; case 0x08: /* non-destructive backspace */ if (scp->cursor_pos > scp->scr_buf) { scp->cursor_pos--; if (scp->xpos > 0) scp->xpos--; else { scp->xpos += scp->xsize - 1; scp->ypos--; } } break; case 0x09: /* non-destructive tab */ { int i = 8 - scp->xpos % 8u; scp->cursor_pos += i; if ((scp->xpos += i) >= scp->xsize) { scp->xpos = 0; scp->ypos++; } } break; case 0x0a: /* newline, same pos */ scp->cursor_pos += scp->xsize; scp->ypos++; break; case 0x0c: /* form feed, clears screen */ clear_screen(scp); break; case 0x0d: /* return, return to pos 0 */ scp->cursor_pos -= scp->xpos; scp->xpos = 0; break; case 0x1b: /* start escape sequence */ scp->term.esc = 1; scp->term.num_param = 0; break; } ptr++; len--; } /* do we have to scroll ?? */ if (scp->cursor_pos >= scp->scr_buf + scp->ysize * scp->xsize) { if (scp->history) { bcopyw(scp->scr_buf, scp->history_head, scp->xsize * sizeof(u_short)); scp->history_head += scp->xsize; if (scp->history_head + scp->xsize > scp->history + scp->history_size) scp->history_head = scp->history; } bcopyw(scp->scr_buf + scp->xsize, scp->scr_buf, scp->xsize * (scp->ysize - 1) * sizeof(u_short)); fillw(scp->term.cur_attr | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - 1), scp->xsize); scp->cursor_pos -= scp->xsize; scp->ypos--; } if (len) goto outloop; write_in_progress--; if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } static void scinit(void) { u_short volatile *cp = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short), was; unsigned hw_cursor; int i; if (init_done) return; init_done = TRUE; /* * Crtat initialized to point to MONO buffer, if not present change * to CGA_BUF offset. ONLY add the difference since locore.s adds * in the remapped offset at the "right" time */ was = *cp; *cp = (u_short) 0xA55A; if (*cp != 0xA55A) crtc_addr = MONO_BASE; else { *cp = was; crtc_addr = COLOR_BASE; Crtat = Crtat + (CGA_BUF-MONO_BUF)/sizeof(u_short); } /* extract cursor location */ outb(crtc_addr,14); hw_cursor = inb(crtc_addr+1)<<8 ; outb(crtc_addr,15); hw_cursor |= inb(crtc_addr+1); /* move hardware cursor out of the way */ outb(crtc_addr,14); outb(crtc_addr+1, 0xff); outb(crtc_addr,15); outb(crtc_addr+1, 0xff); /* is this a VGA or higher ? */ outb(crtc_addr, 7); if (inb(crtc_addr) == 7) { u_long pa; u_long segoff; crtc_vga = TRUE; /* * Get the BIOS video mode pointer. */ segoff = *(u_long *)pa_to_va(0x4a8); pa = (((segoff & 0xffff0000) >> 12) + (segoff & 0xffff)); if (ISMAPPED(pa, sizeof(u_long))) { segoff = *(u_long *)pa_to_va(pa); pa = (((segoff & 0xffff0000) >> 12) + (segoff & 0xffff)); if (ISMAPPED(pa, 64)) video_mode_ptr = (char *)pa_to_va(pa); } } current_default = &user_default; console[0] = &main_console; init_scp(console[0]); console[0]->scr_buf = console[0]->mouse_pos = Crtat; console[0]->cursor_pos = Crtat + hw_cursor; console[0]->xpos = hw_cursor % COL; console[0]->ypos = hw_cursor / COL; cur_console = console[0]; for (i=1; iscr_buf = scp->cursor_pos = scp->scr_buf = scp->mouse_pos = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_NOWAIT); scp->history_head = scp->history_pos = scp->history = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_NOWAIT); bzero(scp->history_head, scp->history_size*sizeof(u_short)); if (crtc_vga && video_mode_ptr) set_mode(scp); clear_screen(scp); return scp; } static void init_scp(scr_stat *scp) { scp->mode = M_VGA_C80x25; scp->font = FONT_16; scp->xsize = COL; scp->ysize = ROW; scp->term.esc = 0; scp->term.std_attr = current_default->std_attr; scp->term.rev_attr = current_default->rev_attr; scp->term.cur_attr = scp->term.std_attr; scp->border = BG_BLACK; scp->cursor_start = -1; scp->cursor_end = -1; scp->mouse_xpos = scp->mouse_ypos = 0; scp->bell_pitch = BELL_PITCH; scp->bell_duration = BELL_DURATION; scp->status = (*(char *)pa_to_va(0x417) & 0x20) ? NLKED : 0; scp->status |= CURSOR_ENABLED; scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; scp->history_head = scp->history_pos = scp->history = NULL; scp->history_size = HISTORY_SIZE; } static u_char *get_fstr(u_int c, u_int *len) { u_int i; if (!(c & FKEY)) return(NULL); i = (c & 0xFF) - F_FN; if (i > n_fkey_tab) return(NULL); *len = fkey_tab[i].len; return(fkey_tab[i].str); } static void update_leds(int which) { int s; static u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* replace CAPS led with ALTGR led for ALTGR keyboards */ if (key_map.n_keys > ALTGR_OFFSET) { if (which & ALKED) which |= CLKED; else which &= ~CLKED; } s = spltty(); kbd_cmd(KB_SETLEDS); kbd_cmd(xlate_leds[which & LED_MASK]); splx(s); } static void history_to_screen(scr_stat *scp) { int i; scp->status &= ~UPDATE_SCREEN; for (i=0; iysize; i++) bcopyw(scp->history + (((scp->history_pos - scp->history) + scp->history_size-((i+1)*scp->xsize))%scp->history_size), scp->scr_buf + (scp->xsize * (scp->ysize-1 - i)), scp->xsize * sizeof(u_short)); scp->status |= UPDATE_SCREEN; } static int history_up_line(scr_stat *scp) { if (WRAPHIST(scp, scp->history_pos, -(scp->xsize*scp->ysize)) != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, -scp->xsize); history_to_screen(scp); return 0; } else return -1; } static int history_down_line(scr_stat *scp) { if (scp->history_pos != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, scp->xsize); history_to_screen(scp); return 0; } else return -1; } /* * scgetc(noblock) - get character from keyboard. * If noblock = 0 wait until a key is pressed. * Else return NOKEY. */ u_int scgetc(int noblock) { u_char scancode, keycode; u_int state, action; struct key_t *key; static u_char esc_flag = 0, compose = 0; static u_int chr = 0; next_code: kbd_wait(); /* First see if there is something in the keyboard port */ if (inb(KB_STAT) & KB_BUF_FULL) scancode = inb(KB_DATA); else if (noblock) return(NOKEY); else goto next_code; if (cur_console->status & KBD_RAW_MODE) return scancode; #if ASYNCH if (scancode == KB_ACK || scancode == KB_RESEND) { kbd_reply = scancode; if (noblock) return(NOKEY); goto next_code; } #endif keycode = scancode & 0x7F; switch (esc_flag) { case 0x00: /* normal scancode */ switch(scancode) { case 0xB8: /* left alt (compose key) */ if (compose) { compose = 0; if (chr > 255) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); chr = 0; } } break; case 0x38: if (!compose) { compose = 1; chr = 0; } break; case 0xE0: case 0xE1: esc_flag = scancode; goto next_code; } break; case 0xE0: /* 0xE0 prefix */ esc_flag = 0; switch (keycode) { case 0x1C: /* right enter key */ keycode = 0x59; break; case 0x1D: /* right ctrl key */ keycode = 0x5A; break; case 0x35: /* keypad divide key */ keycode = 0x5B; break; case 0x37: /* print scrn key */ keycode = 0x5C; break; case 0x38: /* right alt key (alt gr) */ keycode = 0x5D; break; case 0x47: /* grey home key */ keycode = 0x5E; break; case 0x48: /* grey up arrow key */ keycode = 0x5F; break; case 0x49: /* grey page up key */ keycode = 0x60; break; case 0x4B: /* grey left arrow key */ keycode = 0x61; break; case 0x4D: /* grey right arrow key */ keycode = 0x62; break; case 0x4F: /* grey end key */ keycode = 0x63; break; case 0x50: /* grey down arrow key */ keycode = 0x64; break; case 0x51: /* grey page down key */ keycode = 0x65; break; case 0x52: /* grey insert key */ keycode = 0x66; break; case 0x53: /* grey delete key */ keycode = 0x67; break; /* the following 3 are only used on the MS "Natural" keyboard */ case 0x5b: /* left Window key */ keycode = 0x69; break; case 0x5c: /* right Window key */ keycode = 0x6a; break; case 0x5d: /* menu key */ keycode = 0x6b; break; default: /* ignore everything else */ goto next_code; } break; case 0xE1: /* 0xE1 prefix */ esc_flag = 0; if (keycode == 0x1D) esc_flag = 0x1D; goto next_code; /* NOT REACHED */ case 0x1D: /* pause / break */ esc_flag = 0; if (keycode != 0x45) goto next_code; keycode = 0x68; break; } /* if scroll-lock pressed allow history browsing */ if (cur_console->history && cur_console->status & SLKED) { int i; cur_console->status &= ~CURSOR_ENABLED; if (!(cur_console->status & BUFFER_SAVED)) { cur_console->status |= BUFFER_SAVED; cur_console->history_save = cur_console->history_head; /* copy screen into top of history buffer */ for (i=0; iysize; i++) { bcopyw(cur_console->scr_buf + (cur_console->xsize * i), cur_console->history_head, cur_console->xsize * sizeof(u_short)); cur_console->history_head += cur_console->xsize; if (cur_console->history_head + cur_console->xsize > cur_console->history + cur_console->history_size) cur_console->history_head=cur_console->history; } cur_console->history_pos = cur_console->history_head; history_to_screen(cur_console); } switch (scancode) { case 0x47: /* home key */ cur_console->history_pos = cur_console->history_head; history_to_screen(cur_console); goto next_code; case 0x4F: /* end key */ cur_console->history_pos = WRAPHIST(cur_console, cur_console->history_head, cur_console->xsize*cur_console->ysize); history_to_screen(cur_console); goto next_code; case 0x48: /* up arrow key */ if (history_up_line(cur_console)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; case 0x50: /* down arrow key */ if (history_down_line(cur_console)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; case 0x49: /* page up key */ for (i=0; iysize; i++) if (history_up_line(cur_console)) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; case 0x51: /* page down key */ for (i=0; iysize; i++) if (history_down_line(cur_console)) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; } } if (compose) { switch (scancode) { /* key pressed process it */ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ chr = (scancode - 0x40) + chr*10; goto next_code; case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ chr = (scancode - 0x47) + chr*10; goto next_code; case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ chr = (scancode - 0x4E) + chr*10; goto next_code; case 0x52: /* keypad 0 */ chr *= 10; goto next_code; /* key release, no interest here */ case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */ case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */ case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */ case 0xD2: /* keypad 0 */ goto next_code; case 0x38: /* left alt key */ break; default: if (chr) { compose = chr = 0; do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; } break; } } state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0)); if ((!agrs && (cur_console->status & ALKED)) || (agrs && !(cur_console->status & ALKED))) keycode += ALTGR_OFFSET; key = &key_map.key[keycode]; if ( ((key->flgs & FLAG_LOCK_C) && (cur_console->status & CLKED)) || ((key->flgs & FLAG_LOCK_N) && (cur_console->status & NLKED)) ) state ^= 1; /* Check for make/break */ action = key->map[state]; if (scancode & 0x80) { /* key released */ if (key->spcl & 0x80) { switch (action) { case LSH: shfts &= ~1; break; case RSH: shfts &= ~2; break; case LCTR: ctls &= ~1; break; case RCTR: ctls &= ~2; break; case LALT: alts &= ~1; break; case RALT: alts &= ~2; break; case NLK: nlkcnt = 0; break; case CLK: clkcnt = 0; break; case SLK: slkcnt = 0; break; case ASH: agrs = 0; break; case ALK: alkcnt = 0; break; case META: metas = 0; break; } } if (chr && !compose) { action = chr; chr = 0; return(action); } } else { /* key pressed */ if (key->spcl & (0x80>>state)) { switch (action) { /* LOCKING KEYS */ case NLK: if (!nlkcnt) { nlkcnt++; if (cur_console->status & NLKED) cur_console->status &= ~NLKED; else cur_console->status |= NLKED; update_leds(cur_console->status); } break; case CLK: if (!clkcnt) { clkcnt++; if (cur_console->status & CLKED) cur_console->status &= ~CLKED; else cur_console->status |= CLKED; update_leds(cur_console->status); } break; case SLK: if (!slkcnt) { slkcnt++; if (cur_console->status & SLKED) { cur_console->status &= ~SLKED; if (cur_console->status & BUFFER_SAVED){ int i; u_short *ptr = cur_console->history_save; for (i=0; iysize; i++) { bcopyw(ptr, cur_console->scr_buf + (cur_console->xsize*i), cur_console->xsize * sizeof(u_short)); ptr += cur_console->xsize; if (ptr + cur_console->xsize > cur_console->history + cur_console->history_size) ptr = cur_console->history; } cur_console->status&=~BUFFER_SAVED; cur_console->history_head=cur_console->history_save; cur_console->status|=(CURSOR_ENABLED|UPDATE_SCREEN); } scstart(VIRTUAL_TTY(get_scr_num())); } else cur_console->status |= SLKED; update_leds(cur_console->status); } break; case ALK: if (!alkcnt) { alkcnt++; if (cur_console->status & ALKED) cur_console->status &= ~ALKED; else cur_console->status |= ALKED; update_leds(cur_console->status); } break; /* NON-LOCKING KEYS */ case NOP: break; case RBT: shutdown_nice(); break; case SUSP: #if NAPM > 0 apm_suspend(); #endif break; case DBG: #ifdef DDB /* try to switch to console 0 */ if (cur_console->smode.mode == VT_AUTO && console[0]->smode.mode == VT_AUTO) switch_scr(cur_console, 0); Debugger("manual escape to debugger"); return(NOKEY); #else printf("No debugger in kernel\n"); #endif break; case LSH: shfts |= 1; break; case RSH: shfts |= 2; break; case LCTR: ctls |= 1; break; case RCTR: ctls |= 2; break; case LALT: alts |= 1; break; case RALT: alts |= 2; break; case ASH: agrs = 1; break; case META: metas = 1; break; case NEXT: switch_scr(cur_console, (get_scr_num() + 1) % MAXCONS); break; case BTAB: return(BKEY); default: if (action >= F_SCR && action <= L_SCR) { switch_scr(cur_console, action - F_SCR); break; } if (action >= F_FN && action <= L_FN) action |= FKEY; return(action); } } else { if (metas) action |= MKEY; return(action); } } goto next_code; } int scmmap(dev_t dev, int offset, int nprot) { if (offset > 0x20000 - PAGE_SIZE) return -1; return i386_btop((VIDEOMEM + offset)); } static void kbd_wait(void) { int i = 1000; while (i--) { if ((inb(KB_STAT) & KB_READY) == 0) break; DELAY (10); } } static void kbd_cmd(u_char command) { int retry = 5; do { int i = 100000; kbd_wait(); #if ASYNCH kbd_reply = 0; outb(KB_DATA, command); while (i--) { if (kbd_reply == KB_ACK) return; if (kbd_reply == KB_RESEND) break; } #else outb(KB_DATA, command); while (i--) { if (inb(KB_STAT) & KB_BUF_FULL) { int val; DELAY(10); val = inb(KB_DATA); if (val == KB_ACK) return; if (val == KB_RESEND) break; } } #endif } while (retry--); } static void set_mode(scr_stat *scp) { char *modetable; char special_modetable[64]; int mode, font_size; if (scp != cur_console) return; /* setup video hardware for the given mode */ switch (scp->mode) { case M_VGA_M80x60: bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64); goto special_80x60; case M_VGA_C80x60: bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64); special_80x60: special_modetable[2] = 0x08; special_modetable[19] = 0x47; goto special_480l; case M_VGA_M80x30: bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64); goto special_80x30; case M_VGA_C80x30: bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64); special_80x30: special_modetable[19] = 0x4f; special_480l: special_modetable[9] |= 0xc0; special_modetable[16] = 0x08; special_modetable[17] = 0x3e; special_modetable[26] = 0xea; special_modetable[28] = 0xdf; special_modetable[31] = 0xe7; special_modetable[32] = 0x04; modetable = special_modetable; goto setup_mode; case M_ENH_B80x43: bcopyw(video_mode_ptr+(64*M_ENH_B80x25),&special_modetable, 64); goto special_80x43; case M_ENH_C80x43: bcopyw(video_mode_ptr+(64*M_ENH_C80x25),&special_modetable, 64); special_80x43: special_modetable[28] = 87; goto special_80x50; case M_VGA_M80x50: bcopyw(video_mode_ptr+(64*M_VGA_M80x25),&special_modetable, 64); goto special_80x50; case M_VGA_C80x50: bcopyw(video_mode_ptr+(64*M_VGA_C80x25),&special_modetable, 64); special_80x50: special_modetable[2] = 8; special_modetable[19] = 7; modetable = special_modetable; goto setup_mode; case M_VGA_C40x25: case M_VGA_C80x25: case M_VGA_M80x25: case M_B40x25: case M_C40x25: case M_B80x25: case M_C80x25: case M_ENH_B40x25: case M_ENH_C40x25: case M_ENH_B80x25: case M_ENH_C80x25: modetable = video_mode_ptr + (scp->mode * 64); setup_mode: set_vgaregs(modetable); font_size = *(modetable + 2); /* set font type (size) */ switch (font_size) { case 0x10: outb(TSIDX, 0x03); outb(TSREG, 0x00); /* font 0 */ scp->font = FONT_16; break; case 0x0E: outb(TSIDX, 0x03); outb(TSREG, 0x05); /* font 1 */ scp->font = FONT_14; break; default: case 0x08: outb(TSIDX, 0x03); outb(TSREG, 0x0A); /* font 2 */ scp->font = FONT_8; break; } break; case M_BG320: case M_CG320: case M_BG640: case M_CG320_D: case M_CG640_E: case M_CG640x350: case M_ENH_CG640: case M_BG640x480: case M_CG640x480: case M_VGA_CG320: set_vgaregs(video_mode_ptr + (scp->mode * 64)); break; default: /* call user defined function XXX */ break; } /* set border color for this (virtual) console */ set_border(scp->border); return; } void set_border(int color) { inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x11); outb(ATC, color); inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x20); /* enable Palette */ } static void set_vgaregs(char *modetable) { int i, s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */ outb(TSIDX, 0x07); outb(TSREG, 0x00); /* unlock registers */ for (i=0; i<4; i++) { /* program sequencer */ outb(TSIDX, i+1); outb(TSREG, modetable[i+5]); } outb(MISC, modetable[9]); /* set dot-clock */ outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */ outb(crtc_addr, 0x11); outb(crtc_addr+1, inb(crtc_addr+1) & 0x7F); for (i=0; i<25; i++) { /* program crtc */ outb(crtc_addr, i); if (i == 14 || i == 15) /* no hardware cursor */ outb(crtc_addr+1, 0xff); else outb(crtc_addr+1, modetable[i+10]); } inb(crtc_addr+6); /* reset flip-flop */ for (i=0; i<20; i++) { /* program attribute ctrl */ outb(ATC, i); outb(ATC, modetable[i+35]); } for (i=0; i<9; i++) { /* program graph data ctrl */ outb(GDCIDX, i); outb(GDCREG, modetable[i+55]); } inb(crtc_addr+6); /* reset flip-flop */ outb(ATC ,0x20); /* enable palette */ splx(s); } static void set_font_mode() { /* setup vga for loading fonts (graphics plane mode) */ inb(crtc_addr+6); outb(ATC, 0x30); outb(ATC, 0x01); #if SLOW_VGA outb(TSIDX, 0x02); outb(TSREG, 0x04); outb(TSIDX, 0x04); outb(TSREG, 0x06); outb(GDCIDX, 0x04); outb(GDCREG, 0x02); outb(GDCIDX, 0x05); outb(GDCREG, 0x00); outb(GDCIDX, 0x06); outb(GDCREG, 0x05); #else outw(TSIDX, 0x0402); outw(TSIDX, 0x0604); outw(GDCIDX, 0x0204); outw(GDCIDX, 0x0005); outw(GDCIDX, 0x0506); /* addr = a0000, 64kb */ #endif } static void set_normal_mode() { int s = splhigh(); /* setup vga for normal operation mode again */ inb(crtc_addr+6); outb(ATC, 0x30); outb(ATC, 0x0C); #if SLOW_VGA outb(TSIDX, 0x02); outb(TSREG, 0x03); outb(TSIDX, 0x04); outb(TSREG, 0x02); outb(GDCIDX, 0x04); outb(GDCREG, 0x00); outb(GDCIDX, 0x05); outb(GDCREG, 0x10); if (crtc_addr == MONO_BASE) { outb(GDCIDX, 0x06); outb(GDCREG, 0x0A); /* addr = b0000, 32kb */ } else { outb(GDCIDX, 0x06); outb(GDCREG, 0x0E); /* addr = b8000, 32kb */ } #else outw(TSIDX, 0x0302); outw(TSIDX, 0x0204); outw(GDCIDX, 0x0004); outw(GDCIDX, 0x1005); if (crtc_addr == MONO_BASE) outw(GDCIDX, 0x0A06); /* addr = b0000, 32kb */ else outw(GDCIDX, 0x0E06); /* addr = b8000, 32kb */ #endif splx(s); } static void copy_font(int operation, int font_type, char* font_image) { int ch, line, segment, fontsize; u_char val; switch (font_type) { default: case FONT_8: segment = 0x8000; fontsize = 8; break; case FONT_14: segment = 0x4000; fontsize = 14; break; case FONT_16: segment = 0x0000; fontsize = 16; break; } outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */ outb(TSIDX, 0x01); outb(TSREG, val | 0x20); set_font_mode(); for (ch=0; ch < 256; ch++) for (line=0; line < fontsize; line++) if (operation) *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line) = font_image[(ch*fontsize)+line]; else font_image[(ch*fontsize)+line] = *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line); set_normal_mode(); outb(TSIDX, 0x01); outb(TSREG, val & 0xDF); /* enable screen */ } static void draw_mouse_image(scr_stat *scp) { caddr_t address; int i, font_size; char *font_buffer; u_short buffer[32]; u_short xoffset, yoffset; u_short *crt_pos = Crtat + (scp->mouse_pos - scp->scr_buf); xoffset = scp->mouse_xpos % 8; switch (scp->font) { default: case FONT_8: font_size = 8; font_buffer = font_8; yoffset = scp->mouse_ypos % 8; address = (caddr_t)VIDEOMEM+0x8000; break; case FONT_14: font_size = 14; font_buffer = font_14; yoffset = scp->mouse_ypos % 14; address = (caddr_t)VIDEOMEM+0x4000; break; case FONT_16: font_size = 16; font_buffer = font_16; yoffset = scp->mouse_ypos % 16; address = (caddr_t)VIDEOMEM; break; } bcopyw(font_buffer+((*(scp->mouse_pos) & 0xff)*font_size), &scp->mouse_cursor[0], font_size); bcopyw(font_buffer+((*(scp->mouse_pos+1) & 0xff)*font_size), &scp->mouse_cursor[32], font_size); bcopyw(font_buffer+((*(scp->mouse_pos+scp->xsize) & 0xff)*font_size), &scp->mouse_cursor[64], font_size); bcopyw(font_buffer+((*(scp->mouse_pos+scp->xsize+1) & 0xff)*font_size), &scp->mouse_cursor[96], font_size); for (i=0; imouse_cursor[i]<<8 | scp->mouse_cursor[i+32]; buffer[i+font_size]=scp->mouse_cursor[i+64]<<8|scp->mouse_cursor[i+96]; } for (i=0; i<16; i++) { buffer[i+yoffset] = ( buffer[i+yoffset] & ~(mouse_and_mask[i] >> xoffset)) | (mouse_or_mask[i] >> xoffset); } for (i=0; imouse_cursor[i] = (buffer[i] & 0xff00) >> 8; scp->mouse_cursor[i+32] = buffer[i] & 0xff; scp->mouse_cursor[i+64] = (buffer[i+font_size] & 0xff00) >> 8; scp->mouse_cursor[i+96] = buffer[i+font_size] & 0xff; } /* * if we didn't update entire screen, restore old mouse position * and check if we overwrote the cursor location.. */ if ((scp->status & UPDATE_MOUSE) && !(scp->status & UPDATE_SCREEN)) { u_short *ptr = scp->scr_buf + (scp->mouse_oldpos - Crtat); if (crt_pos != scp->mouse_oldpos) { *(scp->mouse_oldpos) = scp->mouse_saveunder[0]; *(scp->mouse_oldpos+1) = scp->mouse_saveunder[1]; *(scp->mouse_oldpos+scp->xsize) = scp->mouse_saveunder[2]; *(scp->mouse_oldpos+scp->xsize+1) = scp->mouse_saveunder[3]; } scp->mouse_saveunder[0] = *(scp->mouse_pos); scp->mouse_saveunder[1] = *(scp->mouse_pos+1); scp->mouse_saveunder[2] = *(scp->mouse_pos+scp->xsize); scp->mouse_saveunder[3] = *(scp->mouse_pos+scp->xsize+1); if ((scp->cursor_pos == (ptr)) || (scp->cursor_pos == (ptr+1)) || (scp->cursor_pos == (ptr+scp->xsize)) || (scp->cursor_pos == (ptr+scp->xsize+1)) || (scp->cursor_pos == (scp->mouse_pos)) || (scp->cursor_pos == (scp->mouse_pos+1)) || (scp->cursor_pos == (scp->mouse_pos+scp->xsize)) || (scp->cursor_pos == (scp->mouse_pos+scp->xsize+1))) scp->status &= ~CURSOR_SHOWN; } scp->mouse_oldpos = crt_pos; while (!(inb(crtc_addr+6) & 0x08)) /* wait for vertical retrace */ ; *(crt_pos) = *(scp->mouse_pos)&0xff00|0xd0; *(crt_pos+1) = *(scp->mouse_pos+1)&0xff00|0xd1; *(crt_pos+scp->xsize) = *(scp->mouse_pos+scp->xsize)&0xff00|0xd2; *(crt_pos+scp->xsize+1) = *(scp->mouse_pos+scp->xsize+1)&0xff00|0xd3; set_font_mode(); bcopy(scp->mouse_cursor, (char *)pa_to_va(address) + 0xd0 * 32, 128); set_normal_mode(); } static void save_palette(void) { int i; outb(PALRADR, 0x00); for (i=0x00; i<0x300; i++) palette[i] = inb(PALDATA); inb(crtc_addr+6); /* reset flip/flop */ } void load_palette(void) { int i; outb(PIXMASK, 0xFF); /* no pixelmask */ outb(PALWADR, 0x00); for (i=0x00; i<0x300; i++) outb(PALDATA, palette[i]); inb(crtc_addr+6); /* reset flip/flop */ outb(ATC, 0x20); /* enable palette */ } static void do_bell(scr_stat *scp, int pitch, int duration) { if (scp == cur_console) { if (configuration & VISUAL_BELL) { if (blink_in_progress) return; blink_in_progress = 4; blink_screen(scp); timeout((timeout_func_t)blink_screen, scp, hz/10); } else sysbeep(pitch, duration); } } static void blink_screen(scr_stat *scp) { if (blink_in_progress > 1) { if (blink_in_progress & 1) fillw(kernel_default.std_attr | scr_map[0x20], Crtat, scp->xsize * scp->ysize); else fillw(kernel_default.rev_attr | scr_map[0x20], Crtat, scp->xsize * scp->ysize); blink_in_progress--; timeout((timeout_func_t)blink_screen, scp, hz/10); } else { scp->status |= UPDATE_SCREEN; blink_in_progress = FALSE; if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } } #endif /* NSC */ Index: head/sys/isa/syscons.h =================================================================== --- head/sys/isa/syscons.h (revision 6781) +++ head/sys/isa/syscons.h (revision 6782) @@ -1,208 +1,208 @@ /*- * Copyright (c) 1995 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software withough specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: syscons.h,v 1.1 1995/02/22 13:40:21 sos Exp $ + * $Id: syscons.h,v 1.2 1995/02/25 20:09:21 pst Exp $ */ /* * The APM stuff is -not- under conditional compilation because we don't want * the size of the scr_stat structure to vary depending upon if APM has been * compiled in or not, that can cause utilities and lkms to crash! */ #include /* vm things */ #define ISMAPPED(pa, width) \ (((pa) <= (u_long)0x1000 - (width)) \ || ((pa) >= 0xa0000 && (pa) <= 0x100000 - (width))) #define pa_to_va(pa) (KERNBASE + (pa)) /* works if ISMAPPED(pa...) */ /* printable chars */ #define PRINTABLE(ch) (ch>0x1B || (ch>0x0d && ch<0x1b) || ch<0x07) /* status flags */ #define LOCK_KEY_MASK 0x0000F #define LED_MASK 0x00007 #define UNKNOWN_MODE 0x00010 #define KBD_RAW_MODE 0x00020 #define SWITCH_WAIT_REL 0x00040 #define SWITCH_WAIT_ACQ 0x00080 #define BUFFER_SAVED 0x00100 #define CURSOR_ENABLED 0x00200 #define CURSOR_SHOWN 0x00400 #define MOUSE_ENABLED 0x00800 #define UPDATE_MOUSE 0x01000 #define UPDATE_SCREEN 0x02000 /* configuration flags */ #define VISUAL_BELL 0x00001 #define BLINK_CURSOR 0x00002 #define CHAR_CURSOR 0x00004 /* video hardware memory addresses */ #define VIDEOMEM 0x000A0000 /* misc defines */ #define FALSE 0 #define TRUE 1 #define MAX_ESC_PAR 5 #define LOAD 1 #define SAVE 0 #define COL 80 #define ROW 25 #define BELL_DURATION 5 #define BELL_PITCH 800 #define TIMER_FREQ 1193182 /* should be in isa.h */ #define CONSOLE_BUFSIZE 1024 #define PCBURST 128 #define FONT_8 0x001 #define FONT_14 0x002 #define FONT_16 0x004 #define HISTORY_SIZE 100*80 /* defines related to hardware addresses */ #define MONO_BASE 0x3B4 /* crt controller base mono */ #define COLOR_BASE 0x3D4 /* crt controller base color */ #define MISC 0x3C2 /* misc output register */ #define ATC IO_VGA+0x00 /* attribute controller */ #define TSIDX IO_VGA+0x04 /* timing sequencer idx */ #define TSREG IO_VGA+0x05 /* timing sequencer data */ #define PIXMASK IO_VGA+0x06 /* pixel write mask */ #define PALRADR IO_VGA+0x07 /* palette read address */ #define PALWADR IO_VGA+0x08 /* palette write address */ #define PALDATA IO_VGA+0x09 /* palette data register */ #define GDCIDX IO_VGA+0x0E /* graph data controller idx */ #define GDCREG IO_VGA+0x0F /* graph data controller data */ /* special characters */ #define cntlc 0x03 #define cntld 0x04 #define bs 0x08 #define lf 0x0a #define cr 0x0d #define del 0x7f typedef struct term_stat { int esc; /* processing escape sequence */ int num_param; /* # of parameters to ESC */ int last_param; /* last parameter # */ int param[MAX_ESC_PAR]; /* contains ESC parameters */ int cur_attr; /* current attributes */ int std_attr; /* normal attributes */ int rev_attr; /* reverse attributes */ } term_stat; typedef struct scr_stat { u_short *scr_buf; /* buffer when off screen */ int xpos; /* current X position */ int ypos; /* current Y position */ int xsize; /* X size */ int ysize; /* Y size */ term_stat term; /* terminal emulation stuff */ int status; /* status (bitfield) */ u_short *cursor_pos; /* cursor buffer position */ u_short cursor_saveunder; /* saved chars under cursor */ char cursor_start; /* cursor start line # */ char cursor_end; /* cursor end line # */ u_short *mouse_pos; /* mouse buffer position */ u_short *mouse_oldpos; /* mouse old buffer position */ u_short mouse_saveunder[4]; /* saved chars under mouse */ short mouse_xpos; /* mouse x coordinate */ short mouse_ypos; /* mouse y coordinate */ u_char mouse_cursor[128]; /* mouse cursor bitmap store */ u_short bell_duration; u_short bell_pitch; u_char border; /* border color */ u_char mode; /* mode */ u_char font; /* font on this screen */ pid_t pid; /* pid of controlling proc */ struct proc *proc; /* proc* of controlling proc */ struct vt_mode smode; /* switch mode */ u_short *history; /* circular history buffer */ u_short *history_head; /* current head position */ u_short *history_pos; /* position shown on screen */ u_short *history_save; /* save area index */ int history_size; /* size of history buffer */ struct apmhook r_hook; /* reconfiguration support */ } scr_stat; typedef struct default_attr { int std_attr; /* normal attributes */ int rev_attr; /* reverse attributes */ } default_attr; /* function prototypes */ int scprobe(struct isa_device *dev); int scattach(struct isa_device *dev); int scopen(dev_t dev, int flag, int mode, struct proc *p); int scclose(dev_t dev, int flag, int mode, struct proc *p); int scread(dev_t dev, struct uio *uio, int flag); int scwrite(dev_t dev, struct uio *uio, int flag); int scparam(struct tty *tp, struct termios *t); int scioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p); void scxint(dev_t dev); void scstart(struct tty *tp); void pccnprobe(struct consdev *cp); void pccninit(struct consdev *cp); void pccnputc(dev_t dev, char c); int pccngetc(dev_t dev); int pccncheckc(dev_t dev); void scintr(int unit); int pcmmap(dev_t dev, int offset, int nprot); static void scinit(void); static u_int scgetc(int noblock); -static struct tty *get_tty_ptr(dev_t dev); +struct tty *scdevtotty(dev_t dev); static scr_stat *get_scr_stat(dev_t dev); static scr_stat *alloc_scp(); static void init_scp(scr_stat *scp); static int get_scr_num(); static void scrn_timer(); static void clear_screen(scr_stat *scp); static int switch_scr(scr_stat *scp, u_int next_scr); static void exchange_scr(void); static inline void move_crsr(scr_stat *scp, int x, int y); static void scan_esc(scr_stat *scp, u_char c); static inline void draw_cursor(scr_stat *scp, int show); static void ansi_put(scr_stat *scp, u_char *buf, int len); static u_char *get_fstr(u_int c, u_int *len); static void update_leds(int which); static void history_to_screen(scr_stat *scp); static int history_up_line(scr_stat *scp); static int history_down_line(scr_stat *scp); static void kbd_wait(void); static void kbd_cmd(u_char command); static void set_mode(scr_stat *scp); void set_border(int color); static void set_vgaregs(char *modetable); static void set_font_mode(); static void set_normal_mode(); static void copy_font(int operation, int font_type, char* font_image); static void draw_mouse_image(scr_stat *scp); static void save_palette(void); void load_palette(void); static void do_bell(scr_stat *scp, int pitch, int duration); static void blink_screen(scr_stat *scp); Index: head/sys/kern/tty.c =================================================================== --- head/sys/kern/tty.c (revision 6781) +++ head/sys/kern/tty.c (revision 6782) @@ -1,2187 +1,2200 @@ /*- * Copyright (c) 1982, 1986, 1990, 1991, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tty.c 8.8 (Berkeley) 1/21/94 - * $Id: tty.c,v 1.32 1995/02/25 20:09:29 pst Exp $ + * $Id: tty.c,v 1.33 1995/02/27 19:47:31 ugen Exp $ */ #include "snp.h" #include #include #include #include #define TTYDEFCHARS #include #undef TTYDEFCHARS #include #include #include #include #include #include #include #include #include #include #if NSNP > 0 #include #endif #include static int proc_compare __P((struct proc *p1, struct proc *p2)); static void ttyblock __P((struct tty *tp)); static void ttyecho __P((int, struct tty *tp)); static void ttyrubo __P((struct tty *, int)); /* Symbolic sleep message strings. */ char ttclos[] = "ttycls"; char ttopen[] = "ttyopn"; char ttybg[] = "ttybg"; char ttybuf[] = "ttybuf"; char ttyin[] = "ttyin"; char ttyout[] = "ttyout"; /* * Table with character classes and parity. The 8th bit indicates parity, * the 7th bit indicates the character is an alphameric or underscore (for * ALTWERASE), and the low 6 bits indicate delay type. If the low 6 bits * are 0 then the character needs no special processing on output; classes * other than 0 might be translated or (not currently) require delays. */ #define E 0x00 /* Even parity. */ #define O 0x80 /* Odd parity. */ #define PARITY(c) (char_type[c] & O) #define ALPHA 0x40 /* Alpha or underscore. */ #define ISALPHA(c) (char_type[(c) & TTY_CHARMASK] & ALPHA) #define CCLASSMASK 0x3f #define CCLASS(c) (char_type[c] & CCLASSMASK) #define BS BACKSPACE #define CC CONTROL #define CR RETURN #define NA ORDINARY | ALPHA #define NL NEWLINE #define NO ORDINARY #define TB TAB #define VT VTAB char const char_type[] = { E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* nul - bel */ O|BS, E|TB, E|NL, O|CC, E|VT, O|CR, O|CC, E|CC, /* bs - si */ O|CC, E|CC, E|CC, O|CC, E|CC, O|CC, O|CC, E|CC, /* dle - etb */ E|CC, O|CC, O|CC, E|CC, O|CC, E|CC, E|CC, O|CC, /* can - us */ O|NO, E|NO, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* sp - ' */ E|NO, O|NO, O|NO, E|NO, O|NO, E|NO, E|NO, O|NO, /* ( - / */ E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* 0 - 7 */ O|NA, E|NA, E|NO, O|NO, E|NO, O|NO, O|NO, E|NO, /* 8 - ? */ O|NO, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* @ - G */ E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* H - O */ E|NA, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* P - W */ O|NA, E|NA, E|NA, O|NO, E|NO, O|NO, O|NO, O|NA, /* X - _ */ E|NO, O|NA, O|NA, E|NA, O|NA, E|NA, E|NA, O|NA, /* ` - g */ O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* h - o */ O|NA, E|NA, E|NA, O|NA, E|NA, O|NA, O|NA, E|NA, /* p - w */ E|NA, O|NA, O|NA, E|NO, O|NO, E|NO, E|NO, O|CC, /* x - del */ /* * Meta chars; should be settable per character set; * for now, treat them all as normal characters. */ NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, }; #undef BS #undef CC #undef CR #undef NA #undef NL #undef NO #undef TB #undef VT /* Macros to clear/set/test flags. */ #define SET(t, f) (t) |= (f) #define CLR(t, f) (t) &= ~(f) #define ISSET(t, f) ((t) & (f)) /* * Initial open of tty, or (re)entry to standard tty line discipline. */ int ttyopen(device, tp) dev_t device; register struct tty *tp; { int s; s = spltty(); tp->t_dev = device; if (!ISSET(tp->t_state, TS_ISOPEN)) { SET(tp->t_state, TS_ISOPEN); bzero(&tp->t_winsize, sizeof(tp->t_winsize)); } CLR(tp->t_state, TS_WOPEN); /* * Initialize or restore a cblock allocation policy suitable for * the standard line discipline. */ clist_alloc_cblocks(&tp->t_canq, TTYHOG, 512); clist_alloc_cblocks(&tp->t_outq, TTMAXHIWAT + 200, 512); clist_alloc_cblocks(&tp->t_rawq, TTYHOG, TTYHOG); splx(s); return (0); } /* * Handle close() on a tty line: flush and set to initial state, * bumping generation number so that pending read/write calls * can detect recycling of the tty. */ int ttyclose(tp) register struct tty *tp; { extern struct tty *constty; /* Temporary virtual console. */ int s; s = spltty(); if (constty == tp) constty = NULL; ttyflush(tp, FREAD | FWRITE); clist_free_cblocks(&tp->t_canq); clist_free_cblocks(&tp->t_outq); clist_free_cblocks(&tp->t_rawq); #if NSNP > 0 if (ISSET(tp->t_state, TS_SNOOP) && tp->t_sc != NULL) snpdown((struct snoop *)tp->t_sc); #endif tp->t_gen++; tp->t_pgrp = NULL; tp->t_session = NULL; tp->t_state = 0; splx(s); return (0); } #define FLUSHQ(q) { \ if ((q)->c_cc) \ ndflush(q, (q)->c_cc); \ } /* Is 'c' a line delimiter ("break" character)? */ #define TTBREAKC(c) \ ((c) == '\n' || (((c) == cc[VEOF] || \ (c) == cc[VEOL] || (c) == cc[VEOL2]) && (c) != _POSIX_VDISABLE)) /*- * TODO: * o Fix races for sending the start char in ttyflush(). * o Handle inter-byte timeout for "MIN > 0, TIME > 0" in ttyselect(). * With luck, there will be MIN chars before select() returns(). * o Handle CLOCAL consistently for ptys. Perhaps disallow setting it. * o Don't allow input in TS_ZOMBIE case. It would be visible through * FIONREAD. * o Do the new sio locking stuff here and use it to avoid special * case for EXTPROC? * o Lock PENDIN too? * o Move EXTPROC and/or PENDIN to t_state? * o Wrap most of ttioctl in spltty/splx. * o Implement TIOCNOTTY or remove it from . */ /* * Process input of a single character received on a tty. */ int ttyinput(c, tp) register int c; register struct tty *tp; { register int iflag, lflag; register u_char *cc; int i, err; /* * If input is pending take it first. */ lflag = tp->t_lflag; if (ISSET(lflag, PENDIN)) ttypend(tp); /* * Gather stats. */ if (ISSET(lflag, ICANON)) { ++tk_cancc; ++tp->t_cancc; } else { ++tk_rawcc; ++tp->t_rawcc; } ++tk_nin; /* Handle exceptional conditions (break, parity, framing). */ cc = tp->t_cc; iflag = tp->t_iflag; err = (ISSET(c, TTY_ERRORMASK)); if (err) { CLR(c, TTY_ERRORMASK); if (ISSET(err, TTY_FE) && !c) { /* Break. */ if (ISSET(iflag, IGNBRK)) goto endcase; else if (ISSET(iflag, BRKINT) && ISSET(lflag, ISIG) && (cc[VINTR] != _POSIX_VDISABLE)) c = cc[VINTR]; else if (ISSET(iflag, PARMRK)) goto parmrk; } else if ((ISSET(err, TTY_PE) && ISSET(iflag, INPCK)) || ISSET(err, TTY_FE)) { if (ISSET(iflag, IGNPAR)) goto endcase; else if (ISSET(iflag, PARMRK)) { parmrk: (void)putc(0377 | TTY_QUOTE, &tp->t_rawq); (void)putc(0 | TTY_QUOTE, &tp->t_rawq); (void)putc(c | TTY_QUOTE, &tp->t_rawq); goto endcase; } else c = 0; } } /* * In tandem mode, check high water mark. */ if (ISSET(iflag, IXOFF) || ISSET(tp->t_cflag, CRTS_IFLOW)) ttyblock(tp); if (!ISSET(tp->t_state, TS_TYPEN) && ISSET(iflag, ISTRIP)) CLR(c, 0x80); if (!ISSET(lflag, EXTPROC)) { /* * Check for literal nexting very first */ if (ISSET(tp->t_state, TS_LNCH)) { SET(c, TTY_QUOTE); CLR(tp->t_state, TS_LNCH); } /* * Scan for special characters. This code * is really just a big case statement with * non-constant cases. The bottom of the * case statement is labeled ``endcase'', so goto * it after a case match, or similar. */ /* * Control chars which aren't controlled * by ICANON, ISIG, or IXON. */ if (ISSET(lflag, IEXTEN)) { if (CCEQ(cc[VLNEXT], c)) { if (ISSET(lflag, ECHO)) { if (ISSET(lflag, ECHOE)) { (void)ttyoutput('^', tp); (void)ttyoutput('\b', tp); } else ttyecho(c, tp); } SET(tp->t_state, TS_LNCH); goto endcase; } if (CCEQ(cc[VDISCARD], c)) { if (ISSET(lflag, FLUSHO)) CLR(tp->t_lflag, FLUSHO); else { ttyflush(tp, FWRITE); ttyecho(c, tp); if (tp->t_rawq.c_cc + tp->t_canq.c_cc) ttyretype(tp); SET(tp->t_lflag, FLUSHO); } goto startoutput; } } /* * Signals. */ if (ISSET(lflag, ISIG)) { if (CCEQ(cc[VINTR], c) || CCEQ(cc[VQUIT], c)) { if (!ISSET(lflag, NOFLSH)) ttyflush(tp, FREAD | FWRITE); ttyecho(c, tp); pgsignal(tp->t_pgrp, CCEQ(cc[VINTR], c) ? SIGINT : SIGQUIT, 1); goto endcase; } if (CCEQ(cc[VSUSP], c)) { if (!ISSET(lflag, NOFLSH)) ttyflush(tp, FREAD); ttyecho(c, tp); pgsignal(tp->t_pgrp, SIGTSTP, 1); goto endcase; } } /* * Handle start/stop characters. */ if (ISSET(iflag, IXON)) { if (CCEQ(cc[VSTOP], c)) { if (!ISSET(tp->t_state, TS_TTSTOP)) { SET(tp->t_state, TS_TTSTOP); #ifdef sun4c /* XXX */ (*tp->t_stop)(tp, 0); #else (*cdevsw[major(tp->t_dev)].d_stop)(tp, 0); #endif return (0); } if (!CCEQ(cc[VSTART], c)) return (0); /* * if VSTART == VSTOP then toggle */ goto endcase; } if (CCEQ(cc[VSTART], c)) goto restartoutput; } /* * IGNCR, ICRNL, & INLCR */ if (c == '\r') { if (ISSET(iflag, IGNCR)) goto endcase; else if (ISSET(iflag, ICRNL)) c = '\n'; } else if (c == '\n' && ISSET(iflag, INLCR)) c = '\r'; } if (!ISSET(tp->t_lflag, EXTPROC) && ISSET(lflag, ICANON)) { /* * From here on down canonical mode character * processing takes place. */ /* * erase (^H / ^?) */ if (CCEQ(cc[VERASE], c)) { if (tp->t_rawq.c_cc) ttyrub(unputc(&tp->t_rawq), tp); goto endcase; } /* * kill (^U) */ if (CCEQ(cc[VKILL], c)) { if (ISSET(lflag, ECHOKE) && tp->t_rawq.c_cc == tp->t_rocount && !ISSET(lflag, ECHOPRT)) while (tp->t_rawq.c_cc) ttyrub(unputc(&tp->t_rawq), tp); else { ttyecho(c, tp); if (ISSET(lflag, ECHOK) || ISSET(lflag, ECHOKE)) ttyecho('\n', tp); FLUSHQ(&tp->t_rawq); tp->t_rocount = 0; } CLR(tp->t_state, TS_LOCAL); goto endcase; } /* * word erase (^W) */ if (CCEQ(cc[VWERASE], c)) { int alt = ISSET(lflag, ALTWERASE); int ctype; /* * erase whitespace */ while ((c = unputc(&tp->t_rawq)) == ' ' || c == '\t') ttyrub(c, tp); if (c == -1) goto endcase; /* * erase last char of word and remember the * next chars type (for ALTWERASE) */ ttyrub(c, tp); c = unputc(&tp->t_rawq); if (c == -1) goto endcase; if (c == ' ' || c == '\t') { (void)putc(c, &tp->t_rawq); goto endcase; } ctype = ISALPHA(c); /* * erase rest of word */ do { ttyrub(c, tp); c = unputc(&tp->t_rawq); if (c == -1) goto endcase; } while (c != ' ' && c != '\t' && (alt == 0 || ISALPHA(c) == ctype)); (void)putc(c, &tp->t_rawq); goto endcase; } /* * reprint line (^R) */ if (CCEQ(cc[VREPRINT], c)) { ttyretype(tp); goto endcase; } /* * ^T - kernel info and generate SIGINFO */ if (CCEQ(cc[VSTATUS], c)) { if (ISSET(lflag, ISIG)) pgsignal(tp->t_pgrp, SIGINFO, 1); if (!ISSET(lflag, NOKERNINFO)) ttyinfo(tp); goto endcase; } } /* * Check for input buffer overflow */ if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= TTYHOG) { if (ISSET(iflag, IMAXBEL)) { if (tp->t_outq.c_cc < tp->t_hiwat) (void)ttyoutput(CTRL('g'), tp); } else ttyflush(tp, FREAD | FWRITE); goto endcase; } /* * Put data char in q for user and * wakeup on seeing a line delimiter. */ if (putc(c, &tp->t_rawq) >= 0) { if (!ISSET(lflag, ICANON)) { ttwakeup(tp); ttyecho(c, tp); goto endcase; } if (TTBREAKC(c)) { tp->t_rocount = 0; catq(&tp->t_rawq, &tp->t_canq); ttwakeup(tp); } else if (tp->t_rocount++ == 0) tp->t_rocol = tp->t_column; if (ISSET(tp->t_state, TS_ERASE)) { /* * end of prterase \.../ */ CLR(tp->t_state, TS_ERASE); (void)ttyoutput('/', tp); } i = tp->t_column; ttyecho(c, tp); if (CCEQ(cc[VEOF], c) && ISSET(lflag, ECHO)) { /* * Place the cursor over the '^' of the ^D. */ i = min(2, tp->t_column - i); while (i > 0) { (void)ttyoutput('\b', tp); i--; } } } endcase: /* * IXANY means allow any character to restart output. */ if (ISSET(tp->t_state, TS_TTSTOP) && !ISSET(iflag, IXANY) && cc[VSTART] != cc[VSTOP]) return (0); restartoutput: CLR(tp->t_lflag, FLUSHO); CLR(tp->t_state, TS_TTSTOP); startoutput: return (ttstart(tp)); } /* * Output a single character on a tty, doing output processing * as needed (expanding tabs, newline processing, etc.). * Returns < 0 if succeeds, otherwise returns char to resend. * Must be recursive. */ int ttyoutput(c, tp) register int c; register struct tty *tp; { register long oflag; register int col, s; oflag = tp->t_oflag; if (!ISSET(oflag, OPOST)) { if (ISSET(tp->t_lflag, FLUSHO)) return (-1); if (putc(c, &tp->t_outq)) return (c); tk_nout++; tp->t_outcc++; return (-1); } /* * Do tab expansion if OXTABS is set. Special case if we external * processing, we don't do the tab expansion because we'll probably * get it wrong. If tab expansion needs to be done, let it happen * externally. */ CLR(c, ~TTY_CHARMASK); if (c == '\t' && ISSET(oflag, OXTABS) && !ISSET(tp->t_lflag, EXTPROC)) { c = 8 - (tp->t_column & 7); if (!ISSET(tp->t_lflag, FLUSHO)) { s = spltty(); /* Don't interrupt tabs. */ c -= b_to_q(" ", c, &tp->t_outq); tk_nout += c; tp->t_outcc += c; splx(s); } tp->t_column += c; return (c ? -1 : '\t'); } if (c == CEOT && ISSET(oflag, ONOEOT)) return (-1); /* * Newline translation: if ONLCR is set, * translate newline into "\r\n". */ if (c == '\n' && ISSET(tp->t_oflag, ONLCR)) { tk_nout++; tp->t_outcc++; if (putc('\r', &tp->t_outq)) return (c); } tk_nout++; tp->t_outcc++; if (!ISSET(tp->t_lflag, FLUSHO) && putc(c, &tp->t_outq)) return (c); col = tp->t_column; switch (CCLASS(c)) { case BACKSPACE: if (col > 0) --col; break; case CONTROL: break; case NEWLINE: case RETURN: col = 0; break; case ORDINARY: ++col; break; case TAB: col = (col + 8) & ~7; break; } tp->t_column = col; return (-1); } /* * Ioctls for all tty devices. Called after line-discipline specific ioctl * has been called to do discipline-specific functions and/or reject any * of these ioctl commands. */ /* ARGSUSED */ int ttioctl(tp, cmd, data, flag) register struct tty *tp; int cmd, flag; void *data; { extern struct tty *constty; /* Temporary virtual console. */ extern int nlinesw; register struct proc *p; int s, error; p = curproc; /* XXX */ /* If the ioctl involves modification, hang if in the background. */ switch (cmd) { case TIOCFLUSH: case TIOCSETA: case TIOCSETD: case TIOCSETAF: case TIOCSETAW: #ifdef notdef case TIOCSPGRP: #endif case TIOCSTAT: case TIOCSTI: case TIOCSWINSZ: #if defined(COMPAT_43) || defined(COMPAT_SUNOS) case TIOCLBIC: case TIOCLBIS: case TIOCLSET: case TIOCSETC: case OTIOCSETD: case TIOCSETN: case TIOCSETP: case TIOCSLTC: #endif while (isbackground(curproc, tp) && p->p_pgrp->pg_jobc && (p->p_flag & P_PPWAIT) == 0 && (p->p_sigignore & sigmask(SIGTTOU)) == 0 && (p->p_sigmask & sigmask(SIGTTOU)) == 0) { pgsignal(p->p_pgrp, SIGTTOU, 1); error = ttysleep(tp, &lbolt, TTOPRI | PCATCH, ttybg, 0); if (error) return (error); } break; } switch (cmd) { /* Process the ioctl. */ case FIOASYNC: /* set/clear async i/o */ s = spltty(); if (*(int *)data) SET(tp->t_state, TS_ASYNC); else CLR(tp->t_state, TS_ASYNC); splx(s); break; case FIONBIO: /* set/clear non-blocking i/o */ break; /* XXX: delete. */ case FIONREAD: /* get # bytes to read */ *(int *)data = ttnread(tp); break; case TIOCEXCL: /* set exclusive use of tty */ s = spltty(); SET(tp->t_state, TS_XCLUDE); splx(s); break; case TIOCFLUSH: { /* flush buffers */ register int flags = *(int *)data; if (flags == 0) flags = FREAD | FWRITE; else flags &= FREAD | FWRITE; ttyflush(tp, flags); break; } case TIOCCONS: /* become virtual console */ if (*(int *)data) { if (constty && constty != tp && ISSET(constty->t_state, TS_CARR_ON | TS_ISOPEN) == (TS_CARR_ON | TS_ISOPEN)) return (EBUSY); #ifndef UCONSOLE if (error = suser(p->p_ucred, &p->p_acflag)) return (error); #endif constty = tp; } else if (tp == constty) constty = NULL; break; case TIOCDRAIN: /* wait till output drained */ error = ttywait(tp); if (error) return (error); break; case TIOCGETA: { /* get termios struct */ struct termios *t = (struct termios *)data; bcopy(&tp->t_termios, t, sizeof(struct termios)); break; } case TIOCGETD: /* get line discipline */ *(int *)data = tp->t_line; break; case TIOCGWINSZ: /* get window size */ *(struct winsize *)data = tp->t_winsize; break; case TIOCGPGRP: /* get pgrp of tty */ if (!isctty(p, tp)) return (ENOTTY); *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID; break; #ifdef TIOCHPCL case TIOCHPCL: /* hang up on last close */ s = spltty(); SET(tp->t_cflag, HUPCL); splx(s); break; #endif case TIOCNXCL: /* reset exclusive use of tty */ s = spltty(); CLR(tp->t_state, TS_XCLUDE); splx(s); break; case TIOCOUTQ: /* output queue size */ *(int *)data = tp->t_outq.c_cc; break; case TIOCSETA: /* set termios struct */ case TIOCSETAW: /* drain output, set */ case TIOCSETAF: { /* drn out, fls in, set */ register struct termios *t = (struct termios *)data; s = spltty(); if (cmd == TIOCSETAW || cmd == TIOCSETAF) { error = ttywait(tp); if (error) { splx(s); return (error); } if (cmd == TIOCSETAF) ttyflush(tp, FREAD); } if (!ISSET(t->c_cflag, CIGNORE)) { /* * Set device hardware. */ if (tp->t_param && (error = (*tp->t_param)(tp, t))) { splx(s); return (error); } else { if (!ISSET(tp->t_state, TS_CARR_ON) && ISSET(tp->t_cflag, CLOCAL) && !ISSET(t->c_cflag, CLOCAL)) { #if 0 CLR(tp->t_state, TS_ISOPEN); SET(tp->t_state, TS_WOPEN); #endif ttwakeup(tp); } tp->t_cflag = t->c_cflag; tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; } ttsetwater(tp); } if (cmd != TIOCSETAF) { if (ISSET(t->c_lflag, ICANON) != ISSET(tp->t_lflag, ICANON)) if (ISSET(t->c_lflag, ICANON)) { SET(tp->t_lflag, PENDIN); ttwakeup(tp); } else { struct clist tq; catq(&tp->t_rawq, &tp->t_canq); tq = tp->t_rawq; tp->t_rawq = tp->t_canq; tp->t_canq = tq; CLR(tp->t_lflag, PENDIN); } } tp->t_iflag = t->c_iflag; tp->t_oflag = t->c_oflag; /* * Make the EXTPROC bit read only. */ if (ISSET(tp->t_lflag, EXTPROC)) SET(t->c_lflag, EXTPROC); else CLR(t->c_lflag, EXTPROC); tp->t_lflag = t->c_lflag | ISSET(tp->t_lflag, PENDIN); if (t->c_cc[VMIN] != tp->t_cc[VMIN] || t->c_cc[VTIME] != tp->t_cc[VTIME]) ttwakeup(tp); bcopy(t->c_cc, tp->t_cc, sizeof(t->c_cc)); splx(s); break; } case TIOCSETD: { /* set line discipline */ register int t = *(int *)data; dev_t device = tp->t_dev; if ((u_int)t >= nlinesw) return (ENXIO); if (t != tp->t_line) { s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); error = (*linesw[t].l_open)(device, tp); if (error) { (void)(*linesw[tp->t_line].l_open)(device, tp); splx(s); return (error); } tp->t_line = t; splx(s); } break; } case TIOCSTART: /* start output, like ^Q */ s = spltty(); if (ISSET(tp->t_state, TS_TTSTOP) || ISSET(tp->t_lflag, FLUSHO)) { CLR(tp->t_lflag, FLUSHO); CLR(tp->t_state, TS_TTSTOP); ttstart(tp); } splx(s); break; case TIOCSTI: /* simulate terminal input */ if (p->p_ucred->cr_uid && (flag & FREAD) == 0) return (EPERM); if (p->p_ucred->cr_uid && !isctty(p, tp)) return (EACCES); (*linesw[tp->t_line].l_rint)(*(u_char *)data, tp); break; case TIOCSTOP: /* stop output, like ^S */ s = spltty(); if (!ISSET(tp->t_state, TS_TTSTOP)) { SET(tp->t_state, TS_TTSTOP); #ifdef sun4c /* XXX */ (*tp->t_stop)(tp, 0); #else (*cdevsw[major(tp->t_dev)].d_stop)(tp, 0); #endif } splx(s); break; case TIOCSCTTY: /* become controlling tty */ /* Session ctty vnode pointer set in vnode layer. */ if (!SESS_LEADER(p) || ((p->p_session->s_ttyvp || tp->t_session) && (tp->t_session != p->p_session))) return (EPERM); tp->t_session = p->p_session; tp->t_pgrp = p->p_pgrp; p->p_session->s_ttyp = tp; p->p_flag |= P_CONTROLT; break; case TIOCSPGRP: { /* set pgrp of tty */ register struct pgrp *pgrp = pgfind(*(int *)data); if (!isctty(p, tp)) return (ENOTTY); else if (pgrp == NULL || pgrp->pg_session != p->p_session) return (EPERM); tp->t_pgrp = pgrp; break; } case TIOCSTAT: /* simulate control-T */ ttyinfo(tp); break; case TIOCSWINSZ: /* set window size */ if (bcmp((caddr_t)&tp->t_winsize, data, sizeof (struct winsize))) { tp->t_winsize = *(struct winsize *)data; pgsignal(tp->t_pgrp, SIGWINCH, 1); } break; case TIOCSDRAINWAIT: error = suser(p->p_ucred, &p->p_acflag); if (error) return (error); tp->t_timeout = *(int *)data * hz; wakeup((caddr_t)&tp->t_outq); break; case TIOCGDRAINWAIT: *(int *)data = tp->t_timeout / hz; break; default: #if defined(COMPAT_43) || defined(COMPAT_SUNOS) return (ttcompat(tp, cmd, data, flag)); #else return (-1); #endif } return (0); } int ttyselect(tp, rw, p) struct tty *tp; int rw; struct proc *p; { int nread, s; if (tp == NULL) return (ENXIO); s = spltty(); switch (rw) { case FREAD: nread = ttnread(tp); if (nread > 0 || (!ISSET(tp->t_cflag, CLOCAL) && !ISSET(tp->t_state, TS_CARR_ON))) goto win; selrecord(p, &tp->t_rsel); break; case FWRITE: if (tp->t_outq.c_cc <= tp->t_lowat) { win: splx(s); return (1); } selrecord(p, &tp->t_wsel); break; } splx(s); return (0); +} + +/* + * This is a wrapper for compatibility with the select vector used by + * cdevsw. It relies on a proper xxxdevtotty routine. + */ +int +ttselect(dev, rw, p) + dev_t dev; + int rw; + struct proc *p; +{ + return ttyselect((*cdevsw[major(dev)].d_devtotty)(dev), rw, p); } /* * This is now exported to the cy driver as well; if you hack this code, * then be sure to keep /sys/i386/isa/cy.c properly advised! -jkh */ int ttnread(tp) struct tty *tp; { int nread; if (ISSET(tp->t_lflag, PENDIN)) ttypend(tp); nread = tp->t_canq.c_cc; if (!ISSET(tp->t_lflag, ICANON)) { nread += tp->t_rawq.c_cc; if (nread < tp->t_cc[VMIN] && tp->t_cc[VTIME] == 0) nread = 0; } return (nread); } /* * Wait for output to drain. */ int ttywait(tp) register struct tty *tp; { int error, s; error = 0; s = spltty(); while ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) && (ISSET(tp->t_state, TS_CARR_ON) || ISSET(tp->t_cflag, CLOCAL)) && tp->t_oproc) { (*tp->t_oproc)(tp); if ((tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY)) && (ISSET(tp->t_state, TS_CARR_ON) || ISSET(tp->t_cflag, CLOCAL))) { SET(tp->t_state, TS_ASLEEP); error = ttysleep(tp, &tp->t_outq, TTOPRI | PCATCH, ttyout, tp->t_timeout); if (error) break; } } if (!error && (tp->t_outq.c_cc || ISSET(tp->t_state, TS_BUSY))) error = EIO; splx(s); return (error); } /* * Flush if successfully wait. */ int ttywflush(tp) struct tty *tp; { int error; if ((error = ttywait(tp)) == 0) ttyflush(tp, FREAD); return (error); } /* * Flush tty read and/or write queues, notifying anyone waiting. */ void ttyflush(tp, rw) register struct tty *tp; int rw; { register int s; s = spltty(); if (rw & FWRITE) CLR(tp->t_state, TS_TTSTOP); #ifdef sun4c /* XXX */ (*tp->t_stop)(tp, rw); #else (*cdevsw[major(tp->t_dev)].d_stop)(tp, rw); #endif if (rw & FREAD) { FLUSHQ(&tp->t_canq); FLUSHQ(&tp->t_rawq); tp->t_rocount = 0; tp->t_rocol = 0; CLR(tp->t_state, TS_LOCAL); ttwakeup(tp); } if (rw & FWRITE) { FLUSHQ(&tp->t_outq); wakeup((caddr_t)&tp->t_outq); selwakeup(&tp->t_wsel); } if ((rw & FREAD) && ISSET(tp->t_state, TS_TBLOCK) && tp->t_rawq.c_cc < TTYHOG/5) { if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTART] != _POSIX_VDISABLE && putc(tp->t_cc[VSTART], &tp->t_outq) == 0 || ISSET(tp->t_cflag, CRTS_IFLOW)) { CLR(tp->t_state, TS_TBLOCK); ttstart(tp); } } splx(s); } /* * Copy in the default termios characters. */ void ttychars(tp) struct tty *tp; { bcopy(ttydefchars, tp->t_cc, sizeof(ttydefchars)); } /* * Send stop character on input overflow. */ static void ttyblock(tp) register struct tty *tp; { register int total; total = tp->t_rawq.c_cc + tp->t_canq.c_cc; /* * Block further input iff: current input > threshold * AND input is available to user program. */ if (total >= TTYHOG / 2 && !ISSET(tp->t_state, TS_TBLOCK) && (!ISSET(tp->t_lflag, ICANON) || tp->t_canq.c_cc > 0)) { if (ISSET(tp->t_iflag, IXOFF) && tp->t_cc[VSTOP] != _POSIX_VDISABLE && putc(tp->t_cc[VSTOP], &tp->t_outq) == 0 || ISSET(tp->t_cflag, CRTS_IFLOW)) { SET(tp->t_state, TS_TBLOCK); ttstart(tp); } } } void ttrstrt(tp_arg) void *tp_arg; { struct tty *tp; int s; #ifdef DIAGNOSTIC if (tp_arg == NULL) panic("ttrstrt"); #endif tp = tp_arg; s = spltty(); CLR(tp->t_state, TS_TIMEOUT); ttstart(tp); splx(s); } int ttstart(tp) struct tty *tp; { if (tp->t_oproc != NULL) /* XXX: Kludge for pty. */ (*tp->t_oproc)(tp); return (0); } /* * "close" a line discipline */ int ttylclose(tp, flag) struct tty *tp; int flag; { if ((flag & IO_NDELAY) || ttywflush(tp)) ttyflush(tp, FREAD | FWRITE); return (0); } /* * Handle modem control transition on a tty. * Flag indicates new state of carrier. * Returns 0 if the line should be turned off, otherwise 1. */ int ttymodem(tp, flag) register struct tty *tp; int flag; { if (!ISSET(tp->t_state, TS_WOPEN) && ISSET(tp->t_cflag, MDMBUF)) { /* * MDMBUF: do flow control according to carrier flag */ if (flag) { CLR(tp->t_state, TS_TTSTOP); ttstart(tp); } else if (!ISSET(tp->t_state, TS_TTSTOP)) { SET(tp->t_state, TS_TTSTOP); #ifdef sun4c /* XXX */ (*tp->t_stop)(tp, 0); #else (*cdevsw[major(tp->t_dev)].d_stop)(tp, 0); #endif } } else if (flag == 0) { /* * Lost carrier. */ CLR(tp->t_state, TS_CARR_ON); if (ISSET(tp->t_state, TS_ISOPEN) && !ISSET(tp->t_cflag, CLOCAL)) { if (tp->t_session && tp->t_session->s_leader) psignal(tp->t_session->s_leader, SIGHUP); ttyflush(tp, FREAD | FWRITE); return (0); } } else { /* * Carrier now on. */ SET(tp->t_state, TS_CARR_ON); ttwakeup(tp); } return (1); } /* * Default modem control routine (for other line disciplines). * Return argument flag, to turn off device on carrier drop. */ int nullmodem(tp, flag) register struct tty *tp; int flag; { if (flag) SET(tp->t_state, TS_CARR_ON); else { CLR(tp->t_state, TS_CARR_ON); if (!ISSET(tp->t_cflag, CLOCAL)) { if (tp->t_session && tp->t_session->s_leader) psignal(tp->t_session->s_leader, SIGHUP); return (0); } } return (1); } /* * Reinput pending characters after state switch * call at spltty(). */ void ttypend(tp) register struct tty *tp; { struct clist tq; register c; CLR(tp->t_lflag, PENDIN); SET(tp->t_state, TS_TYPEN); /* * XXX this assumes too much about clist internals. It may even * fail if the cblock slush pool is empty. We can't allocate more * cblocks here because we are called from an interrupt handler * and clist_alloc_cblocks() can wait. */ tq = tp->t_rawq; bzero(&tp->t_rawq, sizeof tp->t_rawq); tp->t_rawq.c_cbmax = tq.c_cbmax; tp->t_rawq.c_cbreserved = tq.c_cbreserved; while ((c = getc(&tq)) >= 0) ttyinput(c, tp); CLR(tp->t_state, TS_TYPEN); } /* * Process a read call on a tty device. */ int ttread(tp, uio, flag) register struct tty *tp; struct uio *uio; int flag; { register struct clist *qp; register int c; register tcflag_t lflag; register cc_t *cc = tp->t_cc; register struct proc *p = curproc; int s, first, error = 0, carrier; int has_stime = 0, last_cc = 0; long slp = 0; /* XXX this should be renamed `timo'. */ loop: s = spltty(); lflag = tp->t_lflag; /* * take pending input first */ if (ISSET(lflag, PENDIN)) { ttypend(tp); splx(s); /* reduce latency */ s = spltty(); lflag = tp->t_lflag; /* XXX ttypend() clobbers it */ } /* * Hang process if it's in the background. */ if (isbackground(p, tp)) { splx(s); if ((p->p_sigignore & sigmask(SIGTTIN)) || (p->p_sigmask & sigmask(SIGTTIN)) || p->p_flag & P_PPWAIT || p->p_pgrp->pg_jobc == 0) return (EIO); pgsignal(p->p_pgrp, SIGTTIN, 1); error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, ttybg, 0); if (error) return (error); goto loop; } /* * If canonical, use the canonical queue, * else use the raw queue. * * (should get rid of clists...) */ qp = ISSET(lflag, ICANON) ? &tp->t_canq : &tp->t_rawq; if (flag & IO_NDELAY) { if (qp->c_cc > 0) goto read; carrier = ISSET(tp->t_state, TS_CARR_ON) || ISSET(tp->t_cflag, CLOCAL); if ((!carrier && ISSET(tp->t_state, TS_ISOPEN)) || !ISSET(lflag, ICANON) && cc[VMIN] == 0) { splx(s); return (0); } splx(s); return (EWOULDBLOCK); } if (!ISSET(lflag, ICANON)) { int m = cc[VMIN]; long t = cc[VTIME]; struct timeval stime, timecopy; int x; /* * Check each of the four combinations. * (m > 0 && t == 0) is the normal read case. * It should be fairly efficient, so we check that and its * companion case (m == 0 && t == 0) first. * For the other two cases, we compute the target sleep time * into slp. */ if (t == 0) { if (qp->c_cc < m) goto sleep; if (qp->c_cc > 0) goto read; /* m, t and qp->c_cc are all 0. 0 is enough input. */ splx(s); return (0); } t *= 100000; /* time in us */ #define diff(t1, t2) (((t1).tv_sec - (t2).tv_sec) * 1000000 + \ ((t1).tv_usec - (t2).tv_usec)) if (m > 0) { if (qp->c_cc <= 0) goto sleep; if (qp->c_cc >= m) goto read; x = splclock(); timecopy = time; splx(x); if (!has_stime) { /* first character, start timer */ has_stime = 1; stime = timecopy; slp = t; } else if (qp->c_cc > last_cc) { /* got a character, restart timer */ stime = timecopy; slp = t; } else { /* nothing, check expiration */ slp = t - diff(timecopy, stime); if (slp <= 0) goto read; } last_cc = qp->c_cc; } else { /* m == 0 */ if (qp->c_cc > 0) goto read; x = splclock(); timecopy = time; splx(x); if (!has_stime) { has_stime = 1; stime = timecopy; slp = t; } else { slp = t - diff(timecopy, stime); if (slp <= 0) { /* Timed out, but 0 is enough input. */ splx(s); return (0); } } } #undef diff /* * Rounding down may make us wake up just short * of the target, so we round up. * The formula is ceiling(slp * hz/1000000). * 32-bit arithmetic is enough for hz < 169. * XXX see hzto() for how to avoid overflow if hz * is large (divide by `tick' and/or arrange to * use hzto() if hz is large). */ slp = (long) (((u_long)slp * hz) + 999999) / 1000000; goto sleep; } /* * If there is no input, sleep on rawq * awaiting hardware receipt and notification. * If we have data, we don't need to check for carrier. */ if (qp->c_cc <= 0) { sleep: carrier = ISSET(tp->t_state, TS_CARR_ON) || ISSET(tp->t_cflag, CLOCAL); if (!carrier && ISSET(tp->t_state, TS_ISOPEN)) { splx(s); return (0); /* EOF */ } error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH, carrier ? ttyin : ttopen, (int)slp); splx(s); if (error == EWOULDBLOCK) error = 0; else if (error) return (error); /* * XXX what happens if another process eats some input * while we are asleep (not just here)? It would be * safest to detect changes and reset our state variables * (has_stime and last_cc). */ slp = 0; goto loop; } read: splx(s); /* * Input present, check for input mapping and processing. */ first = 1; while ((c = getc(qp)) >= 0) { /* * delayed suspend (^Y) */ if (CCEQ(cc[VDSUSP], c) && ISSET(lflag, ISIG)) { pgsignal(tp->t_pgrp, SIGTSTP, 1); if (first) { error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, ttybg, 0); if (error) break; goto loop; } break; } /* * Interpret EOF only in canonical mode. */ if (CCEQ(cc[VEOF], c) && ISSET(lflag, ICANON)) break; #if NSNP > 0 /* * Only when tty echoes characters , we want to * feed them to the snoop device.Else they will come * there if the application would like to. */ if (ISSET(tp->t_lflag, ECHO)) if (ISSET(tp->t_state, TS_SNOOP) && tp->t_sc != NULL) snpinc((struct snoop *)tp->t_sc, (char)c); #endif /* * Give user character. */ error = ureadc(c, uio); if (error) break; if (uio->uio_resid == 0) break; /* * In canonical mode check for a "break character" * marking the end of a "line of input". */ if (ISSET(lflag, ICANON) && TTBREAKC(c)) break; first = 0; } /* * Look to unblock output now that (presumably) * the input queue has gone down. */ s = spltty(); if (ISSET(tp->t_state, TS_TBLOCK) && tp->t_rawq.c_cc < TTYHOG/5) { if (ISSET(tp->t_iflag, IXOFF) && cc[VSTART] != _POSIX_VDISABLE && putc(cc[VSTART], &tp->t_outq) == 0 || ISSET(tp->t_cflag, CRTS_IFLOW)) { CLR(tp->t_state, TS_TBLOCK); ttstart(tp); } } splx(s); return (error); } /* * Check the output queue on tp for space for a kernel message (from uprintf * or tprintf). Allow some space over the normal hiwater mark so we don't * lose messages due to normal flow control, but don't let the tty run amok. * Sleeps here are not interruptible, but we return prematurely if new signals * arrive. */ int ttycheckoutq(tp, wait) register struct tty *tp; int wait; { int hiwat, s, oldsig; hiwat = tp->t_hiwat; s = spltty(); oldsig = wait ? curproc->p_siglist : 0; if (tp->t_outq.c_cc > hiwat + 200) while (tp->t_outq.c_cc > hiwat) { ttstart(tp); if (wait == 0 || curproc->p_siglist != oldsig) { splx(s); return (0); } timeout((void (*)__P((void *)))wakeup, (void *)&tp->t_outq, hz); SET(tp->t_state, TS_ASLEEP); (void) tsleep((caddr_t)&tp->t_outq, PZERO - 1, "ttoutq", 0); } splx(s); return (1); } /* * Process a write call on a tty device. */ int ttwrite(tp, uio, flag) register struct tty *tp; register struct uio *uio; int flag; { register char *cp = 0; register int cc, ce; register struct proc *p; int i, hiwat, cnt, error, s; char obuf[OBUFSIZ]; hiwat = tp->t_hiwat; cnt = uio->uio_resid; error = 0; cc = 0; loop: s = spltty(); if (!ISSET(tp->t_state, TS_CARR_ON) && !ISSET(tp->t_cflag, CLOCAL)) { if (ISSET(tp->t_state, TS_ISOPEN)) { splx(s); return (EIO); } else if (flag & IO_NDELAY) { splx(s); error = EWOULDBLOCK; goto out; } else { /* Sleep awaiting carrier. */ error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH,ttopen, 0); splx(s); if (error) goto out; goto loop; } } splx(s); /* * Hang the process if it's in the background. */ p = curproc; if (isbackground(p, tp) && ISSET(tp->t_lflag, TOSTOP) && (p->p_flag & P_PPWAIT) == 0 && (p->p_sigignore & sigmask(SIGTTOU)) == 0 && (p->p_sigmask & sigmask(SIGTTOU)) == 0 && p->p_pgrp->pg_jobc) { pgsignal(p->p_pgrp, SIGTTOU, 1); error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, ttybg, 0); if (error) goto out; goto loop; } /* * Process the user's data in at most OBUFSIZ chunks. Perform any * output translation. Keep track of high water mark, sleep on * overflow awaiting device aid in acquiring new space. */ while (uio->uio_resid > 0 || cc > 0) { if (ISSET(tp->t_lflag, FLUSHO)) { uio->uio_resid = 0; return (0); } if (tp->t_outq.c_cc > hiwat) goto ovhiwat; /* * Grab a hunk of data from the user, unless we have some * leftover from last time. */ if (cc == 0) { cc = min(uio->uio_resid, OBUFSIZ); cp = obuf; error = uiomove(cp, cc, uio); if (error) { cc = 0; break; } #if NSNP > 0 if (ISSET(tp->t_state, TS_SNOOP) && tp->t_sc != NULL) snpin((struct snoop *)tp->t_sc, cp, cc); #endif } /* * If nothing fancy need be done, grab those characters we * can handle without any of ttyoutput's processing and * just transfer them to the output q. For those chars * which require special processing (as indicated by the * bits in char_type), call ttyoutput. After processing * a hunk of data, look for FLUSHO so ^O's will take effect * immediately. */ while (cc > 0) { if (!ISSET(tp->t_oflag, OPOST)) ce = cc; else { ce = cc - scanc((u_int)cc, (u_char *)cp, (u_char *)char_type, CCLASSMASK); /* * If ce is zero, then we're processing * a special character through ttyoutput. */ if (ce == 0) { tp->t_rocount = 0; if (ttyoutput(*cp, tp) >= 0) { /* No Clists, wait a bit. */ ttstart(tp); if (flag & IO_NDELAY) { error = EWOULDBLOCK; goto out; } error = ttysleep(tp, &lbolt, TTOPRI | PCATCH, ttybuf, 0); if (error) goto out; goto loop; } cp++; cc--; if (ISSET(tp->t_lflag, FLUSHO) || tp->t_outq.c_cc > hiwat) goto ovhiwat; continue; } } /* * A bunch of normal characters have been found. * Transfer them en masse to the output queue and * continue processing at the top of the loop. * If there are any further characters in this * <= OBUFSIZ chunk, the first should be a character * requiring special handling by ttyoutput. */ tp->t_rocount = 0; i = b_to_q(cp, ce, &tp->t_outq); ce -= i; tp->t_column += ce; cp += ce, cc -= ce, tk_nout += ce; tp->t_outcc += ce; if (i > 0) { /* No Clists, wait a bit. */ ttstart(tp); if (flag & IO_NDELAY) { error = EWOULDBLOCK; goto out; } error = ttysleep(tp, &lbolt, TTOPRI | PCATCH, ttybuf, 0); if (error) goto out; goto loop; } if (ISSET(tp->t_lflag, FLUSHO) || tp->t_outq.c_cc > hiwat) break; } ttstart(tp); } out: /* * If cc is nonzero, we leave the uio structure inconsistent, as the * offset and iov pointers have moved forward, but it doesn't matter * (the call will either return short or restart with a new uio). */ uio->uio_resid += cc; return (error); ovhiwat: ttstart(tp); s = spltty(); /* * This can only occur if FLUSHO is set in t_lflag, * or if ttstart/oproc is synchronous (or very fast). */ if (tp->t_outq.c_cc <= hiwat) { splx(s); goto loop; } if (flag & IO_NDELAY) { splx(s); uio->uio_resid += cc; return (uio->uio_resid == cnt ? EWOULDBLOCK : 0); } SET(tp->t_state, TS_ASLEEP); error = ttysleep(tp, &tp->t_outq, TTOPRI | PCATCH, ttyout, 0); splx(s); if (error) goto out; goto loop; } /* * Rubout one character from the rawq of tp * as cleanly as possible. */ void ttyrub(c, tp) register int c; register struct tty *tp; { register char *cp; register int savecol; int tabc, s; if (!ISSET(tp->t_lflag, ECHO) || ISSET(tp->t_lflag, EXTPROC)) return; CLR(tp->t_lflag, FLUSHO); if (ISSET(tp->t_lflag, ECHOE)) { if (tp->t_rocount == 0) { /* * Screwed by ttwrite; retype */ ttyretype(tp); return; } if (c == ('\t' | TTY_QUOTE) || c == ('\n' | TTY_QUOTE)) ttyrubo(tp, 2); else { CLR(c, ~TTY_CHARMASK); switch (CCLASS(c)) { case ORDINARY: ttyrubo(tp, 1); break; case BACKSPACE: case CONTROL: case NEWLINE: case RETURN: case VTAB: if (ISSET(tp->t_lflag, ECHOCTL)) ttyrubo(tp, 2); break; case TAB: if (tp->t_rocount < tp->t_rawq.c_cc) { ttyretype(tp); return; } s = spltty(); savecol = tp->t_column; SET(tp->t_state, TS_CNTTB); SET(tp->t_lflag, FLUSHO); tp->t_column = tp->t_rocol; cp = tp->t_rawq.c_cf; if (cp) tabc = *cp; /* XXX FIX NEXTC */ for (; cp; cp = nextc(&tp->t_rawq, cp, &tabc)) ttyecho(tabc, tp); CLR(tp->t_lflag, FLUSHO); CLR(tp->t_state, TS_CNTTB); splx(s); /* savecol will now be length of the tab. */ savecol -= tp->t_column; tp->t_column += savecol; if (savecol > 8) savecol = 8; /* overflow screw */ while (--savecol >= 0) (void)ttyoutput('\b', tp); break; default: /* XXX */ #define PANICSTR "ttyrub: would panic c = %d, val = %d\n" (void)printf(PANICSTR, c, CCLASS(c)); #ifdef notdef panic(PANICSTR, c, CCLASS(c)); #endif } } } else if (ISSET(tp->t_lflag, ECHOPRT)) { if (!ISSET(tp->t_state, TS_ERASE)) { SET(tp->t_state, TS_ERASE); (void)ttyoutput('\\', tp); } ttyecho(c, tp); } else ttyecho(tp->t_cc[VERASE], tp); --tp->t_rocount; } /* * Back over cnt characters, erasing them. */ static void ttyrubo(tp, cnt) register struct tty *tp; int cnt; { while (cnt-- > 0) { (void)ttyoutput('\b', tp); (void)ttyoutput(' ', tp); (void)ttyoutput('\b', tp); } } /* * ttyretype -- * Reprint the rawq line. Note, it is assumed that c_cc has already * been checked. */ void ttyretype(tp) register struct tty *tp; { register char *cp; int s, c; /* Echo the reprint character. */ if (tp->t_cc[VREPRINT] != _POSIX_VDISABLE) ttyecho(tp->t_cc[VREPRINT], tp); (void)ttyoutput('\n', tp); /* * XXX * FIX: NEXTC IS BROKEN - DOESN'T CHECK QUOTE * BIT OF FIRST CHAR. */ s = spltty(); for (cp = tp->t_canq.c_cf, c = (cp != NULL ? *cp : 0); cp != NULL; cp = nextc(&tp->t_canq, cp, &c)) ttyecho(c, tp); for (cp = tp->t_rawq.c_cf, c = (cp != NULL ? *cp : 0); cp != NULL; cp = nextc(&tp->t_rawq, cp, &c)) ttyecho(c, tp); CLR(tp->t_state, TS_ERASE); splx(s); tp->t_rocount = tp->t_rawq.c_cc; tp->t_rocol = 0; } /* * Echo a typed character to the terminal. */ static void ttyecho(c, tp) register int c; register struct tty *tp; { if (!ISSET(tp->t_state, TS_CNTTB)) CLR(tp->t_lflag, FLUSHO); if ((!ISSET(tp->t_lflag, ECHO) && (!ISSET(tp->t_lflag, ECHONL) || c == '\n')) || ISSET(tp->t_lflag, EXTPROC)) return; if (ISSET(tp->t_lflag, ECHOCTL) && ((ISSET(c, TTY_CHARMASK) <= 037 && c != '\t' && c != '\n') || ISSET(c, TTY_CHARMASK) == 0177)) { (void)ttyoutput('^', tp); CLR(c, ~TTY_CHARMASK); if (c == 0177) c = '?'; else c += 'A' - 1; } (void)ttyoutput(c, tp); } /* * Wake up any readers on a tty. */ void ttwakeup(tp) register struct tty *tp; { selwakeup(&tp->t_rsel); if (ISSET(tp->t_state, TS_ASYNC)) pgsignal(tp->t_pgrp, SIGIO, 1); wakeup((caddr_t)&tp->t_rawq); } /* * Look up a code for a specified speed in a conversion table; * used by drivers to map software speed values to hardware parameters. */ int ttspeedtab(speed, table) int speed; register struct speedtab *table; { for ( ; table->sp_speed != -1; table++) if (table->sp_speed == speed) return (table->sp_code); return (-1); } /* * Set tty hi and low water marks. * * Try to arrange the dynamics so there's about one second * from hi to low water. * */ void ttsetwater(tp) struct tty *tp; { register int cps, x; #define CLAMP(x, h, l) ((x) > h ? h : ((x) < l) ? l : (x)) cps = tp->t_ospeed / 10; tp->t_lowat = x = CLAMP(cps / 2, TTMAXLOWAT, TTMINLOWAT); x += cps; x = CLAMP(x, TTMAXHIWAT, TTMINHIWAT); tp->t_hiwat = roundup(x, CBSIZE); #undef CLAMP } /* * Report on state of foreground process group. */ void ttyinfo(tp) register struct tty *tp; { register struct proc *p, *pick; struct timeval utime, stime; int tmp; if (ttycheckoutq(tp,0) == 0) return; /* Print load average. */ tmp = (averunnable.ldavg[0] * 100 + FSCALE / 2) >> FSHIFT; ttyprintf(tp, "load: %d.%02d ", tmp / 100, tmp % 100); if (tp->t_session == NULL) ttyprintf(tp, "not a controlling terminal\n"); else if (tp->t_pgrp == NULL) ttyprintf(tp, "no foreground process group\n"); else if ((p = tp->t_pgrp->pg_mem) == NULL) ttyprintf(tp, "empty foreground process group\n"); else { /* Pick interesting process. */ for (pick = NULL; p != NULL; p = p->p_pgrpnxt) if (proc_compare(pick, p)) pick = p; ttyprintf(tp, " cmd: %s %d [%s] ", pick->p_comm, pick->p_pid, pick->p_stat == SRUN ? "running" : pick->p_wmesg ? pick->p_wmesg : "iowait"); calcru(pick, &utime, &stime, NULL); /* Print user time. */ ttyprintf(tp, "%d.%02du ", utime.tv_sec, utime.tv_usec / 10000); /* Print system time. */ ttyprintf(tp, "%d.%02ds ", stime.tv_sec, stime.tv_usec / 10000); #define pgtok(a) (((a) * NBPG) / 1024) /* Print percentage cpu, resident set size. */ tmp = (pick->p_pctcpu * 10000 + FSCALE / 2) >> FSHIFT; ttyprintf(tp, "%d%% %dk\n", tmp / 100, pick->p_stat == SIDL || pick->p_stat == SZOMB ? 0 : #ifdef pmap_resident_count pgtok(pmap_resident_count(&pick->p_vmspace->vm_pmap)) #else pgtok(pick->p_vmspace->vm_rssize) #endif ); } tp->t_rocount = 0; /* so pending input will be retyped if BS */ } /* * Returns 1 if p2 is "better" than p1 * * The algorithm for picking the "interesting" process is thus: * * 1) Only foreground processes are eligible - implied. * 2) Runnable processes are favored over anything else. The runner * with the highest cpu utilization is picked (p_estcpu). Ties are * broken by picking the highest pid. * 3) The sleeper with the shortest sleep time is next. With ties, * we pick out just "short-term" sleepers (P_SINTR == 0). * 4) Further ties are broken by picking the highest pid. */ #define ISRUN(p) (((p)->p_stat == SRUN) || ((p)->p_stat == SIDL)) #define TESTAB(a, b) ((a)<<1 | (b)) #define ONLYA 2 #define ONLYB 1 #define BOTH 3 static int proc_compare(p1, p2) register struct proc *p1, *p2; { if (p1 == NULL) return (1); /* * see if at least one of them is runnable */ switch (TESTAB(ISRUN(p1), ISRUN(p2))) { case ONLYA: return (0); case ONLYB: return (1); case BOTH: /* * tie - favor one with highest recent cpu utilization */ if (p2->p_estcpu > p1->p_estcpu) return (1); if (p1->p_estcpu > p2->p_estcpu) return (0); return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ } /* * weed out zombies */ switch (TESTAB(p1->p_stat == SZOMB, p2->p_stat == SZOMB)) { case ONLYA: return (1); case ONLYB: return (0); case BOTH: return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ } /* * pick the one with the smallest sleep time */ if (p2->p_slptime > p1->p_slptime) return (0); if (p1->p_slptime > p2->p_slptime) return (1); /* * favor one sleeping in a non-interruptible sleep */ if (p1->p_flag & P_SINTR && (p2->p_flag & P_SINTR) == 0) return (1); if (p2->p_flag & P_SINTR && (p1->p_flag & P_SINTR) == 0) return (0); return (p2->p_pid > p1->p_pid); /* tie - return highest pid */ } /* * Output char to tty; console putchar style. */ int tputchar(c, tp) int c; struct tty *tp; { register int s; s = spltty(); if (ISSET(tp->t_state, TS_CARR_ON | TS_ISOPEN) != (TS_CARR_ON | TS_ISOPEN)) { splx(s); return (-1); } if (c == '\n') (void)ttyoutput('\r', tp); (void)ttyoutput(c, tp); ttstart(tp); splx(s); return (0); } /* * Sleep on chan, returning ERESTART if tty changed while we napped and * returning any errors (e.g. EINTR/ETIMEDOUT) reported by tsleep. If * the tty is revoked, restarting a pending call will redo validation done * at the start of the call. */ int ttysleep(tp, chan, pri, wmesg, timo) struct tty *tp; void *chan; int pri, timo; char *wmesg; { int error; short gen; gen = tp->t_gen; error = tsleep(chan, pri, wmesg, timo); if (error) return (error); return (tp->t_gen == gen ? 0 : ERESTART); } /* * XXX this is usable not useful or used. Most tty drivers have * ifdefs for using ttymalloc() but assume a different interface. */ /* * Allocate a tty struct. Clists in the struct will be allocated by * ttyopen(). */ struct tty * ttymalloc() { struct tty *tp; tp = malloc(sizeof *tp, M_TTYS, M_WAITOK); bzero(tp, sizeof *tp); return (tp); } #if 0 /* XXX not yet usable: session leader holds a ref (see kern_exit.c). */ /* * Free a tty struct. Clists in the struct should have been freed by * ttyclose(). */ void ttyfree(tp) struct tty *tp; { free(tp, M_TTYS); } #endif /* 0 */ Index: head/sys/kern/tty_pty.c =================================================================== --- head/sys/kern/tty_pty.c (revision 6781) +++ head/sys/kern/tty_pty.c (revision 6782) @@ -1,731 +1,721 @@ /* * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tty_pty.c 8.2 (Berkeley) 9/23/93 - * $Id: tty_pty.c,v 1.6 1994/10/29 23:59:48 ache Exp $ + * $Id: tty_pty.c,v 1.7 1995/02/25 20:09:30 pst Exp $ */ /* * Pseudo-teletype Driver * (Actually two drivers, requiring two entries in 'cdevsw') */ #include "pty.h" /* XXX */ #include #include #include #include #include #include #include #include #include #include #include #if NPTY == 1 #undef NPTY #define NPTY 32 /* crude XXX */ #endif #define BUFSIZ 100 /* Chunk size iomoved to/from user */ /* * pts == /dev/tty[pqrs]? * ptc == /dev/pty[pqrs]? */ struct tty pt_tty[NPTY]; /* XXX */ struct pt_ioctl { int pt_flags; struct selinfo pt_selr, pt_selw; u_char pt_send; u_char pt_ucntl; } pt_ioctl[NPTY]; /* XXX */ int npty = NPTY; /* for pstat -t */ #define PF_PKT 0x08 /* packet mode */ #define PF_STOPPED 0x10 /* user told stopped */ #define PF_REMOTE 0x20 /* remote and flow controlled input */ #define PF_NOSTOP 0x40 #define PF_UCNTL 0x80 /* user control mode */ void ptsstop __P((struct tty *, int)); void ptcwakeup __P((struct tty *, int)); /* * Establish n (or default if n is 1) ptys in the system. * * XXX cdevsw & pstat require the array `pty[]' to be an array */ void ptyattach(n) int n; { #ifdef notyet char *mem; register u_long ntb; #define DEFAULT_NPTY 32 /* maybe should allow 0 => none? */ if (n <= 1) n = DEFAULT_NPTY; ntb = n * sizeof(struct tty); mem = malloc(ntb + ALIGNBYTES + n * sizeof(struct pt_ioctl), M_DEVBUF, M_WAITOK); pt_tty = (struct tty *)mem; mem = (char *)ALIGN(mem + ntb); pt_ioctl = (struct pt_ioctl *)mem; npty = n; #endif } /*ARGSUSED*/ int ptsopen(dev, flag, devtype, p) dev_t dev; int flag, devtype; struct proc *p; { register struct tty *tp; int error; if (minor(dev) >= npty) return (ENXIO); tp = &pt_tty[minor(dev)]; if ((tp->t_state & TS_ISOPEN) == 0) { tp->t_state |= TS_WOPEN; ttychars(tp); /* Set up default chars */ tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; ttsetwater(tp); /* would be done in xxparam() */ } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0) return (EBUSY); if (tp->t_oproc) /* Ctrlr still around. */ tp->t_state |= TS_CARR_ON; while ((tp->t_state & TS_CARR_ON) == 0) { tp->t_state |= TS_WOPEN; if (flag&FNONBLOCK) break; error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH, ttopen, 0); if (error) return (error); } error = (*linesw[tp->t_line].l_open)(dev, tp); ptcwakeup(tp, FREAD|FWRITE); return (error); } int ptsclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { register struct tty *tp; int err; tp = &pt_tty[minor(dev)]; err = (*linesw[tp->t_line].l_close)(tp, flag); err |= ttyclose(tp); ptcwakeup(tp, FREAD|FWRITE); return (err); } int ptsread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct proc *p = curproc; register struct tty *tp = &pt_tty[minor(dev)]; register struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; int error = 0; again: if (pti->pt_flags & PF_REMOTE) { while (isbackground(p, tp)) { if ((p->p_sigignore & sigmask(SIGTTIN)) || (p->p_sigmask & sigmask(SIGTTIN)) || p->p_pgrp->pg_jobc == 0 || p->p_flag & P_PPWAIT) return (EIO); pgsignal(p->p_pgrp, SIGTTIN, 1); error = ttysleep(tp, (caddr_t)&lbolt, TTIPRI | PCATCH, ttybg, 0); if (error) return (error); } if (tp->t_canq.c_cc == 0) { if (flag & IO_NDELAY) return (EWOULDBLOCK); error = ttysleep(tp, (caddr_t)&tp->t_canq, TTIPRI | PCATCH, ttyin, 0); if (error) return (error); goto again; } while (tp->t_canq.c_cc > 1 && uio->uio_resid > 0) if (ureadc(getc(&tp->t_canq), uio) < 0) { error = EFAULT; break; } if (tp->t_canq.c_cc == 1) (void) getc(&tp->t_canq); if (tp->t_canq.c_cc) return (error); } else if (tp->t_oproc) error = (*linesw[tp->t_line].l_read)(tp, uio, flag); ptcwakeup(tp, FWRITE); return (error); } /* * Write to pseudo-tty. * Wakeups of controlling tty will happen * indirectly, when tty driver calls ptsstart. */ int ptswrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { register struct tty *tp; tp = &pt_tty[minor(dev)]; if (tp->t_oproc == 0) return (EIO); return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } /* * Start output on pseudo-tty. * Wake up process selecting or sleeping for input from controlling tty. */ void ptsstart(tp) struct tty *tp; { register struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)]; if (tp->t_state & TS_TTSTOP) return; if (pti->pt_flags & PF_STOPPED) { pti->pt_flags &= ~PF_STOPPED; pti->pt_send = TIOCPKT_START; } ptcwakeup(tp, FREAD); } void ptcwakeup(tp, flag) struct tty *tp; int flag; { struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)]; if (flag & FREAD) { selwakeup(&pti->pt_selr); wakeup((caddr_t)&tp->t_outq.c_cf); } if (flag & FWRITE) { selwakeup(&pti->pt_selw); wakeup((caddr_t)&tp->t_rawq.c_cl); } } /*ARGSUSED*/ #ifdef __STDC__ int ptcopen(dev_t dev, int flag, int devtype, struct proc *p) #else int ptcopen(dev, flag, devtype, p) dev_t dev; int flag, devtype; struct proc *p; #endif { register struct tty *tp; struct pt_ioctl *pti; if (minor(dev) >= npty) return (ENXIO); tp = &pt_tty[minor(dev)]; if (tp->t_oproc) return (EIO); tp->t_oproc = ptsstart; #ifdef sun4c tp->t_stop = ptsstop; #endif (void)(*linesw[tp->t_line].l_modem)(tp, 1); tp->t_lflag &= ~EXTPROC; pti = &pt_ioctl[minor(dev)]; pti->pt_flags = 0; pti->pt_send = 0; pti->pt_ucntl = 0; return (0); } int ptcclose(dev) dev_t dev; { register struct tty *tp; tp = &pt_tty[minor(dev)]; (void)(*linesw[tp->t_line].l_modem)(tp, 0); tp->t_state &= ~TS_CARR_ON; tp->t_oproc = 0; /* mark closed */ tp->t_session = 0; return (0); } int ptcread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { register struct tty *tp = &pt_tty[minor(dev)]; struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; char buf[BUFSIZ]; int error = 0, cc; /* * We want to block until the slave * is open, and there's something to read; * but if we lost the slave or we're NBIO, * then return the appropriate error instead. */ for (;;) { if (tp->t_state&TS_ISOPEN) { if (pti->pt_flags&PF_PKT && pti->pt_send) { error = ureadc((int)pti->pt_send, uio); if (error) return (error); if (pti->pt_send & TIOCPKT_IOCTL) { cc = min(uio->uio_resid, sizeof(tp->t_termios)); uiomove((caddr_t)&tp->t_termios, cc, uio); } pti->pt_send = 0; return (0); } if (pti->pt_flags&PF_UCNTL && pti->pt_ucntl) { error = ureadc((int)pti->pt_ucntl, uio); if (error) return (error); pti->pt_ucntl = 0; return (0); } if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0) break; } if ((tp->t_state&TS_CARR_ON) == 0) return (0); /* EOF */ if (flag & IO_NDELAY) return (EWOULDBLOCK); error = tsleep((caddr_t)&tp->t_outq.c_cf, TTIPRI | PCATCH, ttyin, 0); if (error) return (error); } if (pti->pt_flags & (PF_PKT|PF_UCNTL)) error = ureadc(0, uio); while (uio->uio_resid > 0 && error == 0) { cc = q_to_b(&tp->t_outq, buf, min(uio->uio_resid, BUFSIZ)); if (cc <= 0) break; error = uiomove(buf, cc, uio); } if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } selwakeup(&tp->t_wsel); } return (error); } void ptsstop(tp, flush) register struct tty *tp; int flush; { struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)]; int flag; /* note: FLUSHREAD and FLUSHWRITE already ok */ if (flush == 0) { flush = TIOCPKT_STOP; pti->pt_flags |= PF_STOPPED; } else pti->pt_flags &= ~PF_STOPPED; pti->pt_send |= flush; /* change of perspective */ flag = 0; if (flush & FREAD) flag |= FWRITE; if (flush & FWRITE) flag |= FREAD; ptcwakeup(tp, flag); } int ptcselect(dev, rw, p) dev_t dev; int rw; struct proc *p; { register struct tty *tp = &pt_tty[minor(dev)]; struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; int s; if ((tp->t_state&TS_CARR_ON) == 0) return (1); switch (rw) { case FREAD: /* * Need to block timeouts (ttrstart). */ s = spltty(); if ((tp->t_state&TS_ISOPEN) && tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0) { splx(s); return (1); } splx(s); /* FALLTHROUGH */ case 0: /* exceptional */ if ((tp->t_state&TS_ISOPEN) && ((pti->pt_flags&PF_PKT && pti->pt_send) || (pti->pt_flags&PF_UCNTL && pti->pt_ucntl))) return (1); selrecord(p, &pti->pt_selr); break; case FWRITE: if (tp->t_state&TS_ISOPEN) { if (pti->pt_flags & PF_REMOTE) { if (tp->t_canq.c_cc == 0) return (1); } else { if (tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG-2) return (1); if (tp->t_canq.c_cc == 0 && (tp->t_iflag&ICANON)) return (1); } } selrecord(p, &pti->pt_selw); break; } return (0); } int ptcwrite(dev, uio, flag) dev_t dev; register struct uio *uio; int flag; { register struct tty *tp = &pt_tty[minor(dev)]; register u_char *cp = 0; register int cc = 0; u_char locbuf[BUFSIZ]; int cnt = 0; struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; int error = 0; again: if ((tp->t_state&TS_ISOPEN) == 0) goto block; if (pti->pt_flags & PF_REMOTE) { if (tp->t_canq.c_cc) goto block; while (uio->uio_resid > 0 && tp->t_canq.c_cc < TTYHOG - 1) { if (cc == 0) { cc = min(uio->uio_resid, BUFSIZ); cc = min(cc, TTYHOG - 1 - tp->t_canq.c_cc); cp = locbuf; error = uiomove((caddr_t)cp, cc, uio); if (error) return (error); /* check again for safety */ if ((tp->t_state&TS_ISOPEN) == 0) return (EIO); } if (cc) (void) b_to_q((char *)cp, cc, &tp->t_canq); cc = 0; } (void) putc(0, &tp->t_canq); ttwakeup(tp); wakeup((caddr_t)&tp->t_canq); return (0); } while (uio->uio_resid > 0) { if (cc == 0) { cc = min(uio->uio_resid, BUFSIZ); cp = locbuf; error = uiomove((caddr_t)cp, cc, uio); if (error) return (error); /* check again for safety */ if ((tp->t_state&TS_ISOPEN) == 0) return (EIO); } while (cc > 0) { if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 && (tp->t_canq.c_cc > 0 || !(tp->t_iflag&ICANON))) { wakeup((caddr_t)&tp->t_rawq); goto block; } (*linesw[tp->t_line].l_rint)(*cp++, tp); cnt++; cc--; } cc = 0; } return (0); block: /* * Come here to wait for slave to open, for space * in outq, or space in rawq. */ if ((tp->t_state&TS_CARR_ON) == 0) return (EIO); if (flag & IO_NDELAY) { /* adjust for data copied in but not written */ uio->uio_resid += cc; if (cnt == 0) return (EWOULDBLOCK); return (0); } error = tsleep((caddr_t)&tp->t_rawq.c_cl, TTOPRI | PCATCH, ttyout, 0); if (error) { /* adjust for data copied in but not written */ uio->uio_resid += cc; return (error); } goto again; } struct tty * ptydevtotty(dev) dev_t dev; { if (minor(dev) >= npty) return (NULL); return &pt_tty[minor(dev)]; } - -int -ptsselect(dev, rw, p) - dev_t dev; - int rw; - struct proc * p; -{ - return ttyselect(ptydevtotty(dev), rw, p); -} - /*ARGSUSED*/ int ptyioctl(dev, cmd, data, flag, p) dev_t dev; int cmd; caddr_t data; int flag; struct proc *p; { register struct tty *tp = &pt_tty[minor(dev)]; register struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; register u_char *cc = tp->t_cc; int stop, error; /* * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG. * ttywflush(tp) will hang if there are characters in the outq. */ if (cmd == TIOCEXT) { /* * When the EXTPROC bit is being toggled, we need * to send an TIOCPKT_IOCTL if the packet driver * is turned on. */ if (*(int *)data) { if (pti->pt_flags & PF_PKT) { pti->pt_send |= TIOCPKT_IOCTL; ptcwakeup(tp, FREAD); } tp->t_lflag |= EXTPROC; } else { if ((tp->t_state & EXTPROC) && (pti->pt_flags & PF_PKT)) { pti->pt_send |= TIOCPKT_IOCTL; ptcwakeup(tp, FREAD); } tp->t_lflag &= ~EXTPROC; } return(0); } else if (cdevsw[major(dev)].d_open == ptcopen) switch (cmd) { case TIOCGPGRP: /* * We aviod calling ttioctl on the controller since, * in that case, tp must be the controlling terminal. */ *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0; return (0); case TIOCPKT: if (*(int *)data) { if (pti->pt_flags & PF_UCNTL) return (EINVAL); pti->pt_flags |= PF_PKT; } else pti->pt_flags &= ~PF_PKT; return (0); case TIOCUCNTL: if (*(int *)data) { if (pti->pt_flags & PF_PKT) return (EINVAL); pti->pt_flags |= PF_UCNTL; } else pti->pt_flags &= ~PF_UCNTL; return (0); case TIOCREMOTE: if (*(int *)data) pti->pt_flags |= PF_REMOTE; else pti->pt_flags &= ~PF_REMOTE; ttyflush(tp, FREAD|FWRITE); return (0); #ifdef COMPAT_43 case TIOCSETP: case TIOCSETN: #endif case TIOCSETD: case TIOCSETA: case TIOCSETAW: case TIOCSETAF: ndflush(&tp->t_outq, tp->t_outq.c_cc); break; case TIOCSIG: if (*(unsigned int *)data >= NSIG) return(EINVAL); if ((tp->t_lflag&NOFLSH) == 0) ttyflush(tp, FREAD|FWRITE); pgsignal(tp->t_pgrp, *(unsigned int *)data, 1); if ((*(unsigned int *)data == SIGINFO) && ((tp->t_lflag&NOKERNINFO) == 0)) ttyinfo(tp); return(0); } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error < 0) error = ttioctl(tp, cmd, data, flag); if (error < 0) { if (pti->pt_flags & PF_UCNTL && (cmd & ~0xff) == UIOCCMD(0)) { if (cmd & 0xff) { pti->pt_ucntl = (u_char)cmd; ptcwakeup(tp, FREAD); } return (0); } error = ENOTTY; } /* * If external processing and packet mode send ioctl packet. */ if ((tp->t_lflag&EXTPROC) && (pti->pt_flags & PF_PKT)) { switch(cmd) { case TIOCSETA: case TIOCSETAW: case TIOCSETAF: #ifdef COMPAT_43 case TIOCSETP: case TIOCSETN: #endif #if defined(COMPAT_43) || defined(COMPAT_SUNOS) case TIOCSETC: case TIOCSLTC: case TIOCLBIS: case TIOCLBIC: case TIOCLSET: #endif pti->pt_send |= TIOCPKT_IOCTL; ptcwakeup(tp, FREAD); default: break; } } stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s')) && CCEQ(cc[VSTART], CTRL('q')); if (pti->pt_flags & PF_NOSTOP) { if (stop) { pti->pt_send &= ~TIOCPKT_NOSTOP; pti->pt_send |= TIOCPKT_DOSTOP; pti->pt_flags &= ~PF_NOSTOP; ptcwakeup(tp, FREAD); } } else { if (!stop) { pti->pt_send &= ~TIOCPKT_DOSTOP; pti->pt_send |= TIOCPKT_NOSTOP; pti->pt_flags |= PF_NOSTOP; ptcwakeup(tp, FREAD); } } return (error); } Index: head/sys/sys/tty.h =================================================================== --- head/sys/sys/tty.h (revision 6781) +++ head/sys/sys/tty.h (revision 6782) @@ -1,238 +1,239 @@ /*- * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tty.h 8.6 (Berkeley) 1/21/94 - * $Id: tty.h,v 1.10 1995/02/14 21:23:48 ugen Exp $ + * $Id: tty.h,v 1.11 1995/02/25 20:09:44 pst Exp $ */ #ifndef _SYS_TTY_H_ #define _SYS_TTY_H_ #include #include /* For struct selinfo. */ /* * Clists are character lists, which is a variable length linked list * of cblocks, with a count of the number of characters in the list. */ struct clist { int c_cc; /* Number of characters in the clist. */ int c_cbcount; /* Number of cblocks. */ int c_cbmax; /* Max # cblocks allowed for this clist. */ int c_cbreserved; /* # cblocks reserved for this clist. */ char *c_cf; /* Pointer to the first cblock. */ char *c_cl; /* Pointer to the last cblock. */ }; /* * Per-tty structure. * * Should be split in two, into device and tty drivers. * Glue could be masks of what to echo and circular buffer * (low, high, timeout). */ struct tty { struct clist t_rawq; /* Device raw input queue. */ long t_rawcc; /* Raw input queue statistics. */ struct clist t_canq; /* Device canonical queue. */ long t_cancc; /* Canonical queue statistics. */ struct clist t_outq; /* Device output queue. */ long t_outcc; /* Output queue statistics. */ int t_line; /* Interface to device drivers. */ dev_t t_dev; /* Device. */ int t_state; /* Device and driver (TS*) state. */ int t_flags; /* Tty flags. */ int t_timeout; /* Timeout for ttywait() */ struct pgrp *t_pgrp; /* Foreground process group. */ struct session *t_session; /* Enclosing session. */ struct selinfo t_rsel; /* Tty read/oob select. */ struct selinfo t_wsel; /* Tty write select. */ struct termios t_termios; /* Termios state. */ struct winsize t_winsize; /* Window size. */ /* Start output. */ void (*t_oproc) __P((struct tty *)); /* Stop output. */ void (*t_stop) __P((struct tty *, int)); /* Set hardware state. */ int (*t_param) __P((struct tty *, struct termios *)); void *t_sc; /* XXX: net/if_sl.c:sl_softc. */ short t_column; /* Tty output column. */ short t_rocount, t_rocol; /* Tty. */ short t_hiwat; /* High water mark. */ short t_lowat; /* Low water mark. */ short t_gen; /* Generation number. */ }; #define t_cc t_termios.c_cc #define t_cflag t_termios.c_cflag #define t_iflag t_termios.c_iflag #define t_ispeed t_termios.c_ispeed #define t_lflag t_termios.c_lflag #define t_min t_termios.c_min #define t_oflag t_termios.c_oflag #define t_ospeed t_termios.c_ospeed #define t_time t_termios.c_time #define TTIPRI 25 /* Sleep priority for tty reads. */ #define TTOPRI 26 /* Sleep priority for tty writes. */ #define TTMASK 15 #define OBUFSIZ 100 #define TTYHOG 1024 #ifdef KERNEL #define TTMAXHIWAT roundup(2048, CBSIZE) #define TTMINHIWAT roundup(100, CBSIZE) #define TTMAXLOWAT 256 #define TTMINLOWAT 32 #endif /* These flags are kept in t_state. */ #define TS_ASLEEP 0x00001 /* Process waiting for tty. */ #define TS_ASYNC 0x00002 /* Tty in async I/O mode. */ #define TS_BUSY 0x00004 /* Draining output. */ #define TS_CARR_ON 0x00008 /* Carrier is present. */ #define TS_FLUSH 0x00010 /* Outq has been flushed during DMA. */ #define TS_ISOPEN 0x00020 /* Open has completed. */ #define TS_TBLOCK 0x00040 /* Further input blocked. */ #define TS_TIMEOUT 0x00080 /* Wait for output char processing. */ #define TS_TTSTOP 0x00100 /* Output paused. */ #define TS_WOPEN 0x00200 /* Open in progress. */ #define TS_XCLUDE 0x00400 /* Tty requires exclusivity. */ /* State for intra-line fancy editing work. */ #define TS_BKSL 0x00800 /* State for lowercase \ work. */ #define TS_CNTTB 0x01000 /* Counting tab width, ignore FLUSHO. */ #define TS_ERASE 0x02000 /* Within a \.../ for PRTRUB. */ #define TS_LNCH 0x04000 /* Next character is literal. */ #define TS_TYPEN 0x08000 /* Retyping suspended input (PENDIN). */ #define TS_LOCAL (TS_BKSL | TS_CNTTB | TS_ERASE | TS_LNCH | TS_TYPEN) /* * Snoop state,we need this as we have no other indication of * begin snoopped. */ #define TS_SNOOP 0x10000 /* There is snoop on device */ /* Character type information. */ #define ORDINARY 0 #define CONTROL 1 #define BACKSPACE 2 #define NEWLINE 3 #define TAB 4 #define VTAB 5 #define RETURN 6 struct speedtab { int sp_speed; /* Speed. */ int sp_code; /* Code. */ }; /* Modem control commands (driver). */ #define DMSET 0 #define DMBIS 1 #define DMBIC 2 #define DMGET 3 /* Flags on a character passed to ttyinput. */ #define TTY_CHARMASK 0x000000ff /* Character mask */ #define TTY_QUOTE 0x00000100 /* Character quoted */ #define TTY_ERRORMASK 0xff000000 /* Error mask */ #define TTY_FE 0x01000000 /* Framing error or BREAK condition */ #define TTY_PE 0x02000000 /* Parity error */ /* Is tp controlling terminal for p? */ #define isctty(p, tp) \ ((p)->p_session == (tp)->t_session && (p)->p_flag & P_CONTROLT) /* Is p in background of tp? */ #define isbackground(p, tp) \ (isctty((p), (tp)) && (p)->p_pgrp != (tp)->t_pgrp) #ifdef KERNEL extern struct ttychars ttydefaults; /* Symbolic sleep message strings. */ extern char ttyin[], ttyout[], ttopen[], ttclos[], ttybg[], ttybuf[]; int b_to_q __P((char *cp, int cc, struct clist *q)); void catq __P((struct clist *from, struct clist *to)); void clist_alloc_cblocks __P((struct clist *q, int ccmax, int ccres)); void clist_free_cblocks __P((struct clist *q)); /* void clist_init __P((void)); */ /* defined in systm.h for main() */ int getc __P((struct clist *q)); void ndflush __P((struct clist *q, int cc)); int ndqb __P((struct clist *q, int flag)); char *nextc __P((struct clist *q, char *cp, int *c)); int putc __P((int c, struct clist *q)); int q_to_b __P((struct clist *q, char *cp, int cc)); int unputc __P((struct clist *q)); int nullmodem __P((struct tty *tp, int flag)); int tputchar __P((int c, struct tty *tp)); int ttioctl __P((struct tty *tp, int com, void *data, int flag)); int ttread __P((struct tty *tp, struct uio *uio, int flag)); int ttnread __P((struct tty *)); void ttrstrt __P((void *tp)); int ttyselect __P((struct tty *tp, int rw, struct proc *p)); +int ttselect __P((dev_t dev, int rw, struct proc *p)); void ttsetwater __P((struct tty *tp)); int ttspeedtab __P((int speed, struct speedtab *table)); int ttstart __P((struct tty *tp)); void ttwakeup __P((struct tty *tp)); int ttwrite __P((struct tty *tp, struct uio *uio, int flag)); void ttychars __P((struct tty *tp)); int ttycheckoutq __P((struct tty *tp, int wait)); int ttyclose __P((struct tty *tp)); void ttyflush __P((struct tty *tp, int rw)); void ttyinfo __P((struct tty *tp)); int ttyinput __P((int c, struct tty *tp)); int ttylclose __P((struct tty *tp, int flag)); int ttymodem __P((struct tty *tp, int flag)); int ttyopen __P((dev_t device, struct tty *tp)); int ttyoutput __P((int c, struct tty *tp)); void ttypend __P((struct tty *tp)); void ttyretype __P((struct tty *tp)); void ttyrub __P((int c, struct tty *tp)); int ttysleep __P((struct tty *tp, void *chan, int pri, char *wmesg, int timeout)); int ttywait __P((struct tty *tp)); int ttywflush __P((struct tty *tp)); struct tty *ttymalloc __P((void)); void ttyfree __P((struct tty *)); #endif /* KERNEL */ #endif /* !_SYS_TTY_H_ */