/* * * X.29 option for dda driver for UNIX and Ultrix * ________________________________________________________ * / \ * | AAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | * | AAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | * | AAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | * | AAAA AAAA CCCC CCCC | * | AAAA AAAA CCCC CCCC | * | AAAA AAAA CCCC CCCC | * | AAAA AAAA CCCC CCCC | * | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC | * | AAAA AAAAAAAAAAA CCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCC | * | AAAA AAAAAAAAA CCCCCCCCCCCCCC CCCCCCCCCCCCCC | * \________________________________________________________/ * * Copyright (c) 1987 by Advanced Computer Communications * 720 Santa Barbara Street, Santa Barbara, California 93101 * (805) 963-9431 * * File: * if_x29.c * * Author: * * Project: * Development of PAD on 6250 software. * * Function: * To enable network connections on ACP_XX to communicate with UNIX. * * Components: * - files if_x29.c * * Configuration Entry: * * device dda0 at uba? csr 0166740 vector ddainta ddaintb * * Usage Notes: * * - make devices in /dev and edit /etc/ttys for those x29 * devices which you want in your configuration * * System Notes: * * Refer to the installation instructions, readme.txt, which * are included on the driver distribution medium. * * Revision History at end of file */ /* * For efficiency, it is a good idea to modify XXBOARDS when using * less than 4 boards with the X29 option. If using more than 32 * lines per board, you should modify XXBOARDS, XXLPERBRD, LOG2_XXBOARDS * and LOG2_XXLPERBRD. * * Minor numbers are laid out as follows (by default): * (MSB) PBBLLLLL (LSB) * Where P is a flag to determine if the line is outbound (pad) or * inbound (tty). BB is the board number (0-3), and LLLLL is the * X29 line on a board (0-31). Some customers may need more than * 32 lines/board. If there are less than 2 boards, one may shift * the break-point between lines and boards: * * up to 4 boards, 32 lines/board (default) * (MSB) PBBLLLLL (LSB) * XXBOARDS = 4, LOG2_XXBOARDS = 2 * XXLPERBRD = 32, LOG2_XXLPERBRD = 5 * up to 2 boards, 64 lines/board: * (MSB) PBLLLLLL (LSB) * XXBOARDS = 2, LOG2_XXBOARDS = 1 * XXLPERBRD = 64, LOG2_XXLPERBRD = 6 * only 1 board, 128 (actually, 126, as 126 = max svc): * (MSB) PLLLLLLL (LSB) * XXBOARDS = 1, LOG2_XXBOARDS = 0 * XXLPERBRD = 128, LOG2_XXLPERBRD = 7 * * (obviously, these are all powers of two) */ #define XXBOARDS 4 /* # boards running x29 */ #define LOG2_XXBOARDS 2 /* # bits of board info */ #define XXLPERBRD 32 /* # lines per board */ #define LOG2_XXLPERBRD 5 /* # bits of line info */ /* * If you require an 8-bit data path and have no parity misconfigurations, * you may change PARITY_MASKs to 0377. This will leave parity stripping * to the ttdriver. However, the ttdriver won't strip parity when in * raw mode (e.g. at the Password: prompt), so one symptom of a parity * misconfiguration is that users can't login (CR gets received as 0x8D). */ #define INPUT_PARITY_MASK 0177 /* strip off the 8th bit */ #define OUTPUT_PARITY_MASK 0377 /* don't strip off the 8th bit */ /* * macro to translate a device number to the unit (i.e. ACP_n250) * with which it is associated and the port on said unit */ #define UNIT(x) ((minor(x) >> LOG2_XXLPERBRD) & LOG2_XXBOARDS) #define LINE(x) (minor(x) & 0177) /* index into line table */ #define XXSHOW(x) (minor(x) == 255) /* special "show" device */ #define IS_PAD(x) (minor(x) & 0200) /* msb is the pad/tty selector */ #define MAJLINE(x) ((x) & ~0x80) /* major plus corrected minor # */ #define NXXLINES (XXBOARDS * XXLPERBRD) /* number of total x29 lines */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% LOCAL FUNCTIONS %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void xxcntl(); PRIVATE void xxclear(); PRIVATE void xxshow(); PRIVATE void xxpadhandle(); PRIVATE int xxpadparse(); PRIVATE int xxpadcall(); PRIVATE void xxpadmsg(); PRIVATE void xx_qbit_msg(); PRIVATE void xx_tp_hangup(); PRIVATE void x29_init(); PRIVATE void x29_dhandle(); PRIVATE int x29_break_reply_is_required(); #if ACC_ULTRIX >= 30 static int ttbreakc(); /* always keep this private */ #endif /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% LOCAL VARIABLES %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #define SET_PAD 2 #define READ_PAD 4 #define SET_READ_PAD 6 #define PAR_INDICATION 0 #define INVITE_CLEAR 1 #define BREAK_INDIC 3 #define PAD_ERROR 5 /* command codes */ #define XX_C_BREAK 001 #define XX_C_PAD 002 #define XX_C_CLOSE 003 #define XX_C_HOST 004 struct tty xx_tty[NXXLINES]; /* tty structures */ #define MODE_UNUSED 0 /* !just for sanity checks only! */ #define MODE_HOST 1 /* port in host mode (incoming) */ #define MODE_PAD 2 /* port in pad mode (outgoing) */ char xxmode[NXXLINES]; /* mode of port */ int xxstart(); typedef struct { char ref; char val; } x29_pad_pair; PRIVATE x29_pad_pair x29_break_ack_params[] = { 8, 0 /* ref 8 -- normal output to terminal */ }; PRIVATE x29_pad_pair x29_callout_params[] = { 1, 0 /* ref 1 -- no recall char */ }; PRIVATE x29_pad_pair x29_callin_setparams[] = { /* these are the preferred paramters when calling in to Unix */ 2, 0, /* ref 2 -- no echo */ 3, 127, /* ref 3 -- forward data on any char */ 8, 0, /* ref 8 -- normal data delivery to terminal */ 9, 0, /* ref 9 -- no padding after carriage return */ 10, 0, /* ref 10 -- no line folding */ 13, 0, /* ref 13 -- no line feed after CR */ 15, 0 /* ref 15 -- no local edit */ }; /****************************************************************************** * PAD CONTROL INFORMATION AND DEFINITIONS ******************************************************************************/ /* definitions for the pad state field p_state */ #define PS_IDLE 0 /* not opened state */ #define PS_COM 1 /* the pad for this line is in command state */ #define PS_PAD 2 /* this line has data passing though the pad */ #define PS_WAIT 3 /* waiting state */ #define PS_XFR 4 /* data transfer state */ #define P_LINELEN 20 #define P_NOBLOCK 0 typedef struct padinfo { short p_state; /* pad state */ char p_line[P_LINELEN]; /* built up line */ char p_idx; /* index into p_line */ int p_flow; /* index into mbuf when flow off, P_NOBLOCK if not flowed off */ struct mbuf *p_msav; /* place to hang mbuf when flow controlled */ struct mbuf *p_mchsav; /* place to save mbuf chain '' '' '' */ } padinfo; padinfo xx_padinfo[NXXLINES]; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% GLOBAL ROUTINES %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXOPEN() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Open a line. */ /* */ /* Call: xxopen(dev, flag) */ /* Argument: dev: device */ /* flag: indicates type of open, "nonblocking" */ /* "or block if in use" */ /* Returns: 0 for success, else nonzero error code */ /* Called by: kernel software software, this routine is in */ /* the cdevsw table */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*ARGSUSED*/ xxopen(dev, flag) dev_t dev; int flag; { register struct tty *tp; register d; register s; int unit, i; #if ACC_ULTRIX > 00 int inuse; /* store inuse bit while sleeping */ #endif unit = UNIT(dev); d = LINE(dev); if (XXSHOW(dev)) { /* minor device 255 */ xxshow(); return (EPIPE); } /* PST NOTE TO SELF: change the test as follows: * make this d >= NXXLINES, then check to see if unit is present, * Keep that sleep() in the thingy below, so we don't get bouncing * gettys eating up cpu time. */ if ((d >= NXXLINES)) return (ENXIO); /* wait for interface to come up */ while (dda_softc[unit].dda_state != S_LINK_UP) sleep(&dda_softc[unit].dda_state, TTIPRI); tp = &xx_tty[d]; if ((tp->t_state & TS_XCLUDE) && u.u_uid != 0) return EBUSY; /* make sure the port isn't already open in a conflicting manner */ /* i.e. can't open /dev/padJ0 and /dev/ttyJ0 at the same time */ if (tp->t_state & (TS_WOPEN | TS_ISOPEN)) { if ((IS_PAD(dev) && (xxmode[d] == MODE_HOST)) || ((!IS_PAD(dev)) && (xxmode[d] == MODE_PAD))) return EBUSY; } #ifdef DDADEBUG if (DDADBCH(96, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) open line %d flag %o in %s mode\n", unit, d, flag, (IS_PAD(dev) ? "pad" : "host") DDAELOG; } #endif DDADEBUG tp->t_oproc = xxstart; tp->t_state |= TS_WOPEN; /* if first open initialize state */ if ((tp->t_state & TS_ISOPEN) == 0) { ttychars(tp); #if ACC_ULTRIX >= 30 /* posix compliant tty driver */ if (tp->t_cflag & CBAUD == 0) { tp->t_iflag = IGNPAR | ICRNL | IXON | IXANY | IXOFF; tp->t_oflag = OPOST | ONLCR; tp->t_cflag = B9600 | CS8 | CREAD | HUPCL; tp->t_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL; tp->t_line = 0; } #else /* v7 tty driver */ if (tp->t_ispeed == 0) { tp->t_ispeed = B9600; tp->t_ospeed = B9600; tp->t_flags = CRMOD | ANYP; } #endif xxparam(dev); } if (IS_PAD(dev)) { tp->t_state |= TS_CARR_ON; xxmode[d] = MODE_PAD; xxcntl(tp, XX_C_PAD, unit); } else { if ((tp->t_state & TS_CARR_ON) == 0) { xxmode[d] = MODE_HOST; xxcntl(tp, XX_C_HOST, unit); tp->t_flags |= ECHO; #if ACC_ULTRIX < 31 /* on everything other than Ultrix 3.1 */ /* on close tell ACP_XX to drop line */ tp->t_state |= TS_HUPCLS; #endif } } /* if xxcntl did not get called (state had carrier off) or xxcntl's * search for a free lcn failed, then t_addr will be 0, so punt */ if (tp->t_addr == 0) { tp->t_pgrp = 0; tp->t_state = 0; xxmode[d] = MODE_UNUSED; return (EBUSY); } xx_padinfo[d].p_flow = P_NOBLOCK; s = splimp(); #if ACC_ULTRIX > 00 if (flag & O_NDELAY) { if (!IS_PAD(dev)) tp->t_state |= TS_ONDELAY; } else #endif while ((tp->t_state & TS_CARR_ON) == 0) { tp->t_state |= TS_WOPEN; #if ACC_ULTRIX > 00 inuse = tp->t_state & TS_INUSE; #endif sleep(&tp->t_rawq, TTIPRI); /* wakeup came from xxclear */ if ((tp->t_state & TS_WOPEN) == 0) { splx(s); return (EPIPE); } #if ACC_ULTRIX > 00 /* if port became "inuse" while we slept, return */ if ((flag & O_BLKINUSE) && (!inuse) && (tp->t_state & TS_INUSE)) { splx(s); return (EALREADY); } #endif } splx(s); i = ((*linesw[tp->t_line].l_open) (dev, tp)); if (tp->t_pgrp == 0) tp->t_pgrp = u.u_procp->p_pid; return (i); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXCLOSE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Close a line. */ /* */ /* Call: xxclose(dev, flag) */ /* Argument: dev: device */ /* flag: unused */ /* Returns: nothing */ /* Called by: kernel software, this routine is in the */ /* cdevsw table */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*ARGSUSED*/ xxclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { register struct tty *tp; register d; d = LINE(dev); tp = &xx_tty[d]; #ifdef DDADEBUG if (DDADBCH(97, UNIT(dev))) { DDALOG(LOG_DEBUG) "dda%d:(x29) closing line %d\n", UNIT(dev), d DDAELOG; } #endif DDADEBUG /* PST NOTE TO SELF: * Add the 629 driver code for timing out the close below, * because the line could be flowed off and it would hang * forever */ (*linesw[tp->t_line].l_close) (tp, flag); #if ACC_ULTRIX >= 31 if ((tp->t_cflag & HUPCL) || ((tp->t_state & TS_ISOPEN) == 0)) { #else if ((tp->t_state & TS_HUPCLS) || ((tp->t_state & TS_ISOPEN) == 0)) { #endif #ifdef DDADEBUG if (DDADBCH(97, UNIT(dev))) { DDALOG(LOG_DEBUG) "dda%d:(x29) close: tp->t_state = %x\n", UNIT(dev), tp->t_state DDAELOG; } #endif DDADEBUG if (tp->t_state & TS_CARR_ON) xxcntl(tp, XX_C_CLOSE, UNIT(dev)); tp->t_state &= ~TS_CARR_ON; xxmode[d] = MODE_UNUSED; } ttyclose(tp); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXREAD() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Read from a line. */ /* */ /* Call: xxread(dev, uio) */ /* Argument: dev: device */ /* uio: pointer to uio structure */ /* Returns: 0 for success, else nonzero error code */ /* Called by: kernel software, this routine is in */ /* the cdevsw table */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ xxread(dev, uio) dev_t dev; struct uio *uio; { register struct tty *tp; register int l, error; if (dda_softc[UNIT(dev)].dda_state != S_LINK_UP) return (ENXIO); l = LINE(dev); tp = &xx_tty[l]; error = (*linesw[tp->t_line].l_read)(tp, uio); if (xx_padinfo[l].p_flow != P_NOBLOCK) { /* currently blocked? */ if (tp->t_flags & (RAW | CBREAK)) { /* using raw q? */ if (tp->t_rawq.c_cc < TTYHOG / 8) { /* if rawq is low, then * it's time to unblock */ x29_dhandle(&dda_softc[UNIT(dev)], (struct dda_cb *) (tp->t_addr), 1); } /* else cooked mode, different test */ /* canonical q empty? then it's time to unblock */ } else if (tp->t_canq.c_cc == 0) { x29_dhandle(&dda_softc[UNIT(dev)], (struct dda_cb *) (tp->t_addr), 1); } } return (error); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXWRITE() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Write on a line. */ /* */ /* Call: xxwrite(dev, uio) */ /* Argument: dev: device */ /* uio: pointer to uio structure */ /* Returns: 0 for success, else nonzero error code */ /* Called by: kernel software software, this routine is in */ /* the cdevsw table */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ xxwrite(dev, uio) dev_t dev; struct uio *uio; { register struct tty *tp; if (dda_softc[UNIT(dev)].dda_state != S_LINK_UP) return (ENXIO); tp = &xx_tty[LINE(dev)]; return (*linesw[tp->t_line].l_write)(tp, uio); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXIOCTL() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Process ioctl request. */ /* */ /* Call: xxioctl(dev, cmd, data, flag) */ /* Argument: dev: device */ /* cmd: ioctl command */ /* data: pointer to data */ /* flag: ignored */ /* Returns: 0 for sucess, else nonzero error code */ /* Called by: kernel software software, this routine is in */ /* the cdevsw table */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #define TIOACCQBIT (int)(0x80800000|('t'<<8)|125) xxioctl(dev, cmd, data, flag) dev_t dev; caddr_t data; { register struct tty *tp; int error; tp = &xx_tty[LINE(dev)]; if (cmd == TIOACCQBIT) { #ifdef DDADEBUG if (DDADBCH(98, UNIT(dev))) { DDALOG(LOG_DEBUG) "dda%d:(x29) ioctl qbit msg: cmd=%x ACC=%x\n", UNIT(dev), cmd, TIOACCQBIT DDAELOG; } #endif DDADEBUG xx_qbit_msg(tp, UNIT(dev), data); return (0); } error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag); if (error >= 0) return (error); error = ttioctl(tp, cmd, data, flag); if (error >= 0) { if (cmd == TIOCSETP || cmd == TIOCSETN) xxparam(dev); return (error); } switch (cmd) { case TIOCREMOTE: if (xxmode[LINE(dev)] == 0) return (EBUSY); xxcntl(tp, XX_C_PAD, UNIT(dev)); break; case TIOCSBRK: xxcntl(tp, XX_C_BREAK, UNIT(dev)); break; case TIOCCBRK: case TIOCSDTR: case TIOCCDTR: break; default: return (ENOTTY); } return (0); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXPARAM() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Set parameters from open or stty. */ /* This routine is being left in as a dummy in case in the future */ /* there is a mechanism for the host to send information i.e. */ /* "hangup line" to the ACP _XX */ /* */ /* Call: xxparam(dev) */ /* Argument: dev: device */ /* Returns: none */ /* Called by: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*ARGSUSED*/ xxparam(dev) dev_t dev; { } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXSTART() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Start (restart) transmission on a given line. This is the */ /* start routine which is called from above by the tty driver and */ /* from below on a transmission complete interrupt for a given */ /* line. */ /* */ /* Call: xxstart(tp) */ /* Argument: tp: pointer to tty structure */ /* Returns: none */ /* Called by: tty driver */ /* xxreset() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ xxstart(tp) register struct tty *tp; { register struct dda_softc *ds; register int nch, cc, k; register struct dda_cb *dc; register char *cp, *p; struct ifqueue *oq; struct mbuf *m; padinfo *pp; int unit, line, s, j; extern int ttrstrt(); line = tp - xx_tty; unit = UNIT(line); dc = (struct dda_cb *) tp->t_addr; ds = &dda_softc[unit]; pp = &xx_padinfo[line]; s = splimp(); #ifdef DDADEBUG if (DDADBCH(99, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) xxstart: line %d t_state = %x\n", unit, line, tp->t_state DDAELOG; } #endif DDADEBUG /* If it's currently active, or delaying, no need to do anything. */ if ((tp->t_state & TS_CARR_ON) == 0) { tp->t_state &= ~(TS_TTSTOP | TS_BUSY); ttyflush(tp, FREAD | FWRITE); tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t) &tp->t_outq); goto out; } if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) goto out; /* wait for free */ if (dda_softc[unit].dda_state != S_LINK_UP) { ttyflush(tp, FREAD | FWRITE); DMESG(unit, 96, (DDALOG(LOG_ERR) "dda%d:(x29) xxstart: unit offline\n", unit DDAELOG) ); goto out; } /* If the writer was sleeping on output overflow, wake him when low tide * is reached. */ if (tp->t_outq.c_cc <= TTLOWAT(tp)) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t) &tp->t_outq); } if (tp->t_wsel) { selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); tp->t_wsel = 0; tp->t_state &= ~TS_WCOLL; } } /* restart transmission unless output queue is empty */ if (tp->t_outq.c_cc == 0) goto out; /* if this is an outbound pad line and it's in command mode */ if (pp->p_state == PS_COM) { xxpadhandle(ds, tp, pp); goto out; } /* Allocate an mbuf to stuff the chars into */ m = 0; MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) { DMESG(unit, 97, (DDALOG(LOG_ERR) "dda%d:(x29) xxstart: could not get mbuf\n", unit DDAELOG) ); goto out; } cp = mtod(m, char *); cc = 0; /* copy at most MLEN-1 chars out -- must save one byte for subfunc */ while ((cc < MLEN - 1) && (tp->t_outq.c_cc > 0)) { if (tp->t_flags & (RAW | LITOUT)) nch = ndqb(&tp->t_outq, 0); else { nch = ndqb(&tp->t_outq, 0200); if (nch == 0) { /* if first item was a delay */ (void) getc(&tp->t_outq); /* discard the character */ continue; } } if (nch > (MLEN - 1) - cc) nch = (MLEN - 1) - cc; /* If any characters were set up, start transmission; */ if (nch) { j = q_to_b(&tp->t_outq, cp, nch); #if OUTPUT_PARITY_MASK != 0377 /* strip all characters as desired */ for (p = cp, k = j; k; k--, p++) *p &= OUTPUT_PARITY_MASK; #endif #ifdef DDADEBUG if (DDADBCH(100, unit) && j != nch) { DDALOG(LOG_DEBUG) "dda%d:(x29) xxstart: asked for %d got %d chars\n", unit, nch, j DDAELOG; } #endif DDADEBUG cc += nch; cp += nch; } else break; } #ifdef DDADEBUG if (DDADBCH(101, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) xxstart: mbuf %x len %d\n", unit, m, m->m_len DDAELOG; } #endif /* if any data was stuffed into the mbuf then send it */ if (cc) { m->m_dat[MLEN - 1] = 0; /* subfunction: no Q-bit */ m->m_len = cc; oq = &(dc->dc_oq); /* point to output queue */ if (IF_QFULL(oq)) { /* if q full */ IF_DROP(oq); /* drop the data */ m_freem(m); ds->dda_if.if_collisions++; /* for netstat display */ splx(s); return (ENOBUFS); } IF_ENQUEUE(oq, m); /* otherwise queue it */ tp->t_state |= TS_BUSY; dda_start(ds, dc); /* and try to output */ } else m_freem(m); out: if (dc->dc_lcn != 0) /* something left in oq? */ dda_start(ds, dc); /* restart output */ splx(s); return (0); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXRESET() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* In response to UNIBUS reset, reset state and restart */ /* transmitters. */ /* */ /* Call: xxreset(uban) */ /* Argument: uban: UNIBUS adaptor number */ /* Returns: none */ /* Called by: kernel software in response to UNIBUS reset */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*ARGSUSED*/ xxreset(uban) int uban; { } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXSTOP() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Dummy stop routine. */ /* */ /* Call: xxstop(tp, flag) */ /* Argument: tp: pointer to tty structure */ /* flag: indicates */ /* Returns: none */ /* Called by: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*ARGSUSED*/ xxstop(tp, flag) struct tty *tp; int flag; { } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXSELECT() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Circumvent bug in our bastardized design which causes ttselect */ /* to fail. */ /* */ /* Call: xxselect(dev, rw) */ /* Argument: dev: device */ /* rw: read or write indicator */ /* Returns: 0 or 1 */ /* Called by: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ xxselect(dev, rw) dev_t dev; int rw; { #ifdef DDADEBUG int unit = UNIT(dev); if (DDADBCH(102, unit)) DDALOG(LOG_DEBUG) "dda%d:(x29) select()\n", unit DDAELOG; #endif DDADEBUG return (ttselect(MAJLINE(dev), rw)); } /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% %%*/ /*%% LOCAL FUNCTIONS %%*/ /*%% %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% X29_SUPR() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine processes received supervisor messages. */ /* Depending on the message type, the appropriate action is */ /* taken. */ /* */ /* Call: x29_supr(ds, p) */ /* Arguments: ds: pointer to dev control block struct */ /* p: pointer to a character array */ /* containing the supervisor message */ /* Returns: nothing */ /* Called by: dda_supr() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void x29_supr(ds, p) struct dda_softc *ds; u_char p[]; { register struct dda_cb *dc; register struct tty *tp; register int lcn; int maxlcn; int line; #ifdef DDADEBUG if (DDADBCH(103, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_supr()\n", ds->dda_if.if_unit DDAELOG; } #endif DDADEBUG switch (p[0]) { case LINE_STATUS: /* link status msg */ case RESTART: /* restart received */ case RSTRT_ACK: /* restart ack */ case STATRESP: /* Statistics Response from FEP */ DMESG(ds->dda_if.if_unit, 98, (DDALOG(LOG_ERR) "dda%d:(x29) x29_supr: unexpected message type\n", ds->dda_if.if_unit DDAELOG)); break; case ANSWER: /* call answered */ lcn = p[1] / 2; dc = &(ds->dda_cb[lcn]); if (dc->dc_state == LC_CALL_PENDING) { /* if a call pending */ decode_answer(p, dc); dc->dc_state = LC_DATA_IDLE; /* set state */ dc->dc_flags = DC_X29; line = dc->dc_line; /* which line are we? */ #ifdef DDADEBUG if (DDADBCH(114, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_supr: answer: line=%d\n", ds->dda_if.if_unit, line DDAELOG; } #endif DDADEBUG if (line == -1) { /* fubar! */ DMESG(ds->dda_if.if_unit, 107, (DDALOG(LOG_ERR) "dda%d:(x29) x29_supr: answer: line was -1, VC 0x%x\n", ds->dda_if.if_unit, p[1] DDAELOG)); } xx_padinfo[line].p_state = PS_PAD; xxstart(&xx_tty[line]); } else { DMESG(ds->dda_if.if_unit, 108, (DDALOG(LOG_ERR) "dda%d:(x29) x29_supr: unexpected answer on LCN %d\n", ds->dda_if.if_unit, lcn DDAELOG)); } if (LOG_CALLS) { DDALOG(LOG_INFO) "dda%d:(x29) LCN %d: connected\n", ds->dda_if.if_unit, lcn DDAELOG; } break; case RING: /* incoming call */ if (decode_ring(p)) { /* find a free lcn associated with a XX_HOST open */ dc = &ds->dda_cb[1]; maxlcn = nddach[ds->dda_if.if_unit]; for (lcn = 1; lcn <= maxlcn; lcn++) { if (dc->dc_state == LC_IDLE && dc->dc_flags & DC_X29W) break; dc++; } if (lcn > maxlcn) { /* if no free lcn's */ if (LOG_BUSY) { DDALOG(LOG_ERR) "dda%d:(x29) no free X29W lcns, call rejected, vc=0x%x\n", ds->dda_if.if_unit, p[1] DDAELOG; } send_supr(ds, CLEARVC, p[2], 0); /* clear call */ break; /* exit case */ } /* got a good lcn, now use it */ #ifdef DDADEBUG if (DDADBCH(103, ds->dda_if.if_unit)) { DDALOG(LOG_ERR) "dda%d:(x29) supr_msg: call from 0x%0x\n", ds->dda_if.if_unit, (u_long) dc->dc_inaddr.s_addr DDAELOG; } #endif DDADEBUG dc->dc_state = LC_DATA_IDLE; /* set state */ dc->dc_pktsizein = 0; dc->dc_pktsizeout = 0; dc->dc_wsizein = 0; dc->dc_wsizeout = 0; dc->dc_flags = DC_X29; send_supr(ds, ANSWER, lcn * 2, p[2]); /* send answer */ if (LOG_CALLS) { DDALOG(LOG_INFO) "dda%d:(x29) Call accepted LCN %d\n", ds->dda_if.if_unit, dc->dc_lcn DDAELOG; } line = dc->dc_line; #ifdef DDADEBUG if (DDADBCH(114, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_supr: ring: line=%d\n", ds->dda_if.if_unit, line DDAELOG; } #endif DDADEBUG if (line == -1) { /* fubar! */ DMESG(ds->dda_if.if_unit, 107, (DDALOG(LOG_ERR) "dda%d:(x29) x29_supr: ring: line was -1, VC 0x%x\n", ds->dda_if.if_unit, p[1] DDAELOG)); break; } tp = &xx_tty[line]; xx_padinfo[line].p_state = PS_XFR; wakeup((caddr_t) &tp->t_rawq); tp->t_state |= TS_CARR_ON; #if ACC_ULTRIX > 00 tp->t_state &= ~TS_ONDELAY; #endif /* I would prefer to wait a bit before sending this */ send_x29_param_msg(ds, dc, SET_PAD, x29_callin_setparams, sizeof(x29_callin_setparams)); } else { /* bad decode */ send_supr(ds, CLEARVC, p[2], 0); /* clear call */ DMESG(ds->dda_if.if_unit, 100, (DDALOG(LOG_ERR) "dda%d:(x29) Bad decode, call REJECTED VC 0x%x\n", ds->dda_if.if_unit, p[1] DDAELOG)); } break; case CLEARLC: /* clear by LCN */ lcn = p[1] / 2; /* get LCN */ dc = &(ds->dda_cb[lcn]); if (dc->dc_state != LC_CLR_PENDING) { /* if no clear pending */ send_supr(ds, CLEARLC, p[1], 0); /* ack the clear */ } if (dc->dc_state == LC_CALL_PENDING) /* call is cleared */ DMESG(ds->dda_if.if_unit, 101, (DDALOG(LOG_ERR) "dda%d:(x29) Call cleared LCN %d (%x %x)\n", ds->dda_if.if_unit, dc->dc_lcn, p[2], p[4] DDAELOG)); hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_IDLE); dc->dc_state = LC_IDLE; dc->dc_timer = TMO_OFF; /* stop timer */ dc->dc_wsizein = dc->dc_wsizeout = 0; dc->dc_pktsizein = dc->dc_pktsizeout = 0; abort_io(ds->dda_if.if_unit, lcn); xx_tp_hangup(ds, dc); /* will clear flags */ break; case CLEARVC: /* clear by VCN */ send_supr(ds, CLEARVC, p[1], 0); /* send clear ack */ if (LOG_CALLS) { DDALOG(LOG_INFO) "dda%d:(x29) Network cleared VC %x (%x %x)\n", ds->dda_if.if_unit, p[1], p[2], p[4] DDAELOG; } break; case RESET: /* X25 reset */ send_supr(ds, RESET_ACK, p[1], 0); /* send reset ack */ abort_io(ds->dda_if.if_unit, (int) p[1] / 2); DMESG(ds->dda_if.if_unit, 102, (DDALOG(LOG_ERR) "dda%d:(x29) X25 RESET on LCN %d (%x %x)\n", ds->dda_if.if_unit, p[1] / 2, p[2], p[4] DDAELOG)); break; case INTERRUPT: /* X25 interrupt */ #ifdef INDICATE_BREAK_ON_INTERRUPT lcn = p[1] / 2; dc = &(ds->dda_cb[lcn]); line = dc->dc_line; if (line == -1) { /* fubar! */ DMESG(ds->dda_if.if_unit, 107, (DDALOG(LOG_ERR) "dda%d:(x29) x29_supr: break: line was -1, VC 0x%x\n", ds->dda_if.if_unit, p[1] DDAELOG)); break; } tp = &xx_tty[line]; if (tp->t_flags & RAW) c = 0; else #if ACC_ULTRIX >= 30 c = tp->c_cc[VINTR];/* else make it the interrupt */ #else c = tp->t_intrc; /* else make it the interrupt */ #endif #if NBK > 0 if (tp->t_line == NETLDISC) { BKINPUT(c, tp); } else #endif (*linesw[tp->t_line].l_rint) (c, tp); /* send_supr (ds, INTR_ACK, p[1], 0); not needed -- done by FE */ #endif break; case INTR_ACK: /* quietly drop the acknowledgement */ break; default: DMESG(ds->dda_if.if_unit, 104, (DDALOG(LOG_ERR) "dda%d:(x29) supervisor error (%x %x %x %x)\n", ds->dda_if.if_unit, p[0], p[1], p[2], p[3] DDAELOG)); } } /* hangup any attached processes */ PRIVATE void xx_tp_hangup(ds, dc) struct dda_softc *ds; register struct dda_cb *dc; { register struct tty *tp; register padinfo *pp; register int line; line = dc->dc_line; if (line == -1) { /* fubar! */ DMESG(ds->dda_if.if_unit, 107, (DDALOG(LOG_ERR) "dda%d:(x29) xx_tp_hangup: line was -1\n", ds->dda_if.if_unit DDAELOG)); return; } tp = &xx_tty[line]; pp = &xx_padinfo[line]; if (pp->p_flow != P_NOBLOCK) { /* currently blocked? */ register struct hdx_chan *hc; hc = (struct hdx_chan *) & dc->dc_rchan; dda_rrq(ds, hc); /* make sure we hang a read */ } pp->p_flow = P_NOBLOCK; tp->t_state &= ~(TS_CARR_ON | TS_ASLEEP | TS_BUSY); ttyflush(tp, FREAD | FWRITE); gsignal(tp->t_pgrp, SIGHUP); gsignal(tp->t_pgrp, SIGCONT); tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t) &tp->t_outq); xxmode[line] = MODE_UNUSED; tp->t_addr = (caddr_t) NULL; pp->p_state = PS_IDLE; if (pp->p_mchsav) { m_freem(pp->p_mchsav); pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL; } dc->dc_flags &= ~(DC_X29 | DC_X29W); /* release to others */ } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% X29_DATA() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* This routine is called when a data channel I/O completes. */ /* If the completion was for a write, an attempt is made to */ /* start output on the next packet waiting for output on that */ /* LCN. If the completion was for a read, the received packet */ /* is sent to the IP input queue (if no error) and another read */ /* is started on the LCN. */ /* */ /* Call: x29_data(ds, hc, cc, cnt) */ /* Argument: ds: device control block */ /* hc: half duplex channel control block */ /* cc: Mailbox I/O completion status */ /* cnt: byte count */ /* Returns: nothing */ /* Called by: ddainta() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #define QBIT 0x80 PRIVATE void x29_data(ds, hc, cc, cnt, subcc) register struct dda_softc *ds; register struct hdx_chan *hc; int cc, cnt, subcc; { register struct dda_cb *dc = &(ds->dda_cb[hc->hc_chan / 2]); register struct tty *tp; #ifdef DDADEBUG if (DDADBCH(104, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_data: chan=%x cc=%x cnt=%x subcc=%x\n", ds->dda_if.if_unit, hc->hc_chan, cc, cnt, subcc DDAELOG; } #endif DDADEBUG if (hc->hc_chan & 0x01) { /* if write, fire up next output */ #ifdef DDADEBUG dc->dc_out_t = TMO_OFF; /* turn off output completion timer */ #endif if ((hc->hc_func != DDAABT) && (hc->hc_curr = hc->hc_curr->m_next)) dda_wrq(ds, hc, 0); else { /* it is abort | no more data left */ char qbit_indicator; qbit_indicator = hc->hc_mbuf->m_dat[MLEN - 1]; m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; if (hc->hc_func == DDAABT) { hc->hc_func &= ~DDAABT; hc->hc_inv &= ~INVALID_MBUF; } else ds->dda_if.if_opackets++; dc->dc_flags &= ~DC_OBUSY; if (qbit_indicator == QBIT) { /* Q-bit packet? */ dda_start(ds, dc); /* restart output */ } else { tp = &xx_tty[dc->dc_line]; tp->t_state &= ~TS_BUSY; xxstart(tp); /* restart tty output */ } } /* it's a packet coming in from the front end to the host */ } else { #ifdef DDADEBUG dc->dc_flags &= ~DC_IPEND; #endif hc = &dc->dc_rchan; #ifdef DDADEBUG if (DDADBCH(105, ds->dda_if.if_unit)) { u_char *p; DDALOG(LOG_DEBUG) "dda%d:(x29) ", ds->dda_if.if_unit DDAELOG; p = mtod(hc->hc_curr, u_char *); prt_bytes(ds->dda_if.if_unit, "received data", p, (cnt < 64 ? cnt : 64)); } if (DDADBCH(106, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_data: read complete mbuf=%x curr=%x\n", ds->dda_if.if_unit, hc->hc_mbuf, hc->hc_curr DDAELOG; } #endif DDADEBUG if (dc->dc_state != LC_DATA_IDLE) { m_freem(hc->hc_mbuf); /* toss the packet, lcn is dead */ hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } else if (cc == DDAIOCOK || (cc == DDAIOCOKP && !(subcc & QBIT))) { /* Queue up I/O completion OK transfers and I/O OK with more data * pending transfers (as long as it's not a Qbit message). * This algorythm operates differently than the IP handler due * to the fact that we don't need to wait for the entire X.25 * packet to arrive on the host before we assemble it. To do * so should be OK, but unfortunately it seems some brain-dead * PAD's generate packets with the M-bit set if they have more * data in their internal buffers. This can cause the system * to burn up mbufs waiting for us to finally receive a packet * with the M-bit not set. However, we should hold up on processing * packets with both the Q-bit and the M-bit set until we receive * the entire Q-bit message. If we get 30k Q-bit packets, we will * die, but that is obscenely absurd in the first place. * (sigh) -- pst 7-19-89 */ #ifdef DDADEBUG if (DDADBCH(107, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_data: chan=%x DDAIOCOK\n", ds->dda_if.if_unit, hc->hc_chan DDAELOG; } #endif DDADEBUG hc->hc_curr->m_len += cnt; /* update byte count */ ds->dda_if.if_ipackets++; /* HANDLE THE DATA HERE */ if (subcc & QBIT) { int len; char *mcp; mcp = mtod(hc->hc_curr, char *); len = hc->hc_curr->m_len; #ifdef DDADEBUG if (DDADBCH(108, ds->dda_if.if_unit)) prt_bytes(ds->dda_if.if_unit, "(x29) Qbit:", mcp, (len < 64 ? len : 64)); #endif DDADEBUG if (*mcp == BREAK_INDIC) { /* Break indication? */ register struct tty *tp; if (x29_break_reply_is_required(mcp, len)) { /* tell pad to stop discarding output */ send_x29_param_msg(ds, dc, SET_PAD, x29_break_ack_params, 2); } hc->hc_curr->m_len = 1; /* change data to single byte */ tp = &xx_tty[dc->dc_line]; if (tp->t_flags & RAW) /* if port is in raw mode, */ *mcp = 0; /* make the byte a null */ else #if ACC_ULTRIX >= 30 *mcp = tp->t_cc[VINTR]; /* else make it the interrupt */ #else *mcp = tp->t_intrc; /* else make it the interrupt */ #endif x29_dhandle(ds, dc, 0); return; } else if (*mcp & READ_PAD) { if (len == 1) /* just a message, no params? */ send_x29_param_msg(ds, dc, PAR_INDICATION, x29_callout_params, sizeof(x29_callout_params)); else send_x29_param_msg(ds, dc, PAR_INDICATION, mcp + 1, len - 1); m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } else { m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } } else { /* not Qbit data, process normally */ x29_dhandle(ds, dc, 0); return; } } else if (cc == DDAIOCOKP) { /* good completion, more data pending */ hc->hc_curr->m_len += cnt; } else { /* toss packet */ m_freem(hc->hc_mbuf); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0; } /* hang a new data read */ #ifdef DDADEBUG dc->dc_flags |= DC_IPEND; #endif dda_rrq(ds, hc); } } /* this routine copies chars from the dc_rchan mbuf to the upper * level software. If all the characters are read then the mbuf is * freed and a new read is hung on the channel. * * This routine is called from below by the int A handler and from above * by the device read routine. */ PRIVATE void x29_dhandle(ds, dc, restart) register struct dda_softc *ds; register struct dda_cb *dc; int restart; { register struct tty *tp; register struct hdx_chan *hc; register padinfo *pp; u_char *cp, c; struct mbuf *m2, *m; int s, line; register int j; static int recurse = 0; s = splimp(); if (recurse) { /* don't allow ourselves to be called recursively */ splx(s); return; } else recurse = 1; hc = (struct hdx_chan *) &dc->dc_rchan; line = dc->dc_line; tp = &xx_tty[line]; pp = &xx_padinfo[line]; if (restart) { /* trying to restart input? */ j = pp->p_flow; m = pp->p_mchsav; m2 = pp->p_msav; #ifdef DDADEBUG if (DDADBCH(109, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) flow restart [%d] in %x\n", ds->dda_if.if_unit, j, m DDAELOG; } #endif DDADEBUG } else { j = P_NOBLOCK; m2 = m = hc->hc_mbuf; /* que mbuf chain */ } if (m == 0) { DMESG(ds->dda_if.if_unit, 105, (DDALOG(LOG_ERR) "dda%d:(x29) x29_dhandle: null mbuf\n", ds->dda_if.if_unit DDAELOG)); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) NULL; dda_rrq(ds, hc); goto out; } while (m2) { cp = mtod(m2, u_char *); for (; j < m2->m_len; j++) { c = cp[j] & INPUT_PARITY_MASK; if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= TTYHOG - 2) if (!ttbreakc(c, tp)) continue; /* dump the character */ #if NBK > 0 if (tp->t_line == NETLDISC) { BKINPUT(c, tp); } else #endif (*linesw[tp->t_line].l_rint) (c, tp); /* Block further input iff: Current input > threshold AND input * is available to user program */ if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG / 4 && ((tp->t_flags & (RAW | CBREAK)) || (tp->t_canq.c_cc > 0))) { #ifdef DDADEBUG if (DDADBCH(109, ds->dda_if.if_unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) flow on [%d] in %x of %d\n", ds->dda_if.if_unit, j, m2, m2->m_len DDAELOG; } #endif DDADEBUG pp->p_flow = j + 1; pp->p_msav = m2; pp->p_mchsav = m; if (restart == 0) hc->hc_mbuf = hc->hc_curr = (struct mbuf *) NULL; goto out; } } m2 = m2->m_next; j = P_NOBLOCK; } if (restart) pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL; m_freem(m); hc->hc_mbuf = hc->hc_curr = (struct mbuf *) NULL; pp->p_flow = P_NOBLOCK; #ifdef DDADEBUG dc->dc_flags |= DC_IPEND; #endif dda_rrq(ds, hc); out: recurse = 0; splx(s); } PRIVATE void xx_qbit_msg(tp, unit, msg) register struct tty *tp; int unit; char *msg; { register struct dda_cb *dc; register struct dda_softc *ds; int s; ds = &dda_softc[unit]; dc = (struct dda_cb *) tp->t_addr; s = splimp(); #ifdef DDADEBUG if (DDADBCH(110, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) xx_qbit_msg: %d %d %d\n", unit, msg[0], msg[1], msg[2] DDAELOG; } #endif DDADEBUG if (msg[1] < (MLEN - 4)) send_x29_param_msg(ds, dc, msg[0], msg + 2, msg[1]); splx(s); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXCNTL() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Do modem control functions on a line. */ /* */ /* Call: xxcntl(tp, c, d) */ /* Argument: tp: pointer to tty structure */ /* c: function code */ /* unit: for unit number */ /* Returns: none */ /* Called by: xxopen() */ /* xxclose() */ /* xxread() */ /* xxint() */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void xxcntl(tp, c, unit) register struct tty *tp; int c, unit; { register struct dda_cb *dc; register struct dda_softc *ds; register padinfo *pp; int s, l; l = tp - xx_tty; ds = &dda_softc[unit]; pp = &xx_padinfo[l]; s = splimp(); #ifdef DDADEBUG if (DDADBCH(111, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) xxcntl: tp=0x%x line=%d\n", unit, tp, l DDAELOG; } #endif DDADEBUG switch (c) { case XX_C_PAD: if (tp->t_addr) break; if (dc = find_free_lcn(ds)) { /* race against locate_x25_lcn */ dc->dc_flags = DC_X29; dc->dc_line = l; pp->p_state = PS_COM; tp->t_addr = (caddr_t) dc; tp->t_flags &= ~ECHO; pp->p_flow = P_NOBLOCK; pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL; pp->p_idx = 0; pp->p_line[0] = '\0'; } else tp->t_addr = (caddr_t) NULL; break; case XX_C_HOST: if (tp->t_addr) break; if (dc = find_free_lcn(ds)) { /* race against locate_x25_lcn */ dc->dc_flags = DC_X29W; dc->dc_line = l; pp->p_state = PS_WAIT; pp->p_flow = P_NOBLOCK; pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL; tp->t_addr = (caddr_t) dc; } else tp->t_addr = (caddr_t) NULL; break; case XX_C_CLOSE: pp->p_state = PS_IDLE; if (pp->p_mchsav) { m_freem(pp->p_mchsav); pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL; } dc = (struct dda_cb *) tp->t_addr; if (dc == 0) break; if (pp->p_flow != P_NOBLOCK) { /* currently blocked? */ register struct hdx_chan *hc; hc = (struct hdx_chan *) &dc->dc_rchan; dda_rrq(ds, hc); /* make sure we hang a read */ } #ifdef DDADEBUG if (DDADBCH(111, unit)) { static char *st[] = { "lcn down", "lcn restart", "idle", "call pending", "data idle", "clear pending" }; DDALOG(LOG_DEBUG) "dda%d:(x29) xxcntl: close state: %s\n", unit, st[dc->dc_state] DDAELOG; } #endif DDADEBUG if (dc->dc_state == LC_DATA_IDLE || dc->dc_state == LC_CALL_PENDING) clear_lcn(ds, dc); /* send clear & set state to clr_pending */ /* timers will convert it to LC_IDLE later */ #ifdef DDADEBUG else if (DDADBCH(111, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) xxcntl: warning: state not data_idle\n", unit DDAELOG; } #endif dc->dc_flags &= ~(DC_X29 | DC_X29W); /* release to others */ tp->t_addr = (caddr_t) NULL; break; case XX_C_BREAK: /* really should look at X.3 parameters to decide if an interrupt * packet should be sent. instead, we take an action which assumes * PAD parameter 7 has value 21 */ dc = (struct dda_cb *) tp->t_addr; send_supr(ds, INTERRUPT, dc->dc_lcn * 2, 0); send_x29_param_msg(ds, dc, BREAK_INDIC, 0, 0); break; } splx(s); } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% X29_INIT() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Software reset, clear lines. */ /* */ /* Call: x29_init(unit, active); */ /* Argument: unit: ACP _XX device */ /* Returns: none */ /* Called by: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void x29_init(unit, active) int unit, active; { register int i; register padinfo *pp; #ifdef DDADEBUG if (DDADBCH(113, unit)) { DDALOG(LOG_DEBUG) "dda%d:(x29) x29_init() active=%d\n", unit, active DDAELOG; } #endif DDADEBUG if (active) xxclear(unit); else { for (i = 0; i < XXLPERBRD; i++) { xx_tty[unit * XXLPERBRD + i].t_state = PS_IDLE; pp = &xx_padinfo[unit * XXLPERBRD + i]; pp->p_state = PS_IDLE; pp->p_flow = P_NOBLOCK; pp->p_msav = pp ->p_mchsav = (struct mbuf *) NULL; } } } PRIVATE void xxclear(unit) int unit; { register struct tty *tp; register struct dda_softc *ds; register struct dda_cb *dc; int i, state; ds = &dda_softc[unit]; for (i = 0, tp = &xx_tty[unit * XXLPERBRD]; i < XXLPERBRD; i++, tp++) { state = tp->t_state; #ifdef DDADEBUG if (DDADBCH(112, unit) && state) { DDALOG(LOG_DEBUG) "dda%d:(x29) xxclear: line=%d pgrp=%d state=%d\n", unit, i, tp->t_pgrp, state DDAELOG; } #endif DDADEBUG if (state & TS_WOPEN) { tp->t_state &= ~TS_WOPEN; wakeup(&tp->t_rawq); } if (tp->t_state) { dc = (struct dda_cb *) tp->t_addr; if (dc) { xx_tp_hangup(ds, dc); dc->dc_line = -1; /* break correspondence */ } } } } /*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /*%% XXSHOW() %%*/ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* */ /* Purpose: */ /* */ /* Show status of each active unit */ /* */ /* Call: xxshow() */ /* Argument: none */ /* Returns: none */ /* Called by: none */ /* */ /*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ PRIVATE void xxshow() { register struct tty *tp; register padinfo *pp; int unit, i; static char *st[] = { "idle", " com", " pad", "wait", "xfer" }; for (unit = 0; unit < (NDDA < XXBOARDS ? NDDA : XXBOARDS); unit++) { uprintf("\nACP5250/6250 X29 driver: state of unit %d -\n", unit); uprintf("line\tstate\tlcn\tflow\ttstate\ttflags\n"); for (i = 0, tp = &xx_tty[unit * XXLPERBRD]; i < XXLPERBRD; i++, tp++) { if (tp->t_state) { pp = &xx_padinfo[i]; uprintf("%d:\t%s\t%d\t%d\t%x\t%x\n", i, st[pp->p_state], (struct dda_cb *) (tp->t_addr) - dda_softc[unit].dda_cb, pp->p_flow, tp->t_state, tp->t_flags); } } } uprintf("remaining lines free\n"); } /****************************************************************************** * PAD CODE ******************************************************************************/ /* PADCHARUP - Pass a character up towards the user */ #define PADCHARUP(c,tp) (*linesw[(tp)->t_line].l_rint) ((c), (tp)) PRIVATE void xxpadhandle(ds, tp, pi) struct dda_softc *ds; struct tty *tp; /* pointer to relevant tty structure */ padinfo *pi; /* pointer to relevant padinfo structure */ { register int i; register char c; register struct dda_cb *dc; int nch; char tbuf[CBSIZE]; /* CBSIZE is number of char in a * cblock */ nch = q_to_b(&tp->t_outq, tbuf, CBSIZE); /* handle characters in command state. Its OK if were slow here because * there is a person on the other end of the discussion */ dc = (struct dda_cb *) tp->t_addr; for (i = 0; i < nch; i++) { if (pi->p_idx >= P_LINELEN) { xxpadmsg("\r\ncommand too long\r\n@", tp); pi->p_idx = 0; return; } c = pi->p_line[pi->p_idx] = tbuf[i] & INPUT_PARITY_MASK; if (c == '\r' || c == '\n') { PADCHARUP('\r', tp); PADCHARUP('\n', tp); pi->p_line[pi->p_idx] = '\0'; if (dc && dc->dc_state != LC_IDLE) { xxpadmsg("cannot call, line is in transition\r\n", tp); if (dc && dc->dc_state == LC_CALL_PENDING) xxpadmsg("previous call still pending\r\n", tp); } else if (xxpadparse(ds, pi, tp) == 0) PADCHARUP('@', tp); pi->p_idx = 0; } else if (c == '\b' || c == '\177') { if (pi->p_idx) { pi->p_idx--; xxpadmsg("\b \b", tp); } } else { pi->p_idx++; PADCHARUP(c, tp); } } } PRIVATE int xxpadparse(ds, pi, tp) struct dda_softc *ds; padinfo *pi; struct tty *tp; { char *p = pi->p_line; if (*p == 'c' || *p == 'C') { /* connect command */ for (p++; *p == ' '; *p++); if (*p < '0' || *p > '9') xxpadmsg("???\r\n", tp); else /* place a call */ return xxpadcall(ds, p, tp); } else if (*p) xxpadmsg("invalid command\r\n", tp); return 0; } PRIVATE int xxpadcall(ds, addr, tp) struct dda_softc *ds; char *addr; struct tty *tp; { register int i = 0; struct in_addr in; while (addr[i]) { if (addr[i] < '0' || addr[i] > '9') { xxpadmsg("invalid address\r\n", tp); return 0; } i++; } ddacb_called_addr[0] = i; bcopy(addr, ddacb_called_addr + 1, i); ddacb_user_data[0] = (u_char) 0; /* no user data for now */ in.s_addr = 0; return make_x25_call(ds, (struct dda_cb *) tp->t_addr, in, X25_PROTO_X29); } PRIVATE void xxpadmsg(s, tp) char *s; struct tty *tp; { while (*s) { PADCHARUP(*s, tp); s++; } } /* * This routine is used to respond to * READ_PARAMS and SET_READ_PARAMS requests, and also * to send out a SET_PARAMS request for incoming calls. * The outgoing pad supports NO parameters. */ send_x29_param_msg(ds, dc, type, msg, len) register struct dda_cb *dc; register struct dda_softc *ds; x29_pad_pair *msg; { struct mbuf *m; u_char *p; short i; register struct ifqueue *oq; m = 0; /* Allocate an mbuf to stuff the chars into */ MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) { DMESG(ds->dda_if.if_unit, 106, (DDALOG(LOG_ERR) "dda%d:(x29) couldn't get mbuf for QBIT message\n", ds->dda_if.if_unit DDAELOG)); return; } m->m_dat[MLEN - 1] = QBIT; /* set Q-bit */ p = mtod(m, u_char *); len = len / 2; *p++ = type; if (type == PAR_INDICATION) { /* our pad supports NO parameters */ for (i = 0; i < len; i++) { *p++ = msg[i].ref | 0x80; /* set invalid bit */ *p++ = 1; /* not implemented */ } } else { /* BREAK_INDIC, SET_PAD to ack break */ for (i = 0; i < len; i++) { *p++ = msg[i].ref; *p++ = msg[i].val; } } m->m_len = 1 + 2 * len; oq = &(dc->dc_oq); /* point to output queue */ if (IF_QFULL(oq)) { /* if q full */ IF_DROP(oq); /* drop the data */ m_freem(m); ds->dda_if.if_collisions++; /* for netstat display */ } else { IF_ENQUEUE(oq, m); /* otherwise queue it */ dda_start(ds, dc); /* and try to output */ } } PRIVATE int x29_break_reply_is_required(mcp, len) char *mcp; int len; { mcp++; /* skip over break indication msg */ while (len > 1) { /* while there are parameters left, */ if ((*mcp == 8) && (mcp[1] == 1)) /* paramter 8 set to 1? */ return 1; /* yes */ mcp += 2; len -= 2; } return 0; } /* * Ultrix 3.0 removed the old ttbreakc() kernel routine when moving to * a posix compliant driver. Here it is again, (for our local use only!!!) * */ #if ACC_ULTRIX >= 30 static int ttbreakc(c, tp) register c; register struct tty *tp; { return (c == tp->t_cc[VEOL] || c == tp->t_cc[VEOF] || c == tp->t_cc[VEOL2] || c == '\r' && (tp->t_flags & CRMOD)); } #endif /* Revision History: 09-Jun-1988: Unknown (Brad?) Initial implementation. 15-Feb-1989: Paul Traina Fixed point bug in send_x29_prm_msg 08-Mar-1989: Steve Johnson Fixed bug in xx_flow logic 24-May-1989: Paul Traina Upgraded for Ultrix 3.0 28-May-1989: Paul Traina Added more driver intelligence to disable pad durring call pending 31-May-1989: Paul Traina Added flexible mapping for # of boards per unit 04-Jun-1989: Paul Traina Fixed driver to dequeue Q-bit X29 packets from the mbuf chain properly. 19-Jun-1989: Paul Traina Fixed previous fix-- will need to go over if-elseif logic more carefully to make sure we're doing the right thing. It should be recoded. Modernized entire debug code suite, changed xxshow functionality to use the uprintf() kernel call to display data on user's terminal for the xxshow hack. 12-Jul-1989: Paul Traina Changed format of some debug messages. Removed LOCAL_VOID in favor of PRIVATE routine to aid in debugging. Simplified some chunky logic. 18-Jul-1989: Paul Traina Flipped search order for finding a free X29W lcn at RING time. Moved the dc_key.ttyline field out of the union and made it dc_line. This fixed the Dartmouth singleuser bug. 19-Jul-1989: Paul Traina Changed the packet decode logic in x29_data to immediately process packets with more data pending (i.e. the M-bit) right away, instead of queuing them up. (Note: it still queues up Q-bit packets) This may fix the Dartmouth mbuf problem with blasting uploads. 27-Jul-1989: Paul Traina Removed 8-bit strip in x29_dhandle. 01-Aug-1989: Paul Traina Added additional two parameters to make_x25_call for userdata/length for merge with new pad software. 02-Aug-1989: Paul Traina Reinserted 8-bit strip on data received from the net. (uses PARITY_MASK define for easy change). Fixed forward declaration of ttbreakc(). Improved readability of xxshow output. Removed "super" pad code. Modified ps_state to be a real state variable. 03-Aug-1989: Paul Traina Reversed earlier change to xxselect which didn't pass major #. Modified xxshow output to not use %nd which isn't supported in BSD. 28-Aug-1989: Paul Traina Changed parameters of make_x25_call -- plug user data field directly. 14-Nov-1989: Paul Traina Added support for Ultrix 3.1 which uses HUPCL instead of HUPCLS because of that stupid termio interface (sigh). 16-Nov-1989: Paul Traina Changed parity mask to input_parity_mask, added output_parity_mask. */