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.11 (Berkeley) 04/22/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 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 } 396 if ((*mp = m) == NULL) { 397 m_freem(top); 398 return (NULL); 399 } 400 if (top == NULL) { 401 m->m_pkthdr.rcvif = ifp; 402 m->m_pkthdr.len = len; 403 m->m_len = MHLEN; 404 } else 405 m->m_len = MLEN; 406 /* 407 * If we have at least MINCLSIZE bytes, 408 * allocate a new page. Swap the current 409 * buffer page with the new one. 410 */ 411 if (len >= MINCLSIZE) { 412 MCLGET(m, M_DONTWAIT); 413 if (m->m_flags & M_EXT) { 414 cp = mtod(m, char *); 415 m->m_data = sc->sc_buf; 416 sc->sc_buf = cp; 417 count = MIN(len, MCLBYTES); 418 goto nocopy; 419 } 420 } 421 count = MIN(len, m->m_len); 422 bcopy(cp, mtod(m, caddr_t), count); 423 nocopy: 424 m->m_len = count; 425 cp += count; 426 len -= count; 427 mp = &m->m_next; 428 } 429 return (top); 430 } 431 432 /* 433 * tty interface receiver interrupt. 434 */ 435 slinput(c, tp) 436 register int c; 437 register struct tty *tp; 438 { 439 register struct sl_softc *sc; 440 register struct mbuf *m; 441 int s; 442 443 tk_nin++; 444 sc = (struct sl_softc *)tp->t_sc; 445 if (sc == NULL) 446 return; 447 448 c &= 0xff; 449 if (sc->sc_flags & SC_ESCAPED) { 450 sc->sc_flags &= ~SC_ESCAPED; 451 switch (c) { 452 453 case TRANS_FRAME_ESCAPE: 454 c = FRAME_ESCAPE; 455 break; 456 457 case TRANS_FRAME_END: 458 c = FRAME_END; 459 break; 460 461 default: 462 sc->sc_if.if_ierrors++; 463 sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 464 sc->sc_ilen = 0; 465 return; 466 } 467 } else { 468 switch (c) { 469 470 case FRAME_END: 471 if (sc->sc_ilen == 0) /* ignore */ 472 return; 473 m = sl_btom(sc, sc->sc_ilen, &sc->sc_if); 474 if (m == NULL) { 475 sc->sc_if.if_ierrors++; 476 return; 477 } 478 sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 479 sc->sc_ilen = 0; 480 sc->sc_if.if_ipackets++; 481 s = splimp(); 482 if (IF_QFULL(&ipintrq)) { 483 IF_DROP(&ipintrq); 484 sc->sc_if.if_ierrors++; 485 m_freem(m); 486 } else { 487 IF_ENQUEUE(&ipintrq, m); 488 schednetisr(NETISR_IP); 489 } 490 splx(s); 491 return; 492 493 case FRAME_ESCAPE: 494 sc->sc_flags |= SC_ESCAPED; 495 return; 496 } 497 } 498 if (++sc->sc_ilen > SLMTU) { 499 sc->sc_if.if_ierrors++; 500 sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *); 501 sc->sc_ilen = 0; 502 return; 503 } 504 *sc->sc_mp++ = c; 505 } 506 507 /* 508 * Process an ioctl request. 509 */ 510 slioctl(ifp, cmd, data) 511 register struct ifnet *ifp; 512 int cmd; 513 caddr_t data; 514 { 515 register struct ifaddr *ifa = (struct ifaddr *)data; 516 int s = splimp(), error = 0; 517 518 switch (cmd) { 519 520 case SIOCSIFADDR: 521 if (ifa->ifa_addr->sa_family == AF_INET) 522 ifp->if_flags |= IFF_UP; 523 else 524 error = EAFNOSUPPORT; 525 break; 526 527 case SIOCSIFDSTADDR: 528 if (ifa->ifa_addr->sa_family != AF_INET) 529 error = EAFNOSUPPORT; 530 break; 531 532 default: 533 error = EINVAL; 534 } 535 splx(s); 536 return (error); 537 } 538 #endif 539