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