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