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