133183Sbostic /* 233183Sbostic * Copyright (c) 1987 Regents of the University of California. 333183Sbostic * All rights reserved. 433183Sbostic * 533183Sbostic * Redistribution and use in source and binary forms are permitted 6*34844Sbostic * provided that the above copyright notice and this paragraph are 7*34844Sbostic * duplicated in all such forms and that any documentation, 8*34844Sbostic * advertising materials, and other materials related to such 9*34844Sbostic * distribution and use acknowledge that the software was developed 10*34844Sbostic * by the University of California, Berkeley. The name of the 11*34844Sbostic * University may not be used to endorse or promote products derived 12*34844Sbostic * from this software without specific prior written permission. 13*34844Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*34844Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*34844Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1633183Sbostic * 17*34844Sbostic * @(#)if_sl.c 7.9 (Berkeley) 06/27/88 1833183Sbostic */ 1926122Skarels 2026121Skarels /* 2126121Skarels * Serial Line interface 2226121Skarels * 2326121Skarels * Rick Adams 2426121Skarels * Center for Seismic Studies 2526121Skarels * 1300 N 17th Street, Suite 1450 2626121Skarels * Arlington, Virginia 22209 2726121Skarels * (703)276-7900 2826121Skarels * rick@seismo.ARPA 2926121Skarels * seismo!rick 3026121Skarels * 3126121Skarels * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). 3226378Skarels * N.B.: this belongs in netinet, not net, the way it stands now. 3326122Skarels * Should have a link-layer type designation, but wouldn't be 3426122Skarels * backwards-compatible. 3526121Skarels * 3626121Skarels * Converted to 4.3BSD Beta by Chris Torek. 3726378Skarels * Other changes made at Berkeley, based in part on code by Kirk Smith. 3826121Skarels */ 3926121Skarels 4026121Skarels /* $Header: if_sl.c,v 1.12 85/12/20 21:54:55 chris Exp $ */ 4126121Skarels /* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */ 4226121Skarels 4326121Skarels #include "sl.h" 4426121Skarels #if NSL > 0 4526121Skarels 4626121Skarels #include "param.h" 4726121Skarels #include "mbuf.h" 4826121Skarels #include "buf.h" 4933980Skarels #include "dkstat.h" 5026121Skarels #include "socket.h" 5126121Skarels #include "ioctl.h" 5226378Skarels #include "file.h" 5326121Skarels #include "tty.h" 5426121Skarels #include "errno.h" 5526121Skarels 5626378Skarels #include "if.h" 5726378Skarels #include "netisr.h" 5826378Skarels #include "route.h" 5926378Skarels #if INET 6026121Skarels #include "../netinet/in.h" 6126121Skarels #include "../netinet/in_systm.h" 6228986Skarels #include "../netinet/in_var.h" 6326121Skarels #include "../netinet/ip.h" 6426378Skarels #endif 6526121Skarels 6632060Sbostic #include "../machine/mtpr.h" 6726121Skarels 6826121Skarels /* 6926378Skarels * N.B.: SLMTU is now a hard limit on input packet size. 7033980Skarels * SLMTU must be <= MCLBYTES - sizeof(struct ifnet *). 7126121Skarels */ 7226121Skarels #define SLMTU 1006 7326378Skarels #define SLIP_HIWAT 1000 /* don't start a new packet if HIWAT on queue */ 7426378Skarels #define CLISTRESERVE 1000 /* Can't let clists get too low */ 7526121Skarels 7626121Skarels struct sl_softc { 7726121Skarels struct ifnet sc_if; /* network-visible interface */ 7826121Skarels short sc_flags; /* see below */ 7926121Skarels short sc_ilen; /* length of input-packet-so-far */ 8026121Skarels struct tty *sc_ttyp; /* pointer to tty structure */ 8126121Skarels char *sc_mp; /* pointer to next available buf char */ 8226378Skarels char *sc_buf; /* input buffer */ 8326121Skarels } sl_softc[NSL]; 8426121Skarels 8526121Skarels /* flags */ 8626121Skarels #define SC_ESCAPED 0x0001 /* saw a FRAME_ESCAPE */ 8726121Skarels 8826121Skarels #define FRAME_END 0300 /* Frame End */ 8926121Skarels #define FRAME_ESCAPE 0333 /* Frame Esc */ 9026121Skarels #define TRANS_FRAME_END 0334 /* transposed frame end */ 9126121Skarels #define TRANS_FRAME_ESCAPE 0335 /* transposed frame esc */ 9226121Skarels 9326121Skarels #define t_sc T_LINEP 9426121Skarels 9526121Skarels int sloutput(), slioctl(), ttrstrt(); 9626121Skarels 9726121Skarels /* 9826121Skarels * Called from boot code to establish sl interfaces. 9926121Skarels */ 10026121Skarels slattach() 10126121Skarels { 10226121Skarels register struct sl_softc *sc; 10326121Skarels register int i = 0; 10426121Skarels 10526121Skarels for (sc = sl_softc; i < NSL; sc++) { 10626121Skarels sc->sc_if.if_name = "sl"; 10726121Skarels sc->sc_if.if_unit = i++; 10826121Skarels sc->sc_if.if_mtu = SLMTU; 10926121Skarels sc->sc_if.if_flags = IFF_POINTOPOINT; 11026121Skarels sc->sc_if.if_ioctl = slioctl; 11126121Skarels sc->sc_if.if_output = sloutput; 11226121Skarels sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN; 11326121Skarels if_attach(&sc->sc_if); 11426121Skarels } 11526121Skarels } 11626121Skarels 11726121Skarels /* 11826121Skarels * Line specific open routine. 11926121Skarels * Attach the given tty to the first available sl unit. 12026121Skarels */ 12126378Skarels /* ARGSUSED */ 12226121Skarels slopen(dev, tp) 12326121Skarels dev_t dev; 12426121Skarels register struct tty *tp; 12526121Skarels { 12626121Skarels register struct sl_softc *sc; 12726121Skarels register int nsl; 12826121Skarels 12926378Skarels if (!suser()) 13026378Skarels return (EPERM); 13126378Skarels if (tp->t_line == SLIPDISC) 13226121Skarels return (EBUSY); 13326121Skarels 13426121Skarels for (nsl = 0, sc = sl_softc; nsl < NSL; nsl++, sc++) 13526121Skarels if (sc->sc_ttyp == NULL) { 13626121Skarels sc->sc_flags = 0; 13726121Skarels sc->sc_ilen = 0; 13826378Skarels if (slinit(sc) == 0) 13926378Skarels return (ENOBUFS); 14026121Skarels tp->t_sc = (caddr_t)sc; 14126121Skarels sc->sc_ttyp = tp; 14226378Skarels ttyflush(tp, FREAD | FWRITE); 14326121Skarels return (0); 14426121Skarels } 14526121Skarels 14626378Skarels return (ENXIO); 14726121Skarels } 14826121Skarels 14926121Skarels /* 15026121Skarels * Line specific close routine. 15126121Skarels * Detach the tty from the sl unit. 15226121Skarels * Mimics part of ttyclose(). 15326121Skarels */ 15426121Skarels slclose(tp) 15526121Skarels struct tty *tp; 15626121Skarels { 15726121Skarels register struct sl_softc *sc; 15826121Skarels int s; 15926121Skarels 16026121Skarels ttywflush(tp); 16126121Skarels tp->t_line = 0; 16226121Skarels s = splimp(); /* paranoid; splnet probably ok */ 16326121Skarels sc = (struct sl_softc *)tp->t_sc; 16426121Skarels if (sc != NULL) { 16526121Skarels if_down(&sc->sc_if); 16626121Skarels sc->sc_ttyp = NULL; 16726121Skarels tp->t_sc = NULL; 16826378Skarels MCLFREE((struct mbuf *)sc->sc_buf); 16926378Skarels sc->sc_buf = 0; 17026121Skarels } 17126121Skarels splx(s); 17226121Skarels } 17326121Skarels 17426121Skarels /* 17526121Skarels * Line specific (tty) ioctl routine. 17626121Skarels * Provide a way to get the sl unit number. 17726121Skarels */ 17826378Skarels /* ARGSUSED */ 17926121Skarels sltioctl(tp, cmd, data, flag) 18026121Skarels struct tty *tp; 18126121Skarels caddr_t data; 18226121Skarels { 18326121Skarels 18426121Skarels if (cmd == TIOCGETD) { 18526121Skarels *(int *)data = ((struct sl_softc *)tp->t_sc)->sc_if.if_unit; 18626121Skarels return (0); 18726121Skarels } 18826121Skarels return (-1); 18926121Skarels } 19026121Skarels 19126121Skarels /* 19226121Skarels * Queue a packet. Start transmission if not active. 19326121Skarels */ 19426121Skarels sloutput(ifp, m, dst) 19526121Skarels register struct ifnet *ifp; 19626121Skarels register struct mbuf *m; 19726121Skarels struct sockaddr *dst; 19826121Skarels { 19926121Skarels register struct sl_softc *sc; 20026121Skarels int s; 20126121Skarels 20226121Skarels /* 20326121Skarels * `Cannot happen' (see slioctl). Someday we will extend 20426121Skarels * the line protocol to support other address families. 20526121Skarels */ 20626121Skarels if (dst->sa_family != AF_INET) { 20726121Skarels printf("sl%d: af%d not supported\n", ifp->if_unit, 20826121Skarels dst->sa_family); 20926121Skarels m_freem(m); 21026121Skarels return (EAFNOSUPPORT); 21126121Skarels } 21226121Skarels 21326121Skarels sc = &sl_softc[ifp->if_unit]; 21426121Skarels if (sc->sc_ttyp == NULL) { 21526121Skarels m_freem(m); 21626121Skarels return (ENETDOWN); /* sort of */ 21726121Skarels } 21826899Skarels if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0) { 21926899Skarels m_freem(m); 22026899Skarels return (EHOSTUNREACH); 22126899Skarels } 22226121Skarels s = splimp(); 22326121Skarels if (IF_QFULL(&ifp->if_snd)) { 22426121Skarels IF_DROP(&ifp->if_snd); 22526121Skarels splx(s); 22626121Skarels m_freem(m); 22726378Skarels sc->sc_if.if_oerrors++; 22826121Skarels return (ENOBUFS); 22926121Skarels } 23026121Skarels IF_ENQUEUE(&ifp->if_snd, m); 23133740Skarels if (sc->sc_ttyp->t_outq.c_cc == 0) { 23226121Skarels splx(s); 23326121Skarels slstart(sc->sc_ttyp); 23426121Skarels } else 23526121Skarels splx(s); 23626121Skarels return (0); 23726121Skarels } 23826121Skarels 23926121Skarels /* 24026121Skarels * Start output on interface. Get another datagram 24126121Skarels * to send from the interface queue and map it to 24226121Skarels * the interface before starting output. 24326121Skarels */ 24426121Skarels slstart(tp) 24526121Skarels register struct tty *tp; 24626121Skarels { 24726121Skarels register struct sl_softc *sc = (struct sl_softc *)tp->t_sc; 24826121Skarels register struct mbuf *m; 24926378Skarels register int len; 25026378Skarels register u_char *cp; 25133740Skarels int nd, np, n, s; 25226378Skarels struct mbuf *m2; 25326378Skarels extern int cfreecount; 25426121Skarels 25526378Skarels for (;;) { 25626378Skarels /* 25726378Skarels * If there is more in the output queue, just send it now. 25826378Skarels * We are being called in lieu of ttstart and must do what 25926378Skarels * it would. 26026378Skarels */ 26126378Skarels if (tp->t_outq.c_cc > 0) 26226378Skarels ttstart(tp); 26326378Skarels if (tp->t_outq.c_cc > SLIP_HIWAT) 26426378Skarels return; 26526121Skarels 26626378Skarels /* 26726378Skarels * This happens briefly when the line shuts down. 26826378Skarels */ 26926378Skarels if (sc == NULL) 27026378Skarels return; 27126121Skarels 27226378Skarels /* 27326378Skarels * If system is getting low on clists 27426378Skarels * and we have something running already, stop here. 27526378Skarels */ 27634361Skarels if (cfreecount < CLISTRESERVE + SLMTU && tp->t_outq.c_cc) 27726378Skarels return; 27826121Skarels 27926378Skarels /* 28026378Skarels * Get a packet and send it to the interface. 28126378Skarels */ 28226378Skarels s = splimp(); 28326378Skarels IF_DEQUEUE(&sc->sc_if.if_snd, m); 28433740Skarels splx(s); 28533740Skarels if (m == NULL) 28626378Skarels return; 28726121Skarels 28826378Skarels /* 28926378Skarels * The extra FRAME_END will start up a new packet, and thus 29026378Skarels * will flush any accumulated garbage. We do this whenever 29126378Skarels * the line may have been idle for some time. 29226378Skarels */ 29333740Skarels if (tp->t_outq.c_cc == 0) 29426378Skarels (void) putc(FRAME_END, &tp->t_outq); 29526378Skarels 29626378Skarels while (m) { 29726378Skarels cp = mtod(m, u_char *); 29826378Skarels len = m->m_len; 29926378Skarels while (len > 0) { 30026378Skarels /* 30126378Skarels * Find out how many bytes in the string we can 30226378Skarels * handle without doing something special. 30326378Skarels */ 30426378Skarels nd = locc(FRAME_ESCAPE, len, cp); 30526378Skarels np = locc(FRAME_END, len, cp); 30626378Skarels n = len - MAX(nd, np); 30726378Skarels if (n) { 30826378Skarels /* 30926378Skarels * Put n characters at once 31026378Skarels * into the tty output queue. 31126378Skarels */ 31226378Skarels if (b_to_q((char *)cp, n, &tp->t_outq)) 31326378Skarels break; 31427688Smckusick len -= n; 31527688Smckusick cp += n; 31626121Skarels } 31726378Skarels /* 31826378Skarels * If there are characters left in the mbuf, 31926378Skarels * the first one must be special.. 32026378Skarels * Put it out in a different form. 32126378Skarels */ 32226378Skarels if (len) { 32326378Skarels if (putc(FRAME_ESCAPE, &tp->t_outq)) 32426378Skarels break; 32526378Skarels if (putc(*cp == FRAME_ESCAPE ? 32626378Skarels TRANS_FRAME_ESCAPE : TRANS_FRAME_END, 32726378Skarels &tp->t_outq)) { 32826378Skarels (void) unputc(&tp->t_outq); 32926378Skarels break; 33026378Skarels } 33126378Skarels cp++; 33226378Skarels len--; 33326378Skarels } 33426378Skarels } 33526378Skarels MFREE(m, m2); 33626378Skarels m = m2; 33726121Skarels } 33826378Skarels 33926378Skarels if (putc(FRAME_END, &tp->t_outq)) { 34026378Skarels /* 34126378Skarels * Not enough room. Remove a char to make room 34226378Skarels * and end the packet normally. 34326378Skarels * If you get many collisions (more than one or two 34426378Skarels * a day) you probably do not have enough clists 34526378Skarels * and you should increase "nclist" in param.c. 34626378Skarels */ 34726378Skarels (void) unputc(&tp->t_outq); 34826378Skarels (void) putc(FRAME_END, &tp->t_outq); 34926378Skarels sc->sc_if.if_collisions++; 35026378Skarels } else 35126378Skarels sc->sc_if.if_opackets++; 35226121Skarels } 35326378Skarels } 35426121Skarels 35526378Skarels slinit(sc) 35626378Skarels register struct sl_softc *sc; 35726378Skarels { 35826378Skarels struct mbuf *p; 35926121Skarels 36026378Skarels if (sc->sc_buf == (char *) 0) { 36126378Skarels MCLALLOC(p, 1); 36226378Skarels if (p) { 36326378Skarels sc->sc_buf = (char *)p; 36426378Skarels sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 36526378Skarels } else { 36626378Skarels printf("sl%d: can't allocate buffer\n", sc - sl_softc); 36726378Skarels sc->sc_if.if_flags &= ~IFF_UP; 36826378Skarels return (0); 36926378Skarels } 37026378Skarels } 37126378Skarels return (1); 37226121Skarels } 37326121Skarels 37426121Skarels /* 37526121Skarels * Copy data buffer to mbuf chain; add ifnet pointer ifp. 37626121Skarels */ 37726121Skarels struct mbuf * 37826378Skarels sl_btom(sc, len, ifp) 37926378Skarels struct sl_softc *sc; 38026121Skarels register int len; 38126121Skarels struct ifnet *ifp; 38226121Skarels { 38326378Skarels register caddr_t cp; 38426121Skarels register struct mbuf *m, **mp; 38526378Skarels register unsigned count; 38626121Skarels struct mbuf *top = NULL; 38726121Skarels 38826378Skarels cp = sc->sc_buf + sizeof(struct ifnet *); 38926121Skarels mp = ⊤ 39026121Skarels while (len > 0) { 39126121Skarels MGET(m, M_DONTWAIT, MT_DATA); 39226121Skarels if ((*mp = m) == NULL) { 39326121Skarels m_freem(top); 39426121Skarels return (NULL); 39526121Skarels } 39626378Skarels if (ifp) 39726121Skarels m->m_off += sizeof(ifp); 39826378Skarels /* 39926378Skarels * If we have at least NBPG bytes, 40026378Skarels * allocate a new page. Swap the current buffer page 40126378Skarels * with the new one. We depend on having a space 40226378Skarels * left at the beginning of the buffer 40326378Skarels * for the interface pointer. 40426378Skarels */ 40526378Skarels if (len >= NBPG) { 40626378Skarels MCLGET(m); 40733980Skarels if (m->m_len == MCLBYTES) { 40826378Skarels cp = mtod(m, char *); 40926378Skarels m->m_off = (int)sc->sc_buf - (int)m; 41028184Smckusick sc->sc_buf = cp; 41126378Skarels if (ifp) { 41226378Skarels m->m_off += sizeof(ifp); 41326378Skarels count = MIN(len, 41433980Skarels MCLBYTES - sizeof(struct ifnet *)); 41526378Skarels } else 41633980Skarels count = MIN(len, MCLBYTES); 41726378Skarels goto nocopy; 41826378Skarels } 41926121Skarels } 42026378Skarels if (ifp) 42126378Skarels count = MIN(len, MLEN - sizeof(ifp)); 42226378Skarels else 42326378Skarels count = MIN(len, MLEN); 42426378Skarels bcopy(cp, mtod(m, caddr_t), count); 42526378Skarels nocopy: 42626121Skarels m->m_len = count; 42726121Skarels if (ifp) { 42826121Skarels m->m_off -= sizeof(ifp); 42926121Skarels m->m_len += sizeof(ifp); 43026121Skarels *mtod(m, struct ifnet **) = ifp; 43126121Skarels ifp = NULL; 43226121Skarels } 43326378Skarels cp += count; 43426121Skarels len -= count; 43526121Skarels mp = &m->m_next; 43626121Skarels } 43726121Skarels return (top); 43826121Skarels } 43926121Skarels 44026121Skarels /* 44126121Skarels * tty interface receiver interrupt. 44226121Skarels */ 44326121Skarels slinput(c, tp) 44426121Skarels register int c; 44526121Skarels register struct tty *tp; 44626121Skarels { 44726121Skarels register struct sl_softc *sc; 44826121Skarels register struct mbuf *m; 44926121Skarels int s; 45026121Skarels 45126378Skarels tk_nin++; 45226121Skarels sc = (struct sl_softc *)tp->t_sc; 45326121Skarels if (sc == NULL) 45426121Skarels return; 45526121Skarels 45626121Skarels c &= 0xff; 45726121Skarels if (sc->sc_flags & SC_ESCAPED) { 45826121Skarels sc->sc_flags &= ~SC_ESCAPED; 45926121Skarels switch (c) { 46026121Skarels 46126121Skarels case TRANS_FRAME_ESCAPE: 46226121Skarels c = FRAME_ESCAPE; 46326121Skarels break; 46426121Skarels 46526121Skarels case TRANS_FRAME_END: 46626121Skarels c = FRAME_END; 46726121Skarels break; 46826121Skarels 46926121Skarels default: 47026121Skarels sc->sc_if.if_ierrors++; 47126378Skarels sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 47226121Skarels sc->sc_ilen = 0; 47326121Skarels return; 47426121Skarels } 47526121Skarels } else { 47626121Skarels switch (c) { 47726121Skarels 47826121Skarels case FRAME_END: 47926121Skarels if (sc->sc_ilen == 0) /* ignore */ 48026121Skarels return; 48126378Skarels m = sl_btom(sc, sc->sc_ilen, &sc->sc_if); 48226121Skarels if (m == NULL) { 48326121Skarels sc->sc_if.if_ierrors++; 48426121Skarels return; 48526121Skarels } 48626378Skarels sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 48726121Skarels sc->sc_ilen = 0; 48826121Skarels sc->sc_if.if_ipackets++; 48926121Skarels s = splimp(); 49026121Skarels if (IF_QFULL(&ipintrq)) { 49126121Skarels IF_DROP(&ipintrq); 49226121Skarels sc->sc_if.if_ierrors++; 49326121Skarels m_freem(m); 49426121Skarels } else { 49526121Skarels IF_ENQUEUE(&ipintrq, m); 49626121Skarels schednetisr(NETISR_IP); 49726121Skarels } 49826121Skarels splx(s); 49926121Skarels return; 50026121Skarels 50126121Skarels case FRAME_ESCAPE: 50226121Skarels sc->sc_flags |= SC_ESCAPED; 50326121Skarels return; 50426121Skarels } 50526121Skarels } 50628200Skarels if (++sc->sc_ilen > SLMTU) { 50726121Skarels sc->sc_if.if_ierrors++; 50826378Skarels sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 50926121Skarels sc->sc_ilen = 0; 51026121Skarels return; 51126121Skarels } 51226121Skarels *sc->sc_mp++ = c; 51326121Skarels } 51426121Skarels 51526121Skarels /* 51626121Skarels * Process an ioctl request. 51726121Skarels */ 51826121Skarels slioctl(ifp, cmd, data) 51926121Skarels register struct ifnet *ifp; 52026121Skarels int cmd; 52126121Skarels caddr_t data; 52226121Skarels { 52326121Skarels register struct ifaddr *ifa = (struct ifaddr *)data; 52426121Skarels int s = splimp(), error = 0; 52526121Skarels 52626121Skarels switch (cmd) { 52726121Skarels 52826121Skarels case SIOCSIFADDR: 52926121Skarels if (ifa->ifa_addr.sa_family == AF_INET) 53026121Skarels ifp->if_flags |= IFF_UP; 53126121Skarels else 53226121Skarels error = EAFNOSUPPORT; 53326121Skarels break; 53426121Skarels 53526121Skarels case SIOCSIFDSTADDR: 53626121Skarels if (ifa->ifa_addr.sa_family != AF_INET) 53726121Skarels error = EAFNOSUPPORT; 53826121Skarels break; 53926121Skarels 54026121Skarels default: 54126121Skarels error = EINVAL; 54226121Skarels } 54326121Skarels splx(s); 54426121Skarels return (error); 54526121Skarels } 54626121Skarels #endif 547