1*32060Sbostic /* @(#)if_sl.c 7.4 (Berkeley) 08/13/87 */ 226122Skarels 326121Skarels /* 426121Skarels * Serial Line interface 526121Skarels * 626121Skarels * Rick Adams 726121Skarels * Center for Seismic Studies 826121Skarels * 1300 N 17th Street, Suite 1450 926121Skarels * Arlington, Virginia 22209 1026121Skarels * (703)276-7900 1126121Skarels * rick@seismo.ARPA 1226121Skarels * seismo!rick 1326121Skarels * 1426121Skarels * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). 1526378Skarels * N.B.: this belongs in netinet, not net, the way it stands now. 1626122Skarels * Should have a link-layer type designation, but wouldn't be 1726122Skarels * backwards-compatible. 1826121Skarels * 1926121Skarels * Converted to 4.3BSD Beta by Chris Torek. 2026378Skarels * Other changes made at Berkeley, based in part on code by Kirk Smith. 2126121Skarels */ 2226121Skarels 2326121Skarels /* $Header: if_sl.c,v 1.12 85/12/20 21:54:55 chris Exp $ */ 2426121Skarels /* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */ 2526121Skarels 2626121Skarels #include "sl.h" 2726121Skarels #if NSL > 0 2826121Skarels 2926121Skarels #include "param.h" 3026121Skarels #include "mbuf.h" 3126121Skarels #include "buf.h" 3231047Ssam #include "dkstat.h" 3326121Skarels #include "socket.h" 3426121Skarels #include "ioctl.h" 3526378Skarels #include "file.h" 3626121Skarels #include "tty.h" 3726121Skarels #include "errno.h" 3826121Skarels 3926378Skarels #include "if.h" 4026378Skarels #include "netisr.h" 4126378Skarels #include "route.h" 4226378Skarels #if INET 4326121Skarels #include "../netinet/in.h" 4426121Skarels #include "../netinet/in_systm.h" 4528986Skarels #include "../netinet/in_var.h" 4626121Skarels #include "../netinet/ip.h" 4726378Skarels #endif 4826121Skarels 49*32060Sbostic #include "../machine/mtpr.h" 5026121Skarels 5126121Skarels /* 5226378Skarels * N.B.: SLMTU is now a hard limit on input packet size. 5326378Skarels * SLMTU must be <= CLBYTES - sizeof(struct ifnet *). 5426121Skarels */ 5526121Skarels #define SLMTU 1006 5626378Skarels #define SLIP_HIWAT 1000 /* don't start a new packet if HIWAT on queue */ 5726378Skarels #define CLISTRESERVE 1000 /* Can't let clists get too low */ 5826121Skarels 5926121Skarels struct sl_softc { 6026121Skarels struct ifnet sc_if; /* network-visible interface */ 6126121Skarels short sc_flags; /* see below */ 6226121Skarels short sc_ilen; /* length of input-packet-so-far */ 6326121Skarels struct tty *sc_ttyp; /* pointer to tty structure */ 6426121Skarels char *sc_mp; /* pointer to next available buf char */ 6526378Skarels char *sc_buf; /* input buffer */ 6626121Skarels } sl_softc[NSL]; 6726121Skarels 6826121Skarels /* flags */ 6926121Skarels #define SC_ESCAPED 0x0001 /* saw a FRAME_ESCAPE */ 7026121Skarels #define SC_OACTIVE 0x0002 /* output tty is active */ 7126121Skarels 7226121Skarels #define FRAME_END 0300 /* Frame End */ 7326121Skarels #define FRAME_ESCAPE 0333 /* Frame Esc */ 7426121Skarels #define TRANS_FRAME_END 0334 /* transposed frame end */ 7526121Skarels #define TRANS_FRAME_ESCAPE 0335 /* transposed frame esc */ 7626121Skarels 7726121Skarels #define t_sc T_LINEP 7826121Skarels 7926121Skarels int sloutput(), slioctl(), ttrstrt(); 8026121Skarels 8126121Skarels /* 8226121Skarels * Called from boot code to establish sl interfaces. 8326121Skarels */ 8426121Skarels slattach() 8526121Skarels { 8626121Skarels register struct sl_softc *sc; 8726121Skarels register int i = 0; 8826121Skarels 8926121Skarels for (sc = sl_softc; i < NSL; sc++) { 9026121Skarels sc->sc_if.if_name = "sl"; 9126121Skarels sc->sc_if.if_unit = i++; 9226121Skarels sc->sc_if.if_mtu = SLMTU; 9326121Skarels sc->sc_if.if_flags = IFF_POINTOPOINT; 9426121Skarels sc->sc_if.if_ioctl = slioctl; 9526121Skarels sc->sc_if.if_output = sloutput; 9626121Skarels sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN; 9726121Skarels if_attach(&sc->sc_if); 9826121Skarels } 9926121Skarels } 10026121Skarels 10126121Skarels /* 10226121Skarels * Line specific open routine. 10326121Skarels * Attach the given tty to the first available sl unit. 10426121Skarels */ 10526378Skarels /* ARGSUSED */ 10626121Skarels slopen(dev, tp) 10726121Skarels dev_t dev; 10826121Skarels register struct tty *tp; 10926121Skarels { 11026121Skarels register struct sl_softc *sc; 11126121Skarels register int nsl; 11226121Skarels 11326378Skarels if (!suser()) 11426378Skarels return (EPERM); 11526378Skarels if (tp->t_line == SLIPDISC) 11626121Skarels return (EBUSY); 11726121Skarels 11826121Skarels for (nsl = 0, sc = sl_softc; nsl < NSL; nsl++, sc++) 11926121Skarels if (sc->sc_ttyp == NULL) { 12026121Skarels sc->sc_flags = 0; 12126121Skarels sc->sc_ilen = 0; 12226378Skarels if (slinit(sc) == 0) 12326378Skarels return (ENOBUFS); 12426121Skarels tp->t_sc = (caddr_t)sc; 12526121Skarels sc->sc_ttyp = tp; 12626378Skarels ttyflush(tp, FREAD | FWRITE); 12726121Skarels return (0); 12826121Skarels } 12926121Skarels 13026378Skarels return (ENXIO); 13126121Skarels } 13226121Skarels 13326121Skarels /* 13426121Skarels * Line specific close routine. 13526121Skarels * Detach the tty from the sl unit. 13626121Skarels * Mimics part of ttyclose(). 13726121Skarels */ 13826121Skarels slclose(tp) 13926121Skarels struct tty *tp; 14026121Skarels { 14126121Skarels register struct sl_softc *sc; 14226121Skarels int s; 14326121Skarels 14426121Skarels ttywflush(tp); 14526121Skarels tp->t_line = 0; 14626121Skarels s = splimp(); /* paranoid; splnet probably ok */ 14726121Skarels sc = (struct sl_softc *)tp->t_sc; 14826121Skarels if (sc != NULL) { 14926121Skarels if_down(&sc->sc_if); 15026121Skarels sc->sc_ttyp = NULL; 15126121Skarels tp->t_sc = NULL; 15226378Skarels MCLFREE((struct mbuf *)sc->sc_buf); 15326378Skarels sc->sc_buf = 0; 15426121Skarels } 15526121Skarels splx(s); 15626121Skarels } 15726121Skarels 15826121Skarels /* 15926121Skarels * Line specific (tty) ioctl routine. 16026121Skarels * Provide a way to get the sl unit number. 16126121Skarels */ 16226378Skarels /* ARGSUSED */ 16326121Skarels sltioctl(tp, cmd, data, flag) 16426121Skarels struct tty *tp; 16526121Skarels caddr_t data; 16626121Skarels { 16726121Skarels 16826121Skarels if (cmd == TIOCGETD) { 16926121Skarels *(int *)data = ((struct sl_softc *)tp->t_sc)->sc_if.if_unit; 17026121Skarels return (0); 17126121Skarels } 17226121Skarels return (-1); 17326121Skarels } 17426121Skarels 17526121Skarels /* 17626121Skarels * Queue a packet. Start transmission if not active. 17726121Skarels */ 17826121Skarels sloutput(ifp, m, dst) 17926121Skarels register struct ifnet *ifp; 18026121Skarels register struct mbuf *m; 18126121Skarels struct sockaddr *dst; 18226121Skarels { 18326121Skarels register struct sl_softc *sc; 18426121Skarels int s; 18526121Skarels 18626121Skarels /* 18726121Skarels * `Cannot happen' (see slioctl). Someday we will extend 18826121Skarels * the line protocol to support other address families. 18926121Skarels */ 19026121Skarels if (dst->sa_family != AF_INET) { 19126121Skarels printf("sl%d: af%d not supported\n", ifp->if_unit, 19226121Skarels dst->sa_family); 19326121Skarels m_freem(m); 19426121Skarels return (EAFNOSUPPORT); 19526121Skarels } 19626121Skarels 19726121Skarels sc = &sl_softc[ifp->if_unit]; 19826121Skarels if (sc->sc_ttyp == NULL) { 19926121Skarels m_freem(m); 20026121Skarels return (ENETDOWN); /* sort of */ 20126121Skarels } 20226899Skarels if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0) { 20326899Skarels m_freem(m); 20426899Skarels return (EHOSTUNREACH); 20526899Skarels } 20626121Skarels s = splimp(); 20726121Skarels if (IF_QFULL(&ifp->if_snd)) { 20826121Skarels IF_DROP(&ifp->if_snd); 20926121Skarels splx(s); 21026121Skarels m_freem(m); 21126378Skarels sc->sc_if.if_oerrors++; 21226121Skarels return (ENOBUFS); 21326121Skarels } 21426121Skarels IF_ENQUEUE(&ifp->if_snd, m); 21526121Skarels if ((sc->sc_flags & SC_OACTIVE) == 0) { 21626121Skarels splx(s); 21726121Skarels slstart(sc->sc_ttyp); 21826121Skarels } else 21926121Skarels splx(s); 22026121Skarels return (0); 22126121Skarels } 22226121Skarels 22326121Skarels /* 22426121Skarels * Start output on interface. Get another datagram 22526121Skarels * to send from the interface queue and map it to 22626121Skarels * the interface before starting output. 22726121Skarels */ 22826121Skarels slstart(tp) 22926121Skarels register struct tty *tp; 23026121Skarels { 23126121Skarels register struct sl_softc *sc = (struct sl_softc *)tp->t_sc; 23226121Skarels register struct mbuf *m; 23326378Skarels register int len; 23426378Skarels register u_char *cp; 23526378Skarels int flush, nd, np, n, s; 23626378Skarels struct mbuf *m2; 23726378Skarels extern int cfreecount; 23826121Skarels 23926378Skarels for (;;) { 24026378Skarels /* 24126378Skarels * If there is more in the output queue, just send it now. 24226378Skarels * We are being called in lieu of ttstart and must do what 24326378Skarels * it would. 24426378Skarels */ 24526378Skarels if (tp->t_outq.c_cc > 0) 24626378Skarels ttstart(tp); 24726378Skarels if (tp->t_outq.c_cc > SLIP_HIWAT) 24826378Skarels return; 24926121Skarels 25026378Skarels /* 25126378Skarels * This happens briefly when the line shuts down. 25226378Skarels */ 25326378Skarels if (sc == NULL) 25426378Skarels return; 25526121Skarels 25626378Skarels /* 25726378Skarels * If system is getting low on clists 25826378Skarels * and we have something running already, stop here. 25926378Skarels */ 26026378Skarels if (cfreecount < CLISTRESERVE + SLMTU && 26126378Skarels sc->sc_flags & SC_OACTIVE) 26226378Skarels return; 26326121Skarels 26426378Skarels /* 26526378Skarels * Get a packet and send it to the interface. 26626378Skarels */ 26726378Skarels s = splimp(); 26826378Skarels IF_DEQUEUE(&sc->sc_if.if_snd, m); 26926378Skarels if (m == NULL) { 27026378Skarels if (tp->t_outq.c_cc == 0) 27126378Skarels sc->sc_flags &= ~SC_OACTIVE; 27226378Skarels splx(s); 27326378Skarels return; 27426378Skarels } 27526378Skarels flush = !(sc->sc_flags & SC_OACTIVE); 27626378Skarels sc->sc_flags |= SC_OACTIVE; 27726378Skarels splx(s); 27826121Skarels 27926378Skarels /* 28026378Skarels * The extra FRAME_END will start up a new packet, and thus 28126378Skarels * will flush any accumulated garbage. We do this whenever 28226378Skarels * the line may have been idle for some time. 28326378Skarels */ 28426378Skarels if (flush) 28526378Skarels (void) putc(FRAME_END, &tp->t_outq); 28626378Skarels 28726378Skarels while (m) { 28826378Skarels cp = mtod(m, u_char *); 28926378Skarels len = m->m_len; 29026378Skarels while (len > 0) { 29126378Skarels /* 29226378Skarels * Find out how many bytes in the string we can 29326378Skarels * handle without doing something special. 29426378Skarels */ 29526378Skarels nd = locc(FRAME_ESCAPE, len, cp); 29626378Skarels np = locc(FRAME_END, len, cp); 29726378Skarels n = len - MAX(nd, np); 29826378Skarels if (n) { 29926378Skarels /* 30026378Skarels * Put n characters at once 30126378Skarels * into the tty output queue. 30226378Skarels */ 30326378Skarels if (b_to_q((char *)cp, n, &tp->t_outq)) 30426378Skarels break; 30527688Smckusick len -= n; 30627688Smckusick cp += n; 30726121Skarels } 30826378Skarels /* 30926378Skarels * If there are characters left in the mbuf, 31026378Skarels * the first one must be special.. 31126378Skarels * Put it out in a different form. 31226378Skarels */ 31326378Skarels if (len) { 31426378Skarels if (putc(FRAME_ESCAPE, &tp->t_outq)) 31526378Skarels break; 31626378Skarels if (putc(*cp == FRAME_ESCAPE ? 31726378Skarels TRANS_FRAME_ESCAPE : TRANS_FRAME_END, 31826378Skarels &tp->t_outq)) { 31926378Skarels (void) unputc(&tp->t_outq); 32026378Skarels break; 32126378Skarels } 32226378Skarels cp++; 32326378Skarels len--; 32426378Skarels } 32526378Skarels } 32626378Skarels MFREE(m, m2); 32726378Skarels m = m2; 32826121Skarels } 32926378Skarels 33026378Skarels if (putc(FRAME_END, &tp->t_outq)) { 33126378Skarels /* 33226378Skarels * Not enough room. Remove a char to make room 33326378Skarels * and end the packet normally. 33426378Skarels * If you get many collisions (more than one or two 33526378Skarels * a day) you probably do not have enough clists 33626378Skarels * and you should increase "nclist" in param.c. 33726378Skarels */ 33826378Skarels (void) unputc(&tp->t_outq); 33926378Skarels (void) putc(FRAME_END, &tp->t_outq); 34026378Skarels sc->sc_if.if_collisions++; 34126378Skarels } else 34226378Skarels sc->sc_if.if_opackets++; 34326121Skarels } 34426378Skarels } 34526121Skarels 34626378Skarels slinit(sc) 34726378Skarels register struct sl_softc *sc; 34826378Skarels { 34926378Skarels struct mbuf *p; 35026121Skarels 35126378Skarels if (sc->sc_buf == (char *) 0) { 35229824Skarels int s = splimp(); 35329824Skarels 35426378Skarels MCLALLOC(p, 1); 35529824Skarels splx(s); 35626378Skarels if (p) { 35726378Skarels sc->sc_buf = (char *)p; 35826378Skarels sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 35926378Skarels } else { 36026378Skarels printf("sl%d: can't allocate buffer\n", sc - sl_softc); 36126378Skarels sc->sc_if.if_flags &= ~IFF_UP; 36226378Skarels return (0); 36326378Skarels } 36426378Skarels } 36526378Skarels return (1); 36626121Skarels } 36726121Skarels 36826121Skarels /* 36926121Skarels * Copy data buffer to mbuf chain; add ifnet pointer ifp. 37026121Skarels */ 37126121Skarels struct mbuf * 37226378Skarels sl_btom(sc, len, ifp) 37326378Skarels struct sl_softc *sc; 37426121Skarels register int len; 37526121Skarels struct ifnet *ifp; 37626121Skarels { 37726378Skarels register caddr_t cp; 37826121Skarels register struct mbuf *m, **mp; 37926378Skarels register unsigned count; 38026121Skarels struct mbuf *top = NULL; 38126121Skarels 38226378Skarels cp = sc->sc_buf + sizeof(struct ifnet *); 38326121Skarels mp = ⊤ 38426121Skarels while (len > 0) { 38526121Skarels MGET(m, M_DONTWAIT, MT_DATA); 38626121Skarels if ((*mp = m) == NULL) { 38726121Skarels m_freem(top); 38826121Skarels return (NULL); 38926121Skarels } 39026378Skarels if (ifp) 39126121Skarels m->m_off += sizeof(ifp); 39226378Skarels /* 39326378Skarels * If we have at least NBPG bytes, 39426378Skarels * allocate a new page. Swap the current buffer page 39526378Skarels * with the new one. We depend on having a space 39626378Skarels * left at the beginning of the buffer 39726378Skarels * for the interface pointer. 39826378Skarels */ 39926378Skarels if (len >= NBPG) { 40026378Skarels MCLGET(m); 40126378Skarels if (m->m_len == CLBYTES) { 40226378Skarels cp = mtod(m, char *); 40326378Skarels m->m_off = (int)sc->sc_buf - (int)m; 40428184Smckusick sc->sc_buf = cp; 40526378Skarels if (ifp) { 40626378Skarels m->m_off += sizeof(ifp); 40726378Skarels count = MIN(len, 40826378Skarels CLBYTES - sizeof(struct ifnet *)); 40926378Skarels } else 41026121Skarels count = MIN(len, CLBYTES); 41126378Skarels goto nocopy; 41226378Skarels } 41326121Skarels } 41426378Skarels if (ifp) 41526378Skarels count = MIN(len, MLEN - sizeof(ifp)); 41626378Skarels else 41726378Skarels count = MIN(len, MLEN); 41826378Skarels bcopy(cp, mtod(m, caddr_t), count); 41926378Skarels nocopy: 42026121Skarels m->m_len = count; 42126121Skarels if (ifp) { 42226121Skarels m->m_off -= sizeof(ifp); 42326121Skarels m->m_len += sizeof(ifp); 42426121Skarels *mtod(m, struct ifnet **) = ifp; 42526121Skarels ifp = NULL; 42626121Skarels } 42726378Skarels cp += count; 42826121Skarels len -= count; 42926121Skarels mp = &m->m_next; 43026121Skarels } 43126121Skarels return (top); 43226121Skarels } 43326121Skarels 43426121Skarels /* 43526121Skarels * tty interface receiver interrupt. 43626121Skarels */ 43726121Skarels slinput(c, tp) 43826121Skarels register int c; 43926121Skarels register struct tty *tp; 44026121Skarels { 44126121Skarels register struct sl_softc *sc; 44226121Skarels register struct mbuf *m; 44326121Skarels int s; 44426121Skarels 44526378Skarels tk_nin++; 44626121Skarels sc = (struct sl_softc *)tp->t_sc; 44726121Skarels if (sc == NULL) 44826121Skarels return; 44926121Skarels 45026121Skarels c &= 0xff; 45126121Skarels if (sc->sc_flags & SC_ESCAPED) { 45226121Skarels sc->sc_flags &= ~SC_ESCAPED; 45326121Skarels switch (c) { 45426121Skarels 45526121Skarels case TRANS_FRAME_ESCAPE: 45626121Skarels c = FRAME_ESCAPE; 45726121Skarels break; 45826121Skarels 45926121Skarels case TRANS_FRAME_END: 46026121Skarels c = FRAME_END; 46126121Skarels break; 46226121Skarels 46326121Skarels default: 46426121Skarels sc->sc_if.if_ierrors++; 46526378Skarels sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 46626121Skarels sc->sc_ilen = 0; 46726121Skarels return; 46826121Skarels } 46926121Skarels } else { 47026121Skarels switch (c) { 47126121Skarels 47226121Skarels case FRAME_END: 47326121Skarels if (sc->sc_ilen == 0) /* ignore */ 47426121Skarels return; 47526378Skarels m = sl_btom(sc, sc->sc_ilen, &sc->sc_if); 47626121Skarels if (m == NULL) { 47726121Skarels sc->sc_if.if_ierrors++; 47826121Skarels return; 47926121Skarels } 48026378Skarels sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 48126121Skarels sc->sc_ilen = 0; 48226121Skarels sc->sc_if.if_ipackets++; 48326121Skarels s = splimp(); 48426121Skarels if (IF_QFULL(&ipintrq)) { 48526121Skarels IF_DROP(&ipintrq); 48626121Skarels sc->sc_if.if_ierrors++; 48726121Skarels m_freem(m); 48826121Skarels } else { 48926121Skarels IF_ENQUEUE(&ipintrq, m); 49026121Skarels schednetisr(NETISR_IP); 49126121Skarels } 49226121Skarels splx(s); 49326121Skarels return; 49426121Skarels 49526121Skarels case FRAME_ESCAPE: 49626121Skarels sc->sc_flags |= SC_ESCAPED; 49726121Skarels return; 49826121Skarels } 49926121Skarels } 50028200Skarels if (++sc->sc_ilen > SLMTU) { 50126121Skarels sc->sc_if.if_ierrors++; 50226378Skarels sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 50326121Skarels sc->sc_ilen = 0; 50426121Skarels return; 50526121Skarels } 50626121Skarels *sc->sc_mp++ = c; 50726121Skarels } 50826121Skarels 50926121Skarels /* 51026121Skarels * Process an ioctl request. 51126121Skarels */ 51226121Skarels slioctl(ifp, cmd, data) 51326121Skarels register struct ifnet *ifp; 51426121Skarels int cmd; 51526121Skarels caddr_t data; 51626121Skarels { 51726121Skarels register struct ifaddr *ifa = (struct ifaddr *)data; 51826121Skarels int s = splimp(), error = 0; 51926121Skarels 52026121Skarels switch (cmd) { 52126121Skarels 52226121Skarels case SIOCSIFADDR: 52326121Skarels if (ifa->ifa_addr.sa_family == AF_INET) 52426121Skarels ifp->if_flags |= IFF_UP; 52526121Skarels else 52626121Skarels error = EAFNOSUPPORT; 52726121Skarels break; 52826121Skarels 52926121Skarels case SIOCSIFDSTADDR: 53026121Skarels if (ifa->ifa_addr.sa_family != AF_INET) 53126121Skarels error = EAFNOSUPPORT; 53226121Skarels break; 53326121Skarels 53426121Skarels default: 53526121Skarels error = EINVAL; 53626121Skarels } 53726121Skarels splx(s); 53826121Skarels return (error); 53926121Skarels } 54026121Skarels #endif 541