1*33183Sbostic /* 2*33183Sbostic * Copyright (c) 1987 Regents of the University of California. 3*33183Sbostic * All rights reserved. 4*33183Sbostic * 5*33183Sbostic * Redistribution and use in source and binary forms are permitted 6*33183Sbostic * provided that this notice is preserved and that due credit is given 7*33183Sbostic * to the University of California at Berkeley. The name of the University 8*33183Sbostic * may not be used to endorse or promote products derived from this 9*33183Sbostic * software without specific prior written permission. This software 10*33183Sbostic * is provided ``as is'' without express or implied warranty. 11*33183Sbostic * 12*33183Sbostic * @(#)if_sl.c 7.5 (Berkeley) 12/30/87 13*33183Sbostic */ 1426122Skarels 1526121Skarels /* 1626121Skarels * Serial Line interface 1726121Skarels * 1826121Skarels * Rick Adams 1926121Skarels * Center for Seismic Studies 2026121Skarels * 1300 N 17th Street, Suite 1450 2126121Skarels * Arlington, Virginia 22209 2226121Skarels * (703)276-7900 2326121Skarels * rick@seismo.ARPA 2426121Skarels * seismo!rick 2526121Skarels * 2626121Skarels * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). 2726378Skarels * N.B.: this belongs in netinet, not net, the way it stands now. 2826122Skarels * Should have a link-layer type designation, but wouldn't be 2926122Skarels * backwards-compatible. 3026121Skarels * 3126121Skarels * Converted to 4.3BSD Beta by Chris Torek. 3226378Skarels * Other changes made at Berkeley, based in part on code by Kirk Smith. 3326121Skarels */ 3426121Skarels 3526121Skarels /* $Header: if_sl.c,v 1.12 85/12/20 21:54:55 chris Exp $ */ 3626121Skarels /* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */ 3726121Skarels 3826121Skarels #include "sl.h" 3926121Skarels #if NSL > 0 4026121Skarels 4126121Skarels #include "param.h" 4226121Skarels #include "mbuf.h" 4326121Skarels #include "buf.h" 4431047Ssam #include "dkstat.h" 4526121Skarels #include "socket.h" 4626121Skarels #include "ioctl.h" 4726378Skarels #include "file.h" 4826121Skarels #include "tty.h" 4926121Skarels #include "errno.h" 5026121Skarels 5126378Skarels #include "if.h" 5226378Skarels #include "netisr.h" 5326378Skarels #include "route.h" 5426378Skarels #if INET 5526121Skarels #include "../netinet/in.h" 5626121Skarels #include "../netinet/in_systm.h" 5728986Skarels #include "../netinet/in_var.h" 5826121Skarels #include "../netinet/ip.h" 5926378Skarels #endif 6026121Skarels 6132060Sbostic #include "../machine/mtpr.h" 6226121Skarels 6326121Skarels /* 6426378Skarels * N.B.: SLMTU is now a hard limit on input packet size. 6526378Skarels * SLMTU must be <= CLBYTES - sizeof(struct ifnet *). 6626121Skarels */ 6726121Skarels #define SLMTU 1006 6826378Skarels #define SLIP_HIWAT 1000 /* don't start a new packet if HIWAT on queue */ 6926378Skarels #define CLISTRESERVE 1000 /* Can't let clists get too low */ 7026121Skarels 7126121Skarels struct sl_softc { 7226121Skarels struct ifnet sc_if; /* network-visible interface */ 7326121Skarels short sc_flags; /* see below */ 7426121Skarels short sc_ilen; /* length of input-packet-so-far */ 7526121Skarels struct tty *sc_ttyp; /* pointer to tty structure */ 7626121Skarels char *sc_mp; /* pointer to next available buf char */ 7726378Skarels char *sc_buf; /* input buffer */ 7826121Skarels } sl_softc[NSL]; 7926121Skarels 8026121Skarels /* flags */ 8126121Skarels #define SC_ESCAPED 0x0001 /* saw a FRAME_ESCAPE */ 8226121Skarels #define SC_OACTIVE 0x0002 /* output tty is active */ 8326121Skarels 8426121Skarels #define FRAME_END 0300 /* Frame End */ 8526121Skarels #define FRAME_ESCAPE 0333 /* Frame Esc */ 8626121Skarels #define TRANS_FRAME_END 0334 /* transposed frame end */ 8726121Skarels #define TRANS_FRAME_ESCAPE 0335 /* transposed frame esc */ 8826121Skarels 8926121Skarels #define t_sc T_LINEP 9026121Skarels 9126121Skarels int sloutput(), slioctl(), ttrstrt(); 9226121Skarels 9326121Skarels /* 9426121Skarels * Called from boot code to establish sl interfaces. 9526121Skarels */ 9626121Skarels slattach() 9726121Skarels { 9826121Skarels register struct sl_softc *sc; 9926121Skarels register int i = 0; 10026121Skarels 10126121Skarels for (sc = sl_softc; i < NSL; sc++) { 10226121Skarels sc->sc_if.if_name = "sl"; 10326121Skarels sc->sc_if.if_unit = i++; 10426121Skarels sc->sc_if.if_mtu = SLMTU; 10526121Skarels sc->sc_if.if_flags = IFF_POINTOPOINT; 10626121Skarels sc->sc_if.if_ioctl = slioctl; 10726121Skarels sc->sc_if.if_output = sloutput; 10826121Skarels sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN; 10926121Skarels if_attach(&sc->sc_if); 11026121Skarels } 11126121Skarels } 11226121Skarels 11326121Skarels /* 11426121Skarels * Line specific open routine. 11526121Skarels * Attach the given tty to the first available sl unit. 11626121Skarels */ 11726378Skarels /* ARGSUSED */ 11826121Skarels slopen(dev, tp) 11926121Skarels dev_t dev; 12026121Skarels register struct tty *tp; 12126121Skarels { 12226121Skarels register struct sl_softc *sc; 12326121Skarels register int nsl; 12426121Skarels 12526378Skarels if (!suser()) 12626378Skarels return (EPERM); 12726378Skarels if (tp->t_line == SLIPDISC) 12826121Skarels return (EBUSY); 12926121Skarels 13026121Skarels for (nsl = 0, sc = sl_softc; nsl < NSL; nsl++, sc++) 13126121Skarels if (sc->sc_ttyp == NULL) { 13226121Skarels sc->sc_flags = 0; 13326121Skarels sc->sc_ilen = 0; 13426378Skarels if (slinit(sc) == 0) 13526378Skarels return (ENOBUFS); 13626121Skarels tp->t_sc = (caddr_t)sc; 13726121Skarels sc->sc_ttyp = tp; 13826378Skarels ttyflush(tp, FREAD | FWRITE); 13926121Skarels return (0); 14026121Skarels } 14126121Skarels 14226378Skarels return (ENXIO); 14326121Skarels } 14426121Skarels 14526121Skarels /* 14626121Skarels * Line specific close routine. 14726121Skarels * Detach the tty from the sl unit. 14826121Skarels * Mimics part of ttyclose(). 14926121Skarels */ 15026121Skarels slclose(tp) 15126121Skarels struct tty *tp; 15226121Skarels { 15326121Skarels register struct sl_softc *sc; 15426121Skarels int s; 15526121Skarels 15626121Skarels ttywflush(tp); 15726121Skarels tp->t_line = 0; 15826121Skarels s = splimp(); /* paranoid; splnet probably ok */ 15926121Skarels sc = (struct sl_softc *)tp->t_sc; 16026121Skarels if (sc != NULL) { 16126121Skarels if_down(&sc->sc_if); 16226121Skarels sc->sc_ttyp = NULL; 16326121Skarels tp->t_sc = NULL; 16426378Skarels MCLFREE((struct mbuf *)sc->sc_buf); 16526378Skarels sc->sc_buf = 0; 16626121Skarels } 16726121Skarels splx(s); 16826121Skarels } 16926121Skarels 17026121Skarels /* 17126121Skarels * Line specific (tty) ioctl routine. 17226121Skarels * Provide a way to get the sl unit number. 17326121Skarels */ 17426378Skarels /* ARGSUSED */ 17526121Skarels sltioctl(tp, cmd, data, flag) 17626121Skarels struct tty *tp; 17726121Skarels caddr_t data; 17826121Skarels { 17926121Skarels 18026121Skarels if (cmd == TIOCGETD) { 18126121Skarels *(int *)data = ((struct sl_softc *)tp->t_sc)->sc_if.if_unit; 18226121Skarels return (0); 18326121Skarels } 18426121Skarels return (-1); 18526121Skarels } 18626121Skarels 18726121Skarels /* 18826121Skarels * Queue a packet. Start transmission if not active. 18926121Skarels */ 19026121Skarels sloutput(ifp, m, dst) 19126121Skarels register struct ifnet *ifp; 19226121Skarels register struct mbuf *m; 19326121Skarels struct sockaddr *dst; 19426121Skarels { 19526121Skarels register struct sl_softc *sc; 19626121Skarels int s; 19726121Skarels 19826121Skarels /* 19926121Skarels * `Cannot happen' (see slioctl). Someday we will extend 20026121Skarels * the line protocol to support other address families. 20126121Skarels */ 20226121Skarels if (dst->sa_family != AF_INET) { 20326121Skarels printf("sl%d: af%d not supported\n", ifp->if_unit, 20426121Skarels dst->sa_family); 20526121Skarels m_freem(m); 20626121Skarels return (EAFNOSUPPORT); 20726121Skarels } 20826121Skarels 20926121Skarels sc = &sl_softc[ifp->if_unit]; 21026121Skarels if (sc->sc_ttyp == NULL) { 21126121Skarels m_freem(m); 21226121Skarels return (ENETDOWN); /* sort of */ 21326121Skarels } 21426899Skarels if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0) { 21526899Skarels m_freem(m); 21626899Skarels return (EHOSTUNREACH); 21726899Skarels } 21826121Skarels s = splimp(); 21926121Skarels if (IF_QFULL(&ifp->if_snd)) { 22026121Skarels IF_DROP(&ifp->if_snd); 22126121Skarels splx(s); 22226121Skarels m_freem(m); 22326378Skarels sc->sc_if.if_oerrors++; 22426121Skarels return (ENOBUFS); 22526121Skarels } 22626121Skarels IF_ENQUEUE(&ifp->if_snd, m); 22726121Skarels if ((sc->sc_flags & SC_OACTIVE) == 0) { 22826121Skarels splx(s); 22926121Skarels slstart(sc->sc_ttyp); 23026121Skarels } else 23126121Skarels splx(s); 23226121Skarels return (0); 23326121Skarels } 23426121Skarels 23526121Skarels /* 23626121Skarels * Start output on interface. Get another datagram 23726121Skarels * to send from the interface queue and map it to 23826121Skarels * the interface before starting output. 23926121Skarels */ 24026121Skarels slstart(tp) 24126121Skarels register struct tty *tp; 24226121Skarels { 24326121Skarels register struct sl_softc *sc = (struct sl_softc *)tp->t_sc; 24426121Skarels register struct mbuf *m; 24526378Skarels register int len; 24626378Skarels register u_char *cp; 24726378Skarels int flush, nd, np, n, s; 24826378Skarels struct mbuf *m2; 24926378Skarels extern int cfreecount; 25026121Skarels 25126378Skarels for (;;) { 25226378Skarels /* 25326378Skarels * If there is more in the output queue, just send it now. 25426378Skarels * We are being called in lieu of ttstart and must do what 25526378Skarels * it would. 25626378Skarels */ 25726378Skarels if (tp->t_outq.c_cc > 0) 25826378Skarels ttstart(tp); 25926378Skarels if (tp->t_outq.c_cc > SLIP_HIWAT) 26026378Skarels return; 26126121Skarels 26226378Skarels /* 26326378Skarels * This happens briefly when the line shuts down. 26426378Skarels */ 26526378Skarels if (sc == NULL) 26626378Skarels return; 26726121Skarels 26826378Skarels /* 26926378Skarels * If system is getting low on clists 27026378Skarels * and we have something running already, stop here. 27126378Skarels */ 27226378Skarels if (cfreecount < CLISTRESERVE + SLMTU && 27326378Skarels sc->sc_flags & SC_OACTIVE) 27426378Skarels return; 27526121Skarels 27626378Skarels /* 27726378Skarels * Get a packet and send it to the interface. 27826378Skarels */ 27926378Skarels s = splimp(); 28026378Skarels IF_DEQUEUE(&sc->sc_if.if_snd, m); 28126378Skarels if (m == NULL) { 28226378Skarels if (tp->t_outq.c_cc == 0) 28326378Skarels sc->sc_flags &= ~SC_OACTIVE; 28426378Skarels splx(s); 28526378Skarels return; 28626378Skarels } 28726378Skarels flush = !(sc->sc_flags & SC_OACTIVE); 28826378Skarels sc->sc_flags |= SC_OACTIVE; 28926378Skarels splx(s); 29026121Skarels 29126378Skarels /* 29226378Skarels * The extra FRAME_END will start up a new packet, and thus 29326378Skarels * will flush any accumulated garbage. We do this whenever 29426378Skarels * the line may have been idle for some time. 29526378Skarels */ 29626378Skarels if (flush) 29726378Skarels (void) putc(FRAME_END, &tp->t_outq); 29826378Skarels 29926378Skarels while (m) { 30026378Skarels cp = mtod(m, u_char *); 30126378Skarels len = m->m_len; 30226378Skarels while (len > 0) { 30326378Skarels /* 30426378Skarels * Find out how many bytes in the string we can 30526378Skarels * handle without doing something special. 30626378Skarels */ 30726378Skarels nd = locc(FRAME_ESCAPE, len, cp); 30826378Skarels np = locc(FRAME_END, len, cp); 30926378Skarels n = len - MAX(nd, np); 31026378Skarels if (n) { 31126378Skarels /* 31226378Skarels * Put n characters at once 31326378Skarels * into the tty output queue. 31426378Skarels */ 31526378Skarels if (b_to_q((char *)cp, n, &tp->t_outq)) 31626378Skarels break; 31727688Smckusick len -= n; 31827688Smckusick cp += n; 31926121Skarels } 32026378Skarels /* 32126378Skarels * If there are characters left in the mbuf, 32226378Skarels * the first one must be special.. 32326378Skarels * Put it out in a different form. 32426378Skarels */ 32526378Skarels if (len) { 32626378Skarels if (putc(FRAME_ESCAPE, &tp->t_outq)) 32726378Skarels break; 32826378Skarels if (putc(*cp == FRAME_ESCAPE ? 32926378Skarels TRANS_FRAME_ESCAPE : TRANS_FRAME_END, 33026378Skarels &tp->t_outq)) { 33126378Skarels (void) unputc(&tp->t_outq); 33226378Skarels break; 33326378Skarels } 33426378Skarels cp++; 33526378Skarels len--; 33626378Skarels } 33726378Skarels } 33826378Skarels MFREE(m, m2); 33926378Skarels m = m2; 34026121Skarels } 34126378Skarels 34226378Skarels if (putc(FRAME_END, &tp->t_outq)) { 34326378Skarels /* 34426378Skarels * Not enough room. Remove a char to make room 34526378Skarels * and end the packet normally. 34626378Skarels * If you get many collisions (more than one or two 34726378Skarels * a day) you probably do not have enough clists 34826378Skarels * and you should increase "nclist" in param.c. 34926378Skarels */ 35026378Skarels (void) unputc(&tp->t_outq); 35126378Skarels (void) putc(FRAME_END, &tp->t_outq); 35226378Skarels sc->sc_if.if_collisions++; 35326378Skarels } else 35426378Skarels sc->sc_if.if_opackets++; 35526121Skarels } 35626378Skarels } 35726121Skarels 35826378Skarels slinit(sc) 35926378Skarels register struct sl_softc *sc; 36026378Skarels { 36126378Skarels struct mbuf *p; 36226121Skarels 36326378Skarels if (sc->sc_buf == (char *) 0) { 36429824Skarels int s = splimp(); 36529824Skarels 36626378Skarels MCLALLOC(p, 1); 36729824Skarels splx(s); 36826378Skarels if (p) { 36926378Skarels sc->sc_buf = (char *)p; 37026378Skarels sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 37126378Skarels } else { 37226378Skarels printf("sl%d: can't allocate buffer\n", sc - sl_softc); 37326378Skarels sc->sc_if.if_flags &= ~IFF_UP; 37426378Skarels return (0); 37526378Skarels } 37626378Skarels } 37726378Skarels return (1); 37826121Skarels } 37926121Skarels 38026121Skarels /* 38126121Skarels * Copy data buffer to mbuf chain; add ifnet pointer ifp. 38226121Skarels */ 38326121Skarels struct mbuf * 38426378Skarels sl_btom(sc, len, ifp) 38526378Skarels struct sl_softc *sc; 38626121Skarels register int len; 38726121Skarels struct ifnet *ifp; 38826121Skarels { 38926378Skarels register caddr_t cp; 39026121Skarels register struct mbuf *m, **mp; 39126378Skarels register unsigned count; 39226121Skarels struct mbuf *top = NULL; 39326121Skarels 39426378Skarels cp = sc->sc_buf + sizeof(struct ifnet *); 39526121Skarels mp = ⊤ 39626121Skarels while (len > 0) { 39726121Skarels MGET(m, M_DONTWAIT, MT_DATA); 39826121Skarels if ((*mp = m) == NULL) { 39926121Skarels m_freem(top); 40026121Skarels return (NULL); 40126121Skarels } 40226378Skarels if (ifp) 40326121Skarels m->m_off += sizeof(ifp); 40426378Skarels /* 40526378Skarels * If we have at least NBPG bytes, 40626378Skarels * allocate a new page. Swap the current buffer page 40726378Skarels * with the new one. We depend on having a space 40826378Skarels * left at the beginning of the buffer 40926378Skarels * for the interface pointer. 41026378Skarels */ 41126378Skarels if (len >= NBPG) { 41226378Skarels MCLGET(m); 41326378Skarels if (m->m_len == CLBYTES) { 41426378Skarels cp = mtod(m, char *); 41526378Skarels m->m_off = (int)sc->sc_buf - (int)m; 41628184Smckusick sc->sc_buf = cp; 41726378Skarels if (ifp) { 41826378Skarels m->m_off += sizeof(ifp); 41926378Skarels count = MIN(len, 42026378Skarels CLBYTES - sizeof(struct ifnet *)); 42126378Skarels } else 42226121Skarels count = MIN(len, CLBYTES); 42326378Skarels goto nocopy; 42426378Skarels } 42526121Skarels } 42626378Skarels if (ifp) 42726378Skarels count = MIN(len, MLEN - sizeof(ifp)); 42826378Skarels else 42926378Skarels count = MIN(len, MLEN); 43026378Skarels bcopy(cp, mtod(m, caddr_t), count); 43126378Skarels nocopy: 43226121Skarels m->m_len = count; 43326121Skarels if (ifp) { 43426121Skarels m->m_off -= sizeof(ifp); 43526121Skarels m->m_len += sizeof(ifp); 43626121Skarels *mtod(m, struct ifnet **) = ifp; 43726121Skarels ifp = NULL; 43826121Skarels } 43926378Skarels cp += count; 44026121Skarels len -= count; 44126121Skarels mp = &m->m_next; 44226121Skarels } 44326121Skarels return (top); 44426121Skarels } 44526121Skarels 44626121Skarels /* 44726121Skarels * tty interface receiver interrupt. 44826121Skarels */ 44926121Skarels slinput(c, tp) 45026121Skarels register int c; 45126121Skarels register struct tty *tp; 45226121Skarels { 45326121Skarels register struct sl_softc *sc; 45426121Skarels register struct mbuf *m; 45526121Skarels int s; 45626121Skarels 45726378Skarels tk_nin++; 45826121Skarels sc = (struct sl_softc *)tp->t_sc; 45926121Skarels if (sc == NULL) 46026121Skarels return; 46126121Skarels 46226121Skarels c &= 0xff; 46326121Skarels if (sc->sc_flags & SC_ESCAPED) { 46426121Skarels sc->sc_flags &= ~SC_ESCAPED; 46526121Skarels switch (c) { 46626121Skarels 46726121Skarels case TRANS_FRAME_ESCAPE: 46826121Skarels c = FRAME_ESCAPE; 46926121Skarels break; 47026121Skarels 47126121Skarels case TRANS_FRAME_END: 47226121Skarels c = FRAME_END; 47326121Skarels break; 47426121Skarels 47526121Skarels default: 47626121Skarels sc->sc_if.if_ierrors++; 47726378Skarels sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 47826121Skarels sc->sc_ilen = 0; 47926121Skarels return; 48026121Skarels } 48126121Skarels } else { 48226121Skarels switch (c) { 48326121Skarels 48426121Skarels case FRAME_END: 48526121Skarels if (sc->sc_ilen == 0) /* ignore */ 48626121Skarels return; 48726378Skarels m = sl_btom(sc, sc->sc_ilen, &sc->sc_if); 48826121Skarels if (m == NULL) { 48926121Skarels sc->sc_if.if_ierrors++; 49026121Skarels return; 49126121Skarels } 49226378Skarels sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 49326121Skarels sc->sc_ilen = 0; 49426121Skarels sc->sc_if.if_ipackets++; 49526121Skarels s = splimp(); 49626121Skarels if (IF_QFULL(&ipintrq)) { 49726121Skarels IF_DROP(&ipintrq); 49826121Skarels sc->sc_if.if_ierrors++; 49926121Skarels m_freem(m); 50026121Skarels } else { 50126121Skarels IF_ENQUEUE(&ipintrq, m); 50226121Skarels schednetisr(NETISR_IP); 50326121Skarels } 50426121Skarels splx(s); 50526121Skarels return; 50626121Skarels 50726121Skarels case FRAME_ESCAPE: 50826121Skarels sc->sc_flags |= SC_ESCAPED; 50926121Skarels return; 51026121Skarels } 51126121Skarels } 51228200Skarels if (++sc->sc_ilen > SLMTU) { 51326121Skarels sc->sc_if.if_ierrors++; 51426378Skarels sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 51526121Skarels sc->sc_ilen = 0; 51626121Skarels return; 51726121Skarels } 51826121Skarels *sc->sc_mp++ = c; 51926121Skarels } 52026121Skarels 52126121Skarels /* 52226121Skarels * Process an ioctl request. 52326121Skarels */ 52426121Skarels slioctl(ifp, cmd, data) 52526121Skarels register struct ifnet *ifp; 52626121Skarels int cmd; 52726121Skarels caddr_t data; 52826121Skarels { 52926121Skarels register struct ifaddr *ifa = (struct ifaddr *)data; 53026121Skarels int s = splimp(), error = 0; 53126121Skarels 53226121Skarels switch (cmd) { 53326121Skarels 53426121Skarels case SIOCSIFADDR: 53526121Skarels if (ifa->ifa_addr.sa_family == AF_INET) 53626121Skarels ifp->if_flags |= IFF_UP; 53726121Skarels else 53826121Skarels error = EAFNOSUPPORT; 53926121Skarels break; 54026121Skarels 54126121Skarels case SIOCSIFDSTADDR: 54226121Skarels if (ifa->ifa_addr.sa_family != AF_INET) 54326121Skarels error = EAFNOSUPPORT; 54426121Skarels break; 54526121Skarels 54626121Skarels default: 54726121Skarels error = EINVAL; 54826121Skarels } 54926121Skarels splx(s); 55026121Skarels return (error); 55126121Skarels } 55226121Skarels #endif 553