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