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