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