xref: /netbsd-src/sys/kern/tty_subr.c (revision 267197ec1eebfcb9810ea27a89625b6ddf68e3e7)
1 /*	$NetBSD: tty_subr.c,v 1.32 2007/12/22 02:21:29 ad Exp $	*/
2 
3 /*
4  * Copyright (c) 1993, 1994 Theo de Raadt
5  * All rights reserved.
6  *
7  * Per Lindqvist <pgd@compuram.bbt.se> supplied an almost fully working
8  * set of true clist functions that this is very loosely based on.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: tty_subr.c,v 1.32 2007/12/22 02:21:29 ad Exp $");
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/buf.h>
37 #include <sys/ioctl.h>
38 #include <sys/tty.h>
39 #include <sys/malloc.h>
40 
41 MALLOC_DEFINE(M_TTYS, "ttys", "allocated tty structures");
42 
43 /*
44  * At compile time, choose:
45  * There are two ways the TTY_QUOTE bit can be stored. If QBITS is
46  * defined we allocate an array of bits -- 1/8th as much memory but
47  * setbit(), clrbit(), and isset() take more CPU. If QBITS is
48  * undefined, we just use an array of bytes.
49  *
50  * If TTY_QUOTE functionality isn't required by a line discipline,
51  * it can free c_cq and set it to NULL. This speeds things up,
52  * and also does not use any extra memory. This is useful for (say)
53  * a SLIP line discipline that wants a 32K ring buffer for data
54  * but doesn't need quoting.
55  */
56 #define QBITS
57 
58 #ifdef QBITS
59 #define QMEM(n)		((((n)-1)/NBBY)+1)
60 #else
61 #define QMEM(n)		(n)
62 #endif
63 
64 #ifdef QBITS
65 static void	clrbits(u_char *, int, int);
66 #endif
67 
68 /*
69  * Initialize a particular clist. Ok, they are really ring buffers,
70  * of the specified length, with/without quoting support.
71  */
72 int
73 clalloc(struct clist *clp, int size, int quot)
74 {
75 
76 	clp->c_cs = malloc(size, M_TTYS, M_WAITOK);
77 	if (!clp->c_cs)
78 		return (-1);
79 	memset(clp->c_cs, 0, size);
80 
81 	if(quot) {
82 		clp->c_cq = malloc(QMEM(size), M_TTYS, M_WAITOK);
83 		if (!clp->c_cq) {
84 			free(clp->c_cs, M_TTYS);
85 			return (-1);
86 		}
87 		memset(clp->c_cq, 0, QMEM(size));
88 	} else
89 		clp->c_cq = (u_char *)0;
90 
91 	clp->c_cf = clp->c_cl = (u_char *)0;
92 	clp->c_ce = clp->c_cs + size;
93 	clp->c_cn = size;
94 	clp->c_cc = 0;
95 
96 	cv_init(&clp->c_cv, "tty");
97 	cv_init(&clp->c_cvf, "ttyf");
98 	return (0);
99 }
100 
101 void
102 clfree(struct clist *clp)
103 {
104 	if(clp->c_cs)
105 		free(clp->c_cs, M_TTYS);
106 	if(clp->c_cq)
107 		free(clp->c_cq, M_TTYS);
108 	clp->c_cs = clp->c_cq = (u_char *)0;
109 	cv_destroy(&clp->c_cv);
110 	cv_destroy(&clp->c_cvf);
111 }
112 
113 void
114 clwakeup(struct clist *clp)
115 {
116 
117 	cv_broadcast(&clp->c_cv);
118 }
119 
120 /*
121  * Get a character from a clist.
122  */
123 int
124 getc(struct clist *clp)
125 {
126 	int c = -1;
127 	int s;
128 
129 	s = spltty();
130 	if (clp->c_cc == 0)
131 		goto out;
132 
133 	c = *clp->c_cf & 0xff;
134 	if (clp->c_cq) {
135 #ifdef QBITS
136 		if (isset(clp->c_cq, clp->c_cf - clp->c_cs) )
137 			c |= TTY_QUOTE;
138 #else
139 		if (*(clp->c_cf - clp->c_cs + clp->c_cq))
140 			c |= TTY_QUOTE;
141 #endif
142 	}
143 	if (++clp->c_cf == clp->c_ce)
144 		clp->c_cf = clp->c_cs;
145 	if (--clp->c_cc == 0)
146 		clp->c_cf = clp->c_cl = (u_char *)0;
147 out:
148 	splx(s);
149 	return c;
150 }
151 
152 /*
153  * Copy clist to buffer.
154  * Return number of bytes moved.
155  */
156 int
157 q_to_b(struct clist *clp, u_char *cp, int count)
158 {
159 	int cc;
160 	u_char *p = cp;
161 	int s;
162 
163 	s = spltty();
164 	/* optimize this while loop */
165 	while (count > 0 && clp->c_cc > 0) {
166 		cc = clp->c_cl - clp->c_cf;
167 		if (clp->c_cf >= clp->c_cl)
168 			cc = clp->c_ce - clp->c_cf;
169 		if (cc > count)
170 			cc = count;
171 		memcpy(p, clp->c_cf, cc);
172 		count -= cc;
173 		p += cc;
174 		clp->c_cc -= cc;
175 		clp->c_cf += cc;
176 		if (clp->c_cf == clp->c_ce)
177 			clp->c_cf = clp->c_cs;
178 	}
179 	if (clp->c_cc == 0)
180 		clp->c_cf = clp->c_cl = (u_char *)0;
181 	splx(s);
182 	return p - cp;
183 }
184 
185 /*
186  * Return count of contiguous characters in clist.
187  * Stop counting if flag&character is non-null.
188  */
189 int
190 ndqb(struct clist *clp, int flag)
191 {
192 	int count = 0;
193 	int i;
194 	int cc;
195 	int s;
196 
197 	s = spltty();
198 	if ((cc = clp->c_cc) == 0)
199 		goto out;
200 
201 	if (flag == 0) {
202 		count = clp->c_cl - clp->c_cf;
203 		if (count <= 0)
204 			count = clp->c_ce - clp->c_cf;
205 		goto out;
206 	}
207 
208 	i = clp->c_cf - clp->c_cs;
209 	if (flag & TTY_QUOTE) {
210 		while (cc-- > 0 && !(clp->c_cs[i++] & (flag & ~TTY_QUOTE) ||
211 		    isset(clp->c_cq, i))) {
212 			count++;
213 			if (i == clp->c_cn)
214 				break;
215 		}
216 	} else {
217 		while (cc-- > 0 && !(clp->c_cs[i++] & flag)) {
218 			count++;
219 			if (i == clp->c_cn)
220 				break;
221 		}
222 	}
223 out:
224 	splx(s);
225 	return count;
226 }
227 
228 /*
229  * Flush count bytes from clist.
230  */
231 void
232 ndflush(struct clist *clp, int count)
233 {
234 	int cc;
235 	int s;
236 
237 	s = spltty();
238 	if (count == clp->c_cc) {
239 		clp->c_cc = 0;
240 		clp->c_cf = clp->c_cl = (u_char *)0;
241 		goto out;
242 	}
243 	/* optimize this while loop */
244 	while (count > 0 && clp->c_cc > 0) {
245 		cc = clp->c_cl - clp->c_cf;
246 		if (clp->c_cf >= clp->c_cl)
247 			cc = clp->c_ce - clp->c_cf;
248 		if (cc > count)
249 			cc = count;
250 		count -= cc;
251 		clp->c_cc -= cc;
252 		clp->c_cf += cc;
253 		if (clp->c_cf == clp->c_ce)
254 			clp->c_cf = clp->c_cs;
255 	}
256 	if (clp->c_cc == 0)
257 		clp->c_cf = clp->c_cl = (u_char *)0;
258 out:
259 	splx(s);
260 }
261 
262 /*
263  * Put a character into the output queue.
264  */
265 int
266 putc(int c, struct clist *clp)
267 {
268 	int i;
269 	int s;
270 
271 	s = spltty();
272 	if (clp->c_cc == clp->c_cn)
273 		goto out;
274 
275 	if (clp->c_cc == 0) {
276 		if (!clp->c_cs) {
277 #if defined(DIAGNOSTIC) || 1
278 			printf("putc: required clalloc\n");
279 #endif
280 			if(clalloc(clp, 1024, 1)) {
281 out:
282 				splx(s);
283 				return -1;
284 			}
285 		}
286 		clp->c_cf = clp->c_cl = clp->c_cs;
287 	}
288 
289 	*clp->c_cl = c & 0xff;
290 	i = clp->c_cl - clp->c_cs;
291 	if (clp->c_cq) {
292 #ifdef QBITS
293 		if (c & TTY_QUOTE)
294 			setbit(clp->c_cq, i);
295 		else
296 			clrbit(clp->c_cq, i);
297 #else
298 		q = clp->c_cq + i;
299 		*q = (c & TTY_QUOTE) ? 1 : 0;
300 #endif
301 	}
302 	clp->c_cc++;
303 	clp->c_cl++;
304 	if (clp->c_cl == clp->c_ce)
305 		clp->c_cl = clp->c_cs;
306 	splx(s);
307 	return 0;
308 }
309 
310 #ifdef QBITS
311 /*
312  * optimized version of
313  *
314  * for (i = 0; i < len; i++)
315  *	clrbit(cp, off + len);
316  */
317 static void
318 clrbits(u_char *cp, int off, int len)
319 {
320 	int sby, sbi, eby, ebi;
321 	int i;
322 	u_char mask;
323 
324 	if(len==1) {
325 		clrbit(cp, off);
326 		return;
327 	}
328 
329 	sby = off / NBBY;
330 	sbi = off % NBBY;
331 	eby = (off+len) / NBBY;
332 	ebi = (off+len) % NBBY;
333 	if (sby == eby) {
334 		mask = ((1 << (ebi - sbi)) - 1) << sbi;
335 		cp[sby] &= ~mask;
336 	} else {
337 		mask = (1<<sbi) - 1;
338 		cp[sby++] &= mask;
339 
340 		mask = (1<<ebi) - 1;
341 		cp[eby] &= ~mask;
342 
343 		for (i = sby; i < eby; i++)
344 			cp[i] = 0x00;
345 	}
346 }
347 #endif
348 
349 /*
350  * Copy buffer to clist.
351  * Return number of bytes not transfered.
352  */
353 int
354 b_to_q(const u_char *cp, int count, struct clist *clp)
355 {
356 	int cc;
357 	const u_char *p = cp;
358 	int s;
359 
360 	if (count <= 0)
361 		return 0;
362 
363 	s = spltty();
364 	if (clp->c_cc == clp->c_cn)
365 		goto out;
366 
367 	if (clp->c_cc == 0) {
368 		if (!clp->c_cs) {
369 #if defined(DIAGNOSTIC) || 1
370 			printf("b_to_q: required clalloc\n");
371 #endif
372 			if(clalloc(clp, 1024, 1))
373 				goto out;
374 		}
375 		clp->c_cf = clp->c_cl = clp->c_cs;
376 	}
377 
378 	/* optimize this while loop */
379 	while (count > 0 && clp->c_cc < clp->c_cn) {
380 		cc = clp->c_ce - clp->c_cl;
381 		if (clp->c_cf > clp->c_cl)
382 			cc = clp->c_cf - clp->c_cl;
383 		if (cc > count)
384 			cc = count;
385 		memcpy(clp->c_cl, p, cc);
386 		if (clp->c_cq) {
387 #ifdef QBITS
388 			clrbits(clp->c_cq, clp->c_cl - clp->c_cs, cc);
389 #else
390 			memset(clp->c_cl - clp->c_cs + clp->c_cq, 0, cc);
391 #endif
392 		}
393 		p += cc;
394 		count -= cc;
395 		clp->c_cc += cc;
396 		clp->c_cl += cc;
397 		if (clp->c_cl == clp->c_ce)
398 			clp->c_cl = clp->c_cs;
399 	}
400 out:
401 	splx(s);
402 	return count;
403 }
404 
405 static int cc;
406 
407 /*
408  * Given a non-NULL pointer into the clist return the pointer
409  * to the next character in the list or return NULL if no more chars.
410  *
411  * Callers must not allow getc's to happen between firstc's and getc's
412  * so that the pointer becomes invalid.  Note that interrupts are NOT
413  * masked.
414  */
415 u_char *
416 nextc(struct clist *clp, u_char *cp, int *c)
417 {
418 
419 	if (clp->c_cf == cp) {
420 		/*
421 		 * First time initialization.
422 		 */
423 		cc = clp->c_cc;
424 	}
425 	if (cc == 0 || cp == NULL)
426 		return NULL;
427 	if (--cc == 0)
428 		return NULL;
429 	if (++cp == clp->c_ce)
430 		cp = clp->c_cs;
431 	*c = *cp & 0xff;
432 	if (clp->c_cq) {
433 #ifdef QBITS
434 		if (isset(clp->c_cq, cp - clp->c_cs))
435 			*c |= TTY_QUOTE;
436 #else
437 		if (*(clp->c_cf - clp->c_cs + clp->c_cq))
438 			*c |= TTY_QUOTE;
439 #endif
440 	}
441 	return cp;
442 }
443 
444 /*
445  * Given a non-NULL pointer into the clist return the pointer
446  * to the first character in the list or return NULL if no more chars.
447  *
448  * Callers must not allow getc's to happen between firstc's and getc's
449  * so that the pointer becomes invalid.  Note that interrupts are NOT
450  * masked.
451  *
452  * *c is set to the NEXT character
453  */
454 u_char *
455 firstc(struct clist *clp, int *c)
456 {
457 	u_char *cp;
458 
459 	cc = clp->c_cc;
460 	if (cc == 0)
461 		return NULL;
462 	cp = clp->c_cf;
463 	*c = *cp & 0xff;
464 	if(clp->c_cq) {
465 #ifdef QBITS
466 		if (isset(clp->c_cq, cp - clp->c_cs))
467 			*c |= TTY_QUOTE;
468 #else
469 		if (*(cp - clp->c_cs + clp->c_cq))
470 			*c |= TTY_QUOTE;
471 #endif
472 	}
473 	return clp->c_cf;
474 }
475 
476 /*
477  * Remove the last character in the clist and return it.
478  */
479 int
480 unputc(struct clist *clp)
481 {
482 	unsigned int c = -1;
483 	int s;
484 
485 	s = spltty();
486 	if (clp->c_cc == 0)
487 		goto out;
488 
489 	if (clp->c_cl == clp->c_cs)
490 		clp->c_cl = clp->c_ce - 1;
491 	else
492 		--clp->c_cl;
493 	clp->c_cc--;
494 
495 	c = *clp->c_cl & 0xff;
496 	if (clp->c_cq) {
497 #ifdef QBITS
498 		if (isset(clp->c_cq, clp->c_cl - clp->c_cs))
499 			c |= TTY_QUOTE;
500 #else
501 		if (*(clp->c_cf - clp->c_cs + clp->c_cq))
502 			c |= TTY_QUOTE;
503 #endif
504 	}
505 	if (clp->c_cc == 0)
506 		clp->c_cf = clp->c_cl = (u_char *)0;
507 out:
508 	splx(s);
509 	return c;
510 }
511 
512 /*
513  * Put the chars in the from queue on the end of the to queue.
514  */
515 void
516 catq(struct clist *from, struct clist *to)
517 {
518 	int c;
519 
520 	while ((c = getc(from)) != -1)
521 		putc(c, to);
522 }
523