xref: /netbsd-src/sys/kern/tty_subr.c (revision 7fa608457b817eca6e0977b37f758ae064f3c99c)
1 /*	$NetBSD: tty_subr.c,v 1.30 2007/11/07 15:56:22 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.30 2007/11/07 15:56:22 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, "tty");
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 
114 /*
115  * Get a character from a clist.
116  */
117 int
118 getc(struct clist *clp)
119 {
120 	int c = -1;
121 	int s;
122 
123 	s = spltty();
124 	if (clp->c_cc == 0)
125 		goto out;
126 
127 	c = *clp->c_cf & 0xff;
128 	if (clp->c_cq) {
129 #ifdef QBITS
130 		if (isset(clp->c_cq, clp->c_cf - clp->c_cs) )
131 			c |= TTY_QUOTE;
132 #else
133 		if (*(clp->c_cf - clp->c_cs + clp->c_cq))
134 			c |= TTY_QUOTE;
135 #endif
136 	}
137 	if (++clp->c_cf == clp->c_ce)
138 		clp->c_cf = clp->c_cs;
139 	if (--clp->c_cc == 0)
140 		clp->c_cf = clp->c_cl = (u_char *)0;
141 out:
142 	splx(s);
143 	return c;
144 }
145 
146 /*
147  * Copy clist to buffer.
148  * Return number of bytes moved.
149  */
150 int
151 q_to_b(struct clist *clp, u_char *cp, int count)
152 {
153 	int cc;
154 	u_char *p = cp;
155 	int s;
156 
157 	s = spltty();
158 	/* optimize this while loop */
159 	while (count > 0 && clp->c_cc > 0) {
160 		cc = clp->c_cl - clp->c_cf;
161 		if (clp->c_cf >= clp->c_cl)
162 			cc = clp->c_ce - clp->c_cf;
163 		if (cc > count)
164 			cc = count;
165 		memcpy(p, clp->c_cf, cc);
166 		count -= cc;
167 		p += cc;
168 		clp->c_cc -= cc;
169 		clp->c_cf += cc;
170 		if (clp->c_cf == clp->c_ce)
171 			clp->c_cf = clp->c_cs;
172 	}
173 	if (clp->c_cc == 0)
174 		clp->c_cf = clp->c_cl = (u_char *)0;
175 	splx(s);
176 	return p - cp;
177 }
178 
179 /*
180  * Return count of contiguous characters in clist.
181  * Stop counting if flag&character is non-null.
182  */
183 int
184 ndqb(struct clist *clp, int flag)
185 {
186 	int count = 0;
187 	int i;
188 	int cc;
189 	int s;
190 
191 	s = spltty();
192 	if ((cc = clp->c_cc) == 0)
193 		goto out;
194 
195 	if (flag == 0) {
196 		count = clp->c_cl - clp->c_cf;
197 		if (count <= 0)
198 			count = clp->c_ce - clp->c_cf;
199 		goto out;
200 	}
201 
202 	i = clp->c_cf - clp->c_cs;
203 	if (flag & TTY_QUOTE) {
204 		while (cc-- > 0 && !(clp->c_cs[i++] & (flag & ~TTY_QUOTE) ||
205 		    isset(clp->c_cq, i))) {
206 			count++;
207 			if (i == clp->c_cn)
208 				break;
209 		}
210 	} else {
211 		while (cc-- > 0 && !(clp->c_cs[i++] & flag)) {
212 			count++;
213 			if (i == clp->c_cn)
214 				break;
215 		}
216 	}
217 out:
218 	splx(s);
219 	return count;
220 }
221 
222 /*
223  * Flush count bytes from clist.
224  */
225 void
226 ndflush(struct clist *clp, int count)
227 {
228 	int cc;
229 	int s;
230 
231 	s = spltty();
232 	if (count == clp->c_cc) {
233 		clp->c_cc = 0;
234 		clp->c_cf = clp->c_cl = (u_char *)0;
235 		goto out;
236 	}
237 	/* optimize this while loop */
238 	while (count > 0 && clp->c_cc > 0) {
239 		cc = clp->c_cl - clp->c_cf;
240 		if (clp->c_cf >= clp->c_cl)
241 			cc = clp->c_ce - clp->c_cf;
242 		if (cc > count)
243 			cc = count;
244 		count -= cc;
245 		clp->c_cc -= cc;
246 		clp->c_cf += cc;
247 		if (clp->c_cf == clp->c_ce)
248 			clp->c_cf = clp->c_cs;
249 	}
250 	if (clp->c_cc == 0)
251 		clp->c_cf = clp->c_cl = (u_char *)0;
252 out:
253 	splx(s);
254 }
255 
256 /*
257  * Put a character into the output queue.
258  */
259 int
260 putc(int c, struct clist *clp)
261 {
262 	int i;
263 	int s;
264 
265 	s = spltty();
266 	if (clp->c_cc == clp->c_cn)
267 		goto out;
268 
269 	if (clp->c_cc == 0) {
270 		if (!clp->c_cs) {
271 #if defined(DIAGNOSTIC) || 1
272 			printf("putc: required clalloc\n");
273 #endif
274 			if(clalloc(clp, 1024, 1)) {
275 out:
276 				splx(s);
277 				return -1;
278 			}
279 		}
280 		clp->c_cf = clp->c_cl = clp->c_cs;
281 	}
282 
283 	*clp->c_cl = c & 0xff;
284 	i = clp->c_cl - clp->c_cs;
285 	if (clp->c_cq) {
286 #ifdef QBITS
287 		if (c & TTY_QUOTE)
288 			setbit(clp->c_cq, i);
289 		else
290 			clrbit(clp->c_cq, i);
291 #else
292 		q = clp->c_cq + i;
293 		*q = (c & TTY_QUOTE) ? 1 : 0;
294 #endif
295 	}
296 	clp->c_cc++;
297 	clp->c_cl++;
298 	if (clp->c_cl == clp->c_ce)
299 		clp->c_cl = clp->c_cs;
300 	splx(s);
301 	return 0;
302 }
303 
304 #ifdef QBITS
305 /*
306  * optimized version of
307  *
308  * for (i = 0; i < len; i++)
309  *	clrbit(cp, off + len);
310  */
311 static void
312 clrbits(u_char *cp, int off, int len)
313 {
314 	int sby, sbi, eby, ebi;
315 	int i;
316 	u_char mask;
317 
318 	if(len==1) {
319 		clrbit(cp, off);
320 		return;
321 	}
322 
323 	sby = off / NBBY;
324 	sbi = off % NBBY;
325 	eby = (off+len) / NBBY;
326 	ebi = (off+len) % NBBY;
327 	if (sby == eby) {
328 		mask = ((1 << (ebi - sbi)) - 1) << sbi;
329 		cp[sby] &= ~mask;
330 	} else {
331 		mask = (1<<sbi) - 1;
332 		cp[sby++] &= mask;
333 
334 		mask = (1<<ebi) - 1;
335 		cp[eby] &= ~mask;
336 
337 		for (i = sby; i < eby; i++)
338 			cp[i] = 0x00;
339 	}
340 }
341 #endif
342 
343 /*
344  * Copy buffer to clist.
345  * Return number of bytes not transfered.
346  */
347 int
348 b_to_q(const u_char *cp, int count, struct clist *clp)
349 {
350 	int cc;
351 	const u_char *p = cp;
352 	int s;
353 
354 	if (count <= 0)
355 		return 0;
356 
357 	s = spltty();
358 	if (clp->c_cc == clp->c_cn)
359 		goto out;
360 
361 	if (clp->c_cc == 0) {
362 		if (!clp->c_cs) {
363 #if defined(DIAGNOSTIC) || 1
364 			printf("b_to_q: required clalloc\n");
365 #endif
366 			if(clalloc(clp, 1024, 1))
367 				goto out;
368 		}
369 		clp->c_cf = clp->c_cl = clp->c_cs;
370 	}
371 
372 	/* optimize this while loop */
373 	while (count > 0 && clp->c_cc < clp->c_cn) {
374 		cc = clp->c_ce - clp->c_cl;
375 		if (clp->c_cf > clp->c_cl)
376 			cc = clp->c_cf - clp->c_cl;
377 		if (cc > count)
378 			cc = count;
379 		memcpy(clp->c_cl, p, cc);
380 		if (clp->c_cq) {
381 #ifdef QBITS
382 			clrbits(clp->c_cq, clp->c_cl - clp->c_cs, cc);
383 #else
384 			memset(clp->c_cl - clp->c_cs + clp->c_cq, 0, cc);
385 #endif
386 		}
387 		p += cc;
388 		count -= cc;
389 		clp->c_cc += cc;
390 		clp->c_cl += cc;
391 		if (clp->c_cl == clp->c_ce)
392 			clp->c_cl = clp->c_cs;
393 	}
394 out:
395 	splx(s);
396 	return count;
397 }
398 
399 static int cc;
400 
401 /*
402  * Given a non-NULL pointer into the clist return the pointer
403  * to the next character in the list or return NULL if no more chars.
404  *
405  * Callers must not allow getc's to happen between firstc's and getc's
406  * so that the pointer becomes invalid.  Note that interrupts are NOT
407  * masked.
408  */
409 u_char *
410 nextc(struct clist *clp, u_char *cp, int *c)
411 {
412 
413 	if (clp->c_cf == cp) {
414 		/*
415 		 * First time initialization.
416 		 */
417 		cc = clp->c_cc;
418 	}
419 	if (cc == 0 || cp == NULL)
420 		return NULL;
421 	if (--cc == 0)
422 		return NULL;
423 	if (++cp == clp->c_ce)
424 		cp = clp->c_cs;
425 	*c = *cp & 0xff;
426 	if (clp->c_cq) {
427 #ifdef QBITS
428 		if (isset(clp->c_cq, cp - clp->c_cs))
429 			*c |= TTY_QUOTE;
430 #else
431 		if (*(clp->c_cf - clp->c_cs + clp->c_cq))
432 			*c |= TTY_QUOTE;
433 #endif
434 	}
435 	return cp;
436 }
437 
438 /*
439  * Given a non-NULL pointer into the clist return the pointer
440  * to the first character in the list or return NULL if no more chars.
441  *
442  * Callers must not allow getc's to happen between firstc's and getc's
443  * so that the pointer becomes invalid.  Note that interrupts are NOT
444  * masked.
445  *
446  * *c is set to the NEXT character
447  */
448 u_char *
449 firstc(struct clist *clp, int *c)
450 {
451 	u_char *cp;
452 
453 	cc = clp->c_cc;
454 	if (cc == 0)
455 		return NULL;
456 	cp = clp->c_cf;
457 	*c = *cp & 0xff;
458 	if(clp->c_cq) {
459 #ifdef QBITS
460 		if (isset(clp->c_cq, cp - clp->c_cs))
461 			*c |= TTY_QUOTE;
462 #else
463 		if (*(cp - clp->c_cs + clp->c_cq))
464 			*c |= TTY_QUOTE;
465 #endif
466 	}
467 	return clp->c_cf;
468 }
469 
470 /*
471  * Remove the last character in the clist and return it.
472  */
473 int
474 unputc(struct clist *clp)
475 {
476 	unsigned int c = -1;
477 	int s;
478 
479 	s = spltty();
480 	if (clp->c_cc == 0)
481 		goto out;
482 
483 	if (clp->c_cl == clp->c_cs)
484 		clp->c_cl = clp->c_ce - 1;
485 	else
486 		--clp->c_cl;
487 	clp->c_cc--;
488 
489 	c = *clp->c_cl & 0xff;
490 	if (clp->c_cq) {
491 #ifdef QBITS
492 		if (isset(clp->c_cq, clp->c_cl - clp->c_cs))
493 			c |= TTY_QUOTE;
494 #else
495 		if (*(clp->c_cf - clp->c_cs + clp->c_cq))
496 			c |= TTY_QUOTE;
497 #endif
498 	}
499 	if (clp->c_cc == 0)
500 		clp->c_cf = clp->c_cl = (u_char *)0;
501 out:
502 	splx(s);
503 	return c;
504 }
505 
506 /*
507  * Put the chars in the from queue on the end of the to queue.
508  */
509 void
510 catq(struct clist *from, struct clist *to)
511 {
512 	int c;
513 
514 	while ((c = getc(from)) != -1)
515 		putc(c, to);
516 }
517