rm spaces in printfs
[unix-history] / usr / src / sys / net / if_sl.c
CommitLineData
5c723b90 1/* @(#)if_sl.c 7.2 (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"
e42fd7b3 45#include "../netinet/in_var.h"
41b6e003 46#include "../netinet/ip.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) {
5c723b90
MK
354 int s = splimp();
355
8011f5df 356 MCLALLOC(p, 1);
5c723b90 357 splx(s);
8011f5df
MK
358 if (p) {
359 sc->sc_buf = (char *)p;
360 sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *);
361 } else {
362 printf("sl%d: can't allocate buffer\n", sc - sl_softc);
363 sc->sc_if.if_flags &= ~IFF_UP;
364 return (0);
365 }
366 }
367 return (1);
41b6e003
MK
368}
369
370/*
371 * Copy data buffer to mbuf chain; add ifnet pointer ifp.
372 */
373struct mbuf *
8011f5df
MK
374sl_btom(sc, len, ifp)
375 struct sl_softc *sc;
41b6e003
MK
376 register int len;
377 struct ifnet *ifp;
378{
8011f5df 379 register caddr_t cp;
41b6e003 380 register struct mbuf *m, **mp;
8011f5df 381 register unsigned count;
41b6e003
MK
382 struct mbuf *top = NULL;
383
8011f5df 384 cp = sc->sc_buf + sizeof(struct ifnet *);
41b6e003
MK
385 mp = &top;
386 while (len > 0) {
387 MGET(m, M_DONTWAIT, MT_DATA);
388 if ((*mp = m) == NULL) {
389 m_freem(top);
390 return (NULL);
391 }
8011f5df 392 if (ifp)
41b6e003 393 m->m_off += sizeof(ifp);
8011f5df
MK
394 /*
395 * If we have at least NBPG bytes,
396 * allocate a new page. Swap the current buffer page
397 * with the new one. We depend on having a space
398 * left at the beginning of the buffer
399 * for the interface pointer.
400 */
401 if (len >= NBPG) {
402 MCLGET(m);
403 if (m->m_len == CLBYTES) {
404 cp = mtod(m, char *);
405 m->m_off = (int)sc->sc_buf - (int)m;
492250ec 406 sc->sc_buf = cp;
8011f5df
MK
407 if (ifp) {
408 m->m_off += sizeof(ifp);
409 count = MIN(len,
410 CLBYTES - sizeof(struct ifnet *));
41b6e003 411 } else
8011f5df
MK
412 count = MIN(len, CLBYTES);
413 goto nocopy;
414 }
41b6e003 415 }
8011f5df
MK
416 if (ifp)
417 count = MIN(len, MLEN - sizeof(ifp));
418 else
419 count = MIN(len, MLEN);
420 bcopy(cp, mtod(m, caddr_t), count);
421nocopy:
41b6e003
MK
422 m->m_len = count;
423 if (ifp) {
424 m->m_off -= sizeof(ifp);
425 m->m_len += sizeof(ifp);
426 *mtod(m, struct ifnet **) = ifp;
427 ifp = NULL;
428 }
8011f5df 429 cp += count;
41b6e003
MK
430 len -= count;
431 mp = &m->m_next;
432 }
433 return (top);
434}
435
436/*
437 * tty interface receiver interrupt.
438 */
439slinput(c, tp)
440 register int c;
441 register struct tty *tp;
442{
443 register struct sl_softc *sc;
444 register struct mbuf *m;
445 int s;
446
8011f5df 447 tk_nin++;
41b6e003
MK
448 sc = (struct sl_softc *)tp->t_sc;
449 if (sc == NULL)
450 return;
451
452 c &= 0xff;
453 if (sc->sc_flags & SC_ESCAPED) {
454 sc->sc_flags &= ~SC_ESCAPED;
455 switch (c) {
456
457 case TRANS_FRAME_ESCAPE:
458 c = FRAME_ESCAPE;
459 break;
460
461 case TRANS_FRAME_END:
462 c = FRAME_END;
463 break;
464
465 default:
466 sc->sc_if.if_ierrors++;
8011f5df 467 sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *);
41b6e003
MK
468 sc->sc_ilen = 0;
469 return;
470 }
471 } else {
472 switch (c) {
473
474 case FRAME_END:
475 if (sc->sc_ilen == 0) /* ignore */
476 return;
8011f5df 477 m = sl_btom(sc, sc->sc_ilen, &sc->sc_if);
41b6e003
MK
478 if (m == NULL) {
479 sc->sc_if.if_ierrors++;
480 return;
481 }
8011f5df 482 sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *);
41b6e003
MK
483 sc->sc_ilen = 0;
484 sc->sc_if.if_ipackets++;
485 s = splimp();
486 if (IF_QFULL(&ipintrq)) {
487 IF_DROP(&ipintrq);
488 sc->sc_if.if_ierrors++;
489 m_freem(m);
490 } else {
491 IF_ENQUEUE(&ipintrq, m);
492 schednetisr(NETISR_IP);
493 }
494 splx(s);
495 return;
496
497 case FRAME_ESCAPE:
498 sc->sc_flags |= SC_ESCAPED;
499 return;
500 }
501 }
9e4e65d5 502 if (++sc->sc_ilen > SLMTU) {
41b6e003 503 sc->sc_if.if_ierrors++;
8011f5df 504 sc->sc_mp = sc->sc_buf + sizeof(struct ifnet *);
41b6e003
MK
505 sc->sc_ilen = 0;
506 return;
507 }
508 *sc->sc_mp++ = c;
509}
510
511/*
512 * Process an ioctl request.
513 */
514slioctl(ifp, cmd, data)
515 register struct ifnet *ifp;
516 int cmd;
517 caddr_t data;
518{
519 register struct ifaddr *ifa = (struct ifaddr *)data;
520 int s = splimp(), error = 0;
521
522 switch (cmd) {
523
524 case SIOCSIFADDR:
525 if (ifa->ifa_addr.sa_family == AF_INET)
526 ifp->if_flags |= IFF_UP;
527 else
528 error = EAFNOSUPPORT;
529 break;
530
531 case SIOCSIFDSTADDR:
532 if (ifa->ifa_addr.sa_family != AF_INET)
533 error = EAFNOSUPPORT;
534 break;
535
536 default:
537 error = EINVAL;
538 }
539 splx(s);
540 return (error);
541}
542#endif