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