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.5 (Berkeley) 12/30/87 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: if_sl.c,v 1.12 85/12/20 21:54:55 chris Exp $ */ 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 "dkstat.h" 45 #include "socket.h" 46 #include "ioctl.h" 47 #include "file.h" 48 #include "tty.h" 49 #include "errno.h" 50 51 #include "if.h" 52 #include "netisr.h" 53 #include "route.h" 54 #if INET 55 #include "../netinet/in.h" 56 #include "../netinet/in_systm.h" 57 #include "../netinet/in_var.h" 58 #include "../netinet/ip.h" 59 #endif 60 61 #include "../machine/mtpr.h" 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 1000 /* don't start a new packet if HIWAT on queue */ 69 #define CLISTRESERVE 1000 /* 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 #define SC_OACTIVE 0x0002 /* output tty is active */ 83 84 #define FRAME_END 0300 /* Frame End */ 85 #define FRAME_ESCAPE 0333 /* Frame Esc */ 86 #define TRANS_FRAME_END 0334 /* transposed frame end */ 87 #define TRANS_FRAME_ESCAPE 0335 /* transposed frame esc */ 88 89 #define t_sc T_LINEP 90 91 int sloutput(), slioctl(), ttrstrt(); 92 93 /* 94 * Called from boot code to establish sl interfaces. 95 */ 96 slattach() 97 { 98 register struct sl_softc *sc; 99 register int i = 0; 100 101 for (sc = sl_softc; i < NSL; sc++) { 102 sc->sc_if.if_name = "sl"; 103 sc->sc_if.if_unit = i++; 104 sc->sc_if.if_mtu = SLMTU; 105 sc->sc_if.if_flags = IFF_POINTOPOINT; 106 sc->sc_if.if_ioctl = slioctl; 107 sc->sc_if.if_output = sloutput; 108 sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN; 109 if_attach(&sc->sc_if); 110 } 111 } 112 113 /* 114 * Line specific open routine. 115 * Attach the given tty to the first available sl unit. 116 */ 117 /* ARGSUSED */ 118 slopen(dev, tp) 119 dev_t dev; 120 register struct tty *tp; 121 { 122 register struct sl_softc *sc; 123 register int nsl; 124 125 if (!suser()) 126 return (EPERM); 127 if (tp->t_line == SLIPDISC) 128 return (EBUSY); 129 130 for (nsl = 0, sc = sl_softc; nsl < NSL; nsl++, sc++) 131 if (sc->sc_ttyp == NULL) { 132 sc->sc_flags = 0; 133 sc->sc_ilen = 0; 134 if (slinit(sc) == 0) 135 return (ENOBUFS); 136 tp->t_sc = (caddr_t)sc; 137 sc->sc_ttyp = tp; 138 ttyflush(tp, FREAD | FWRITE); 139 return (0); 140 } 141 142 return (ENXIO); 143 } 144 145 /* 146 * Line specific close routine. 147 * Detach the tty from the sl unit. 148 * Mimics part of ttyclose(). 149 */ 150 slclose(tp) 151 struct tty *tp; 152 { 153 register struct sl_softc *sc; 154 int s; 155 156 ttywflush(tp); 157 tp->t_line = 0; 158 s = splimp(); /* paranoid; splnet probably ok */ 159 sc = (struct sl_softc *)tp->t_sc; 160 if (sc != NULL) { 161 if_down(&sc->sc_if); 162 sc->sc_ttyp = NULL; 163 tp->t_sc = NULL; 164 MCLFREE((struct mbuf *)sc->sc_buf); 165 sc->sc_buf = 0; 166 } 167 splx(s); 168 } 169 170 /* 171 * Line specific (tty) ioctl routine. 172 * Provide a way to get the sl unit number. 173 */ 174 /* ARGSUSED */ 175 sltioctl(tp, cmd, data, flag) 176 struct tty *tp; 177 caddr_t data; 178 { 179 180 if (cmd == TIOCGETD) { 181 *(int *)data = ((struct sl_softc *)tp->t_sc)->sc_if.if_unit; 182 return (0); 183 } 184 return (-1); 185 } 186 187 /* 188 * Queue a packet. Start transmission if not active. 189 */ 190 sloutput(ifp, m, dst) 191 register struct ifnet *ifp; 192 register struct mbuf *m; 193 struct sockaddr *dst; 194 { 195 register struct sl_softc *sc; 196 int s; 197 198 /* 199 * `Cannot happen' (see slioctl). Someday we will extend 200 * the line protocol to support other address families. 201 */ 202 if (dst->sa_family != AF_INET) { 203 printf("sl%d: af%d not supported\n", ifp->if_unit, 204 dst->sa_family); 205 m_freem(m); 206 return (EAFNOSUPPORT); 207 } 208 209 sc = &sl_softc[ifp->if_unit]; 210 if (sc->sc_ttyp == NULL) { 211 m_freem(m); 212 return (ENETDOWN); /* sort of */ 213 } 214 if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0) { 215 m_freem(m); 216 return (EHOSTUNREACH); 217 } 218 s = splimp(); 219 if (IF_QFULL(&ifp->if_snd)) { 220 IF_DROP(&ifp->if_snd); 221 splx(s); 222 m_freem(m); 223 sc->sc_if.if_oerrors++; 224 return (ENOBUFS); 225 } 226 IF_ENQUEUE(&ifp->if_snd, m); 227 if ((sc->sc_flags & SC_OACTIVE) == 0) { 228 splx(s); 229 slstart(sc->sc_ttyp); 230 } else 231 splx(s); 232 return (0); 233 } 234 235 /* 236 * Start output on interface. Get another datagram 237 * to send from the interface queue and map it to 238 * the interface before starting output. 239 */ 240 slstart(tp) 241 register struct tty *tp; 242 { 243 register struct sl_softc *sc = (struct sl_softc *)tp->t_sc; 244 register struct mbuf *m; 245 register int len; 246 register u_char *cp; 247 int flush, nd, np, n, s; 248 struct mbuf *m2; 249 extern int cfreecount; 250 251 for (;;) { 252 /* 253 * If there is more in the output queue, just send it now. 254 * We are being called in lieu of ttstart and must do what 255 * it would. 256 */ 257 if (tp->t_outq.c_cc > 0) 258 ttstart(tp); 259 if (tp->t_outq.c_cc > SLIP_HIWAT) 260 return; 261 262 /* 263 * This happens briefly when the line shuts down. 264 */ 265 if (sc == NULL) 266 return; 267 268 /* 269 * If system is getting low on clists 270 * and we have something running already, stop here. 271 */ 272 if (cfreecount < CLISTRESERVE + SLMTU && 273 sc->sc_flags & SC_OACTIVE) 274 return; 275 276 /* 277 * Get a packet and send it to the interface. 278 */ 279 s = splimp(); 280 IF_DEQUEUE(&sc->sc_if.if_snd, m); 281 if (m == NULL) { 282 if (tp->t_outq.c_cc == 0) 283 sc->sc_flags &= ~SC_OACTIVE; 284 splx(s); 285 return; 286 } 287 flush = !(sc->sc_flags & SC_OACTIVE); 288 sc->sc_flags |= SC_OACTIVE; 289 splx(s); 290 291 /* 292 * The extra FRAME_END will start up a new packet, and thus 293 * will flush any accumulated garbage. We do this whenever 294 * the line may have been idle for some time. 295 */ 296 if (flush) 297 (void) putc(FRAME_END, &tp->t_outq); 298 299 while (m) { 300 cp = mtod(m, u_char *); 301 len = m->m_len; 302 while (len > 0) { 303 /* 304 * Find out how many bytes in the string we can 305 * handle without doing something special. 306 */ 307 nd = locc(FRAME_ESCAPE, len, cp); 308 np = locc(FRAME_END, len, cp); 309 n = len - MAX(nd, np); 310 if (n) { 311 /* 312 * Put n characters at once 313 * into the tty output queue. 314 */ 315 if (b_to_q((char *)cp, n, &tp->t_outq)) 316 break; 317 len -= n; 318 cp += n; 319 } 320 /* 321 * If there are characters left in the mbuf, 322 * the first one must be special.. 323 * Put it out in a different form. 324 */ 325 if (len) { 326 if (putc(FRAME_ESCAPE, &tp->t_outq)) 327 break; 328 if (putc(*cp == FRAME_ESCAPE ? 329 TRANS_FRAME_ESCAPE : TRANS_FRAME_END, 330 &tp->t_outq)) { 331 (void) unputc(&tp->t_outq); 332 break; 333 } 334 cp++; 335 len--; 336 } 337 } 338 MFREE(m, m2); 339 m = m2; 340 } 341 342 if (putc(FRAME_END, &tp->t_outq)) { 343 /* 344 * Not enough room. Remove a char to make room 345 * and end the packet normally. 346 * If you get many collisions (more than one or two 347 * a day) you probably do not have enough clists 348 * and you should increase "nclist" in param.c. 349 */ 350 (void) unputc(&tp->t_outq); 351 (void) putc(FRAME_END, &tp->t_outq); 352 sc->sc_if.if_collisions++; 353 } else 354 sc->sc_if.if_opackets++; 355 } 356 } 357 358 slinit(sc) 359 register struct sl_softc *sc; 360 { 361 struct mbuf *p; 362 363 if (sc->sc_buf == (char *) 0) { 364 int s = splimp(); 365 366 MCLALLOC(p, 1); 367 splx(s); 368 if (p) { 369 sc->sc_buf = (char *)p; 370 sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 371 } else { 372 printf("sl%d: can't allocate buffer\n", sc - sl_softc); 373 sc->sc_if.if_flags &= ~IFF_UP; 374 return (0); 375 } 376 } 377 return (1); 378 } 379 380 /* 381 * Copy data buffer to mbuf chain; add ifnet pointer ifp. 382 */ 383 struct mbuf * 384 sl_btom(sc, len, ifp) 385 struct sl_softc *sc; 386 register int len; 387 struct ifnet *ifp; 388 { 389 register caddr_t cp; 390 register struct mbuf *m, **mp; 391 register unsigned count; 392 struct mbuf *top = NULL; 393 394 cp = sc->sc_buf + sizeof(struct ifnet *); 395 mp = ⊤ 396 while (len > 0) { 397 MGET(m, M_DONTWAIT, MT_DATA); 398 if ((*mp = m) == NULL) { 399 m_freem(top); 400 return (NULL); 401 } 402 if (ifp) 403 m->m_off += sizeof(ifp); 404 /* 405 * If we have at least NBPG bytes, 406 * allocate a new page. Swap the current buffer page 407 * with the new one. We depend on having a space 408 * left at the beginning of the buffer 409 * for the interface pointer. 410 */ 411 if (len >= NBPG) { 412 MCLGET(m); 413 if (m->m_len == CLBYTES) { 414 cp = mtod(m, char *); 415 m->m_off = (int)sc->sc_buf - (int)m; 416 sc->sc_buf = cp; 417 if (ifp) { 418 m->m_off += sizeof(ifp); 419 count = MIN(len, 420 CLBYTES - sizeof(struct ifnet *)); 421 } else 422 count = MIN(len, CLBYTES); 423 goto nocopy; 424 } 425 } 426 if (ifp) 427 count = MIN(len, MLEN - sizeof(ifp)); 428 else 429 count = MIN(len, MLEN); 430 bcopy(cp, mtod(m, caddr_t), count); 431 nocopy: 432 m->m_len = count; 433 if (ifp) { 434 m->m_off -= sizeof(ifp); 435 m->m_len += sizeof(ifp); 436 *mtod(m, struct ifnet **) = ifp; 437 ifp = NULL; 438 } 439 cp += count; 440 len -= count; 441 mp = &m->m_next; 442 } 443 return (top); 444 } 445 446 /* 447 * tty interface receiver interrupt. 448 */ 449 slinput(c, tp) 450 register int c; 451 register struct tty *tp; 452 { 453 register struct sl_softc *sc; 454 register struct mbuf *m; 455 int s; 456 457 tk_nin++; 458 sc = (struct sl_softc *)tp->t_sc; 459 if (sc == NULL) 460 return; 461 462 c &= 0xff; 463 if (sc->sc_flags & SC_ESCAPED) { 464 sc->sc_flags &= ~SC_ESCAPED; 465 switch (c) { 466 467 case TRANS_FRAME_ESCAPE: 468 c = FRAME_ESCAPE; 469 break; 470 471 case TRANS_FRAME_END: 472 c = FRAME_END; 473 break; 474 475 default: 476 sc->sc_if.if_ierrors++; 477 sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 478 sc->sc_ilen = 0; 479 return; 480 } 481 } else { 482 switch (c) { 483 484 case FRAME_END: 485 if (sc->sc_ilen == 0) /* ignore */ 486 return; 487 m = sl_btom(sc, sc->sc_ilen, &sc->sc_if); 488 if (m == NULL) { 489 sc->sc_if.if_ierrors++; 490 return; 491 } 492 sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 493 sc->sc_ilen = 0; 494 sc->sc_if.if_ipackets++; 495 s = splimp(); 496 if (IF_QFULL(&ipintrq)) { 497 IF_DROP(&ipintrq); 498 sc->sc_if.if_ierrors++; 499 m_freem(m); 500 } else { 501 IF_ENQUEUE(&ipintrq, m); 502 schednetisr(NETISR_IP); 503 } 504 splx(s); 505 return; 506 507 case FRAME_ESCAPE: 508 sc->sc_flags |= SC_ESCAPED; 509 return; 510 } 511 } 512 if (++sc->sc_ilen > SLMTU) { 513 sc->sc_if.if_ierrors++; 514 sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 515 sc->sc_ilen = 0; 516 return; 517 } 518 *sc->sc_mp++ = c; 519 } 520 521 /* 522 * Process an ioctl request. 523 */ 524 slioctl(ifp, cmd, data) 525 register struct ifnet *ifp; 526 int cmd; 527 caddr_t data; 528 { 529 register struct ifaddr *ifa = (struct ifaddr *)data; 530 int s = splimp(), error = 0; 531 532 switch (cmd) { 533 534 case SIOCSIFADDR: 535 if (ifa->ifa_addr.sa_family == AF_INET) 536 ifp->if_flags |= IFF_UP; 537 else 538 error = EAFNOSUPPORT; 539 break; 540 541 case SIOCSIFDSTADDR: 542 if (ifa->ifa_addr.sa_family != AF_INET) 543 error = EAFNOSUPPORT; 544 break; 545 546 default: 547 error = EINVAL; 548 } 549 splx(s); 550 return (error); 551 } 552 #endif 553