1: /* 2: * Copyright (c) 1987 Regents of the University of California. 3: * All rights reserved. 4: * 5: * Redistribution and use in source and binary forms are permitted 6: * provided that this notice is preserved and that due credit is given 7: * to the University of California at Berkeley. The name of the University 8: * may not be used to endorse or promote products derived from this 9: * software without specific prior written permission. This software 10: * is provided ``as is'' without express or implied warranty. 11: * 12: * @(#)if_sl.c 7.6.1.1 (Berkeley) 3/15/88 13: */ 14: 15: /* 16: * Serial Line interface 17: * 18: * Rick Adams 19: * Center for Seismic Studies 20: * 1300 N 17th Street, Suite 1450 21: * Arlington, Virginia 22209 22: * (703)276-7900 23: * rick@seismo.ARPA 24: * seismo!rick 25: * 26: * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris). 27: * N.B.: this belongs in netinet, not net, the way it stands now. 28: * Should have a link-layer type designation, but wouldn't be 29: * backwards-compatible. 30: * 31: * Converted to 4.3BSD Beta by Chris Torek. 32: * Other changes made at Berkeley, based in part on code by Kirk Smith. 33: */ 34: 35: /* $Header: /usr/src/sys/net/RCS/if_sl.c,v 1.1 88/08/18 00:28:00 bin Exp Locker: bin $ */ 36: /* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */ 37: 38: #include "sl.h" 39: #if NSL > 0 40: 41: #include "param.h" 42: #include "mbuf.h" 43: #include "buf.h" 44: #include "dk.h" 45: #include "domain.h" 46: #include "protosw.h" 47: #include "socket.h" 48: #include "ioctl.h" 49: #include "file.h" 50: #include "tty.h" 51: #include "errno.h" 52: 53: #include "if.h" 54: #include "netisr.h" 55: #include "route.h" 56: #if INET 57: #include "../netinet/in.h" 58: #include "../netinet/in_systm.h" 59: #include "../netinet/in_var.h" 60: #include "../netinet/ip.h" 61: #endif 62: 63: /* 64: * N.B.: SLMTU is now a hard limit on input packet size. 65: * SLMTU must be <= CLBYTES - sizeof(struct ifnet *). 66: */ 67: #define SLMTU 1006 68: #define SLIP_HIWAT 500 /* don't start a new packet if HIWAT on queue */ 69: #define CLISTRESERVE 200 /* Can't let clists get too low */ 70: 71: struct sl_softc { 72: struct ifnet sc_if; /* network-visible interface */ 73: short sc_flags; /* see below */ 74: short sc_ilen; /* length of input-packet-so-far */ 75: struct tty *sc_ttyp; /* pointer to tty structure */ 76: char *sc_mp; /* pointer to next available buf char */ 77: char *sc_buf; /* input buffer */ 78: } sl_softc[NSL]; 79: 80: /* flags */ 81: #define SC_ESCAPED 0x0001 /* saw a FRAME_ESCAPE */ 82: 83: #define FRAME_END 0300 /* Frame End */ 84: #define FRAME_ESCAPE 0333 /* Frame Esc */ 85: #define TRANS_FRAME_END 0334 /* transposed frame end */ 86: #define TRANS_FRAME_ESCAPE 0335 /* transposed frame esc */ 87: 88: #define t_sc T_LINEP 89: 90: int sloutput(), slioctl(), slopen(), sltioctl(); 91: 92: /* 93: * Called from boot code to establish sl interfaces. 94: */ 95: slattach() 96: { 97: register struct sl_softc *sc; 98: register int i = 0; 99: 100: for (sc = sl_softc; i < NSL; sc++) { 101: sc->sc_if.if_name = "sl"; 102: sc->sc_if.if_unit = i++; 103: sc->sc_if.if_mtu = SLMTU; 104: sc->sc_if.if_flags = IFF_POINTOPOINT; 105: sc->sc_if.if_ioctl = slioctl; 106: sc->sc_if.if_output = sloutput; 107: sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN; 108: if_attach(&sc->sc_if); 109: } 110: } 111: 112: /* 113: * Line specific open routine. 114: * Attach the given tty to the first available sl unit. 115: */ 116: /* ARGSUSED */ 117: slopen(dev, tp) 118: dev_t dev; 119: register struct tty *tp; 120: { 121: struct sl_softc *sc; 122: register int nsl; 123: 124: #ifdef DONE_IN_KERNEL_STUB 125: if (!suser()) 126: return (EPERM); 127: if (tp->t_line == SLIPDISC) 128: return (EBUSY); 129: #endif 130: 131: for (nsl = 0, sc = sl_softc; nsl < NSL; nsl++, sc++) 132: if (sc->sc_ttyp == NULL) { 133: sc->sc_flags = 0; 134: sc->sc_ilen = 0; 135: if (slinit(sc) == 0) 136: return (ENOBUFS); 137: mtkd(&tp->t_sc, sc); 138: sc->sc_ttyp = tp; 139: #ifdef DONE_IN_KERNEL_STUB 140: ttyflush(tp, FREAD | FWRITE); 141: #endif 142: return (0); 143: } 144: 145: return (ENXIO); 146: } 147: 148: /* 149: * Line specific close routine. 150: * Detach the tty from the sl unit. 151: * Mimics part of ttyclose(). 152: */ 153: slclose(tp) 154: struct tty *tp; 155: { 156: register struct sl_softc *sc; 157: int s; 158: 159: #ifdef DONE_IN_KERNEL_STUB 160: ttywflush(tp); 161: tp->t_line = 0; 162: #endif 163: s = splimp(); /* paranoid; splnet probably ok */ 164: sc = (struct sl_softc *)mfkd(&tp->t_sc); 165: if (sc != NULL) { 166: if_down(&sc->sc_if); 167: sc->sc_ttyp = NULL; 168: mtkd(&tp->t_sc, 0); 169: MCLFREE((struct mbuf *)sc->sc_buf); 170: sc->sc_buf = 0; 171: } 172: splx(s); 173: } 174: 175: /* 176: * Line specific (tty) ioctl routine. 177: * Provide a way to get the sl unit number. 178: */ 179: /* ARGSUSED */ 180: sltioctl(tp, cmd, data, flag) 181: struct tty *tp; 182: caddr_t data; 183: { 184: 185: if (cmd == TIOCGETD) { 186: mtkd(data, 187: ((struct sl_softc *)mfkd(&tp->t_sc))->sc_if.if_unit); 188: return (0); 189: } 190: return (-1); 191: } 192: 193: /* 194: * Queue a packet. Start transmission if not active. 195: */ 196: sloutput(ifp, m, dst) 197: register struct ifnet *ifp; 198: register struct mbuf *m; 199: struct sockaddr *dst; 200: { 201: register struct sl_softc *sc; 202: long lval; 203: int s; 204: 205: /* 206: * `Cannot happen' (see slioctl). Someday we will extend 207: * the line protocol to support other address families. 208: */ 209: if (dst->sa_family != AF_INET) { 210: printf("sl%d: af%d not supported\n", ifp->if_unit, 211: dst->sa_family); 212: m_freem(m); 213: return (EAFNOSUPPORT); 214: } 215: 216: sc = &sl_softc[ifp->if_unit]; 217: if (sc->sc_ttyp == NULL) { 218: m_freem(m); 219: return (ENETDOWN); /* sort of */ 220: } 221: cpfromkern(&sc->sc_ttyp->t_state, &lval, 4); 222: if ((lval & TS_CARR_ON) == 0) { 223: m_freem(m); 224: return (EHOSTUNREACH); 225: } 226: s = splimp(); 227: if (IF_QFULL(&ifp->if_snd)) { 228: IF_DROP(&ifp->if_snd); 229: splx(s); 230: m_freem(m); 231: sc->sc_if.if_oerrors++; 232: return (ENOBUFS); 233: } 234: IF_ENQUEUE(&ifp->if_snd, m); 235: if (mfkd(&sc->sc_ttyp->t_outq.c_cc) == 0) { 236: splx(s); 237: slstart(sc->sc_ttyp); 238: } else 239: splx(s); 240: return (0); 241: } 242: 243: /* 244: * Start output on interface. Get another datagram 245: * to send from the interface queue and map it to 246: * the interface before starting output. 247: */ 248: slstart(tp) 249: register struct tty *tp; 250: { 251: register struct sl_softc *sc; 252: register struct mbuf *m; 253: register int len; 254: register u_char *cp; 255: int nd, np, n, s, t, c_cc; 256: char sup_clist[MLEN]; 257: struct mbuf *m2; 258: extern int cfreecount; 259: 260: sc = (struct sl_softc *)mfkd(&tp->t_sc); 261: for (;;) { 262: /* 263: * If there is more in the output queue, just send it now. 264: * We are being called in lieu of ttstart and must do what 265: * it would. 266: */ 267: if ((c_cc = mfkd(&tp->t_outq.c_cc)) > 0) 268: TTSTART(tp); 269: if (c_cc > SLIP_HIWAT) 270: return; 271: 272: /* 273: * This happens briefly when the line shuts down. 274: */ 275: if (sc == NULL) 276: return; 277: 278: /* 279: * If system is getting low on clists 280: * and we have something running already, stop here. 281: */ 282: if (mfkd(&cfreecount) < CLISTRESERVE + SLMTU && c_cc) 283: return; 284: 285: /* 286: * Get a packet and send it to the interface. 287: */ 288: s = splimp(); 289: IF_DEQUEUE(&sc->sc_if.if_snd, m); 290: splx(s); 291: if (m == NULL) 292: return; 293: 294: /* 295: * The extra FRAME_END will start up a new packet, and thus 296: * will flush any accumulated garbage. We do this whenever 297: * the line may have been idle for some time. 298: */ 299: if (c_cc == 0) 300: (void) PUTC(FRAME_END, &tp->t_outq); 301: 302: while (m) { 303: cp = mtod(m, u_char *); 304: len = m->m_len; 305: while (len > 0) { 306: /* 307: * Find out how many bytes in the string we can 308: * handle without doing something special. 309: */ 310: nd = locc(FRAME_ESCAPE, len, cp); 311: np = locc(FRAME_END, len, cp); 312: n = len - MAX(nd, np); 313: /* 314: * Put n characters into the tty output 315: * queue. We do it in chunks of MLEN, using 316: * the supervisor stack to copy into kernel 317: * space. 318: */ 319: while (n) { 320: t = n > MLEN ? MLEN : n; 321: bcopy((char *)cp, sup_clist, t); 322: if (B_TO_Q(sup_clist, t, &tp->t_outq)) 323: break; 324: len -= t; 325: cp += t; 326: n -= t; 327: } 328: if (n) 329: break; 330: /* 331: * If there are characters left in the mbuf, 332: * the first one must be special.. 333: * Put it out in a different form. 334: */ 335: if (len) { 336: if (PUTC(FRAME_ESCAPE, &tp->t_outq)) 337: break; 338: if (PUTC(*cp == FRAME_ESCAPE ? 339: TRANS_FRAME_ESCAPE : TRANS_FRAME_END, 340: &tp->t_outq)) { 341: (void) UNPUTC(&tp->t_outq); 342: break; 343: } 344: cp++; 345: len--; 346: } 347: } 348: MFREE(m, m2); 349: m = m2; 350: } 351: 352: if (PUTC(FRAME_END, &tp->t_outq)) { 353: /* 354: * Not enough room. Remove a char to make room 355: * and end the packet normally. 356: * If you get many collisions (more than one or two 357: * a day) you probably do not have enough clists 358: * and you should increase "nclist" in param.c. 359: */ 360: (void) UNPUTC(&tp->t_outq); 361: (void) PUTC(FRAME_END, &tp->t_outq); 362: sc->sc_if.if_collisions++; 363: } else 364: sc->sc_if.if_opackets++; 365: } 366: } 367: 368: slinit(sc) 369: register struct sl_softc *sc; 370: { 371: struct mbuf *p; 372: 373: if (sc->sc_buf == (char *) 0) { 374: MCLALLOC(p, 1); 375: if (p) { 376: sc->sc_buf = (char *)p; 377: sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 378: } else { 379: printf("sl%d: can't allocate buffer\n", sc - sl_softc); 380: sc->sc_if.if_flags &= ~IFF_UP; 381: return (0); 382: } 383: } 384: return (1); 385: } 386: 387: /* 388: * Copy data buffer to mbuf chain; add ifnet pointer ifp. 389: */ 390: struct mbuf * 391: sl_btom(sc, len, ifp) 392: struct sl_softc *sc; 393: register int len; 394: struct ifnet *ifp; 395: { 396: register caddr_t cp; 397: register struct mbuf *m, **mp; 398: register unsigned count; 399: struct mbuf *top = NULL; 400: 401: cp = sc->sc_buf + sizeof(struct ifnet *); 402: mp = ⊤ 403: while (len > 0) { 404: MGET(m, M_DONTWAIT, MT_DATA); 405: if ((*mp = m) == NULL) { 406: m_freem(top); 407: return (NULL); 408: } 409: if (ifp) 410: m->m_off += sizeof(ifp); 411: /* 412: * If we have at least NBPG bytes, 413: * allocate a new page. Swap the current buffer page 414: * with the new one. We depend on having a space 415: * left at the beginning of the buffer 416: * for the interface pointer. 417: */ 418: if (len >= NBPG) { 419: MCLGET(m); 420: if (m->m_len == CLBYTES) { 421: cp = mtod(m, char *); 422: m->m_off = (int)sc->sc_buf - (int)m; 423: sc->sc_buf = cp; 424: if (ifp) { 425: m->m_off += sizeof(ifp); 426: count = MIN(len, 427: CLBYTES - sizeof(struct ifnet *)); 428: } else 429: count = MIN(len, CLBYTES); 430: goto nocopy; 431: } 432: } 433: if (ifp) 434: count = MIN(len, MLEN - sizeof(ifp)); 435: else 436: count = MIN(len, MLEN); 437: bcopy(cp, mtod(m, caddr_t), count); 438: nocopy: 439: m->m_len = count; 440: if (ifp) { 441: m->m_off -= sizeof(ifp); 442: m->m_len += sizeof(ifp); 443: *mtod(m, struct ifnet **) = ifp; 444: ifp = NULL; 445: } 446: cp += count; 447: len -= count; 448: mp = &m->m_next; 449: } 450: return (top); 451: } 452: 453: /* 454: * tty interface receiver interrupt. 455: */ 456: slinput(c, tp) 457: register int c; 458: register struct tty *tp; 459: { 460: register struct sl_softc *sc; 461: register struct mbuf *m; 462: int s; 463: 464: #ifdef notdef 465: tk_nin++; 466: #endif 467: sc = (struct sl_softc *)mfkd(&tp->t_sc); 468: if (sc == NULL) 469: return; 470: 471: c &= 0xff; 472: if (sc->sc_flags & SC_ESCAPED) { 473: sc->sc_flags &= ~SC_ESCAPED; 474: switch (c) { 475: 476: case TRANS_FRAME_ESCAPE: 477: c = FRAME_ESCAPE; 478: break; 479: 480: case TRANS_FRAME_END: 481: c = FRAME_END; 482: break; 483: 484: default: 485: sc->sc_if.if_ierrors++; 486: sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 487: sc->sc_ilen = 0; 488: return; 489: } 490: } else { 491: switch (c) { 492: 493: case FRAME_END: 494: if (sc->sc_ilen == 0) /* ignore */ 495: return; 496: m = sl_btom(sc, sc->sc_ilen, &sc->sc_if); 497: if (m == NULL) { 498: sc->sc_if.if_ierrors++; 499: return; 500: } 501: sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 502: sc->sc_ilen = 0; 503: sc->sc_if.if_ipackets++; 504: s = splimp(); 505: if (IF_QFULL(&ipintrq)) { 506: IF_DROP(&ipintrq); 507: sc->sc_if.if_ierrors++; 508: m_freem(m); 509: } else { 510: IF_ENQUEUE(&ipintrq, m); 511: schednetisr(NETISR_IP); 512: } 513: splx(s); 514: return; 515: 516: case FRAME_ESCAPE: 517: sc->sc_flags |= SC_ESCAPED; 518: return; 519: } 520: } 521: if (++sc->sc_ilen > SLMTU) { 522: sc->sc_if.if_ierrors++; 523: sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 524: sc->sc_ilen = 0; 525: return; 526: } 527: *sc->sc_mp++ = c; 528: } 529: 530: /* 531: * Process an ioctl request. 532: */ 533: slioctl(ifp, cmd, data) 534: register struct ifnet *ifp; 535: int cmd; 536: caddr_t data; 537: { 538: register struct ifaddr *ifa = (struct ifaddr *)data; 539: int s = splimp(), error = 0; 540: 541: switch (cmd) { 542: 543: case SIOCSIFADDR: 544: if (ifa->ifa_addr.sa_family == AF_INET) 545: ifp->if_flags |= IFF_UP; 546: else 547: error = EAFNOSUPPORT; 548: break; 549: 550: case SIOCSIFDSTADDR: 551: if (ifa->ifa_addr.sa_family != AF_INET) 552: error = EAFNOSUPPORT; 553: break; 554: 555: default: 556: error = EINVAL; 557: } 558: splx(s); 559: return (error); 560: } 561: #endif