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