1 | /* $NetBSD: tty_subr.c,v 1.40 2011/09/24 00:05:38 christos 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.40 2011/09/24 00:05:38 christos 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/kmem.h> |
40 | |
41 | /* |
42 | * At compile time, choose: |
43 | * There are two ways the TTY_QUOTE bit can be stored. If QBITS is |
44 | * defined we allocate an array of bits -- 1/8th as much memory but |
45 | * setbit(), clrbit(), and isset() take more CPU. If QBITS is |
46 | * undefined, we just use an array of bytes. |
47 | * |
48 | * If TTY_QUOTE functionality isn't required by a line discipline, |
49 | * it can free c_cq and set it to NULL. This speeds things up, |
50 | * and also does not use any extra memory. This is useful for (say) |
51 | * a SLIP line discipline that wants a 32K ring buffer for data |
52 | * but doesn't need quoting. |
53 | */ |
54 | #define QBITS |
55 | |
56 | #ifdef QBITS |
57 | #define QMEM(n) ((((n)-1)/NBBY)+1) |
58 | #else |
59 | #define QMEM(n) (n) |
60 | #endif |
61 | |
62 | #ifdef QBITS |
63 | static void clrbits(u_char *, unsigned int, unsigned int); |
64 | #endif |
65 | |
66 | /* |
67 | * Initialize a particular clist. Ok, they are really ring buffers, |
68 | * of the specified length, with/without quoting support. |
69 | */ |
70 | int |
71 | clalloc(struct clist *clp, int size, int quot) |
72 | { |
73 | |
74 | clp->c_cs = kmem_zalloc(size, KM_SLEEP); |
75 | if (!clp->c_cs) |
76 | return (-1); |
77 | |
78 | if (quot) { |
79 | clp->c_cq = kmem_zalloc(QMEM(size), KM_SLEEP); |
80 | if (!clp->c_cq) { |
81 | kmem_free(clp->c_cs, size); |
82 | return (-1); |
83 | } |
84 | } else |
85 | clp->c_cq = NULL; |
86 | |
87 | clp->c_cf = clp->c_cl = NULL; |
88 | clp->c_ce = clp->c_cs + size; |
89 | clp->c_cn = size; |
90 | clp->c_cc = 0; |
91 | |
92 | return (0); |
93 | } |
94 | |
95 | void |
96 | clfree(struct clist *clp) |
97 | { |
98 | if (clp->c_cs) |
99 | kmem_free(clp->c_cs, clp->c_cn); |
100 | if (clp->c_cq) |
101 | kmem_free(clp->c_cq, QMEM(clp->c_cn)); |
102 | clp->c_cs = clp->c_cq = NULL; |
103 | } |
104 | |
105 | /* |
106 | * Get a character from a clist. |
107 | */ |
108 | int |
109 | getc(struct clist *clp) |
110 | { |
111 | int c = -1; |
112 | int s; |
113 | |
114 | s = spltty(); |
115 | if (clp->c_cc == 0) |
116 | goto out; |
117 | |
118 | c = *clp->c_cf & 0xff; |
119 | if (clp->c_cq) { |
120 | #ifdef QBITS |
121 | if (isset(clp->c_cq, clp->c_cf - clp->c_cs) ) |
122 | c |= TTY_QUOTE; |
123 | #else |
124 | if (*(clp->c_cf - clp->c_cs + clp->c_cq)) |
125 | c |= TTY_QUOTE; |
126 | #endif |
127 | } |
128 | *clp->c_cf = 0; /* wipe out to avoid information disclosure */ |
129 | if (++clp->c_cf == clp->c_ce) |
130 | clp->c_cf = clp->c_cs; |
131 | if (--clp->c_cc == 0) |
132 | clp->c_cf = clp->c_cl = (u_char *)0; |
133 | out: |
134 | splx(s); |
135 | return c; |
136 | } |
137 | |
138 | /* |
139 | * Copy clist to buffer. |
140 | * Return number of bytes moved. |
141 | */ |
142 | int |
143 | q_to_b(struct clist *clp, u_char *cp, int count) |
144 | { |
145 | int cc; |
146 | u_char *p = cp; |
147 | int s; |
148 | |
149 | s = spltty(); |
150 | /* optimize this while loop */ |
151 | while (count > 0 && clp->c_cc > 0) { |
152 | cc = clp->c_cl - clp->c_cf; |
153 | if (clp->c_cf >= clp->c_cl) |
154 | cc = clp->c_ce - clp->c_cf; |
155 | if (cc > count) |
156 | cc = count; |
157 | memcpy(p, clp->c_cf, cc); |
158 | count -= cc; |
159 | p += cc; |
160 | clp->c_cc -= cc; |
161 | clp->c_cf += cc; |
162 | if (clp->c_cf == clp->c_ce) |
163 | clp->c_cf = clp->c_cs; |
164 | } |
165 | if (clp->c_cc == 0) |
166 | clp->c_cf = clp->c_cl = (u_char *)0; |
167 | splx(s); |
168 | return p - cp; |
169 | } |
170 | |
171 | /* |
172 | * Return count of contiguous characters in clist. |
173 | * Stop counting if flag&character is non-null. |
174 | */ |
175 | int |
176 | ndqb(struct clist *clp, int flag) |
177 | { |
178 | int count = 0; |
179 | int i; |
180 | int cc; |
181 | int s; |
182 | |
183 | s = spltty(); |
184 | if ((cc = clp->c_cc) == 0) |
185 | goto out; |
186 | |
187 | if (flag == 0) { |
188 | count = clp->c_cl - clp->c_cf; |
189 | if (count <= 0) |
190 | count = clp->c_ce - clp->c_cf; |
191 | goto out; |
192 | } |
193 | |
194 | i = clp->c_cf - clp->c_cs; |
195 | if (flag & TTY_QUOTE) { |
196 | while (cc-- > 0 && !(clp->c_cs[i++] & (flag & ~TTY_QUOTE) || |
197 | isset(clp->c_cq, i))) { |
198 | count++; |
199 | if (i == clp->c_cn) |
200 | break; |
201 | } |
202 | } else { |
203 | while (cc-- > 0 && !(clp->c_cs[i++] & flag)) { |
204 | count++; |
205 | if (i == clp->c_cn) |
206 | break; |
207 | } |
208 | } |
209 | out: |
210 | splx(s); |
211 | return count; |
212 | } |
213 | |
214 | /* |
215 | * Flush count bytes from clist. |
216 | */ |
217 | void |
218 | ndflush(struct clist *clp, int count) |
219 | { |
220 | int cc; |
221 | int s; |
222 | |
223 | s = spltty(); |
224 | if (count == clp->c_cc) { |
225 | clp->c_cc = 0; |
226 | clp->c_cf = clp->c_cl = (u_char *)0; |
227 | goto out; |
228 | } |
229 | /* optimize this while loop */ |
230 | while (count > 0 && clp->c_cc > 0) { |
231 | cc = clp->c_cl - clp->c_cf; |
232 | if (clp->c_cf >= clp->c_cl) |
233 | cc = clp->c_ce - clp->c_cf; |
234 | if (cc > count) |
235 | cc = count; |
236 | count -= cc; |
237 | clp->c_cc -= cc; |
238 | clp->c_cf += cc; |
239 | if (clp->c_cf == clp->c_ce) |
240 | clp->c_cf = clp->c_cs; |
241 | } |
242 | if (clp->c_cc == 0) |
243 | clp->c_cf = clp->c_cl = (u_char *)0; |
244 | out: |
245 | splx(s); |
246 | } |
247 | |
248 | /* |
249 | * Put a character into the output queue. |
250 | */ |
251 | int |
252 | putc(int c, struct clist *clp) |
253 | { |
254 | int i; |
255 | int s; |
256 | |
257 | s = spltty(); |
258 | if (clp->c_cc == clp->c_cn) |
259 | goto out; |
260 | |
261 | if (clp->c_cc == 0) { |
262 | if (!clp->c_cs) { |
263 | #if defined(DIAGNOSTIC) || 1 |
264 | printf("putc: required clalloc\n" ); |
265 | #endif |
266 | if (clalloc(clp, clp->c_cn, 1)) { |
267 | out: |
268 | splx(s); |
269 | return -1; |
270 | } |
271 | } |
272 | clp->c_cf = clp->c_cl = clp->c_cs; |
273 | } |
274 | |
275 | *clp->c_cl = c & 0xff; |
276 | i = clp->c_cl - clp->c_cs; |
277 | if (clp->c_cq) { |
278 | #ifdef QBITS |
279 | if (c & TTY_QUOTE) |
280 | setbit(clp->c_cq, i); |
281 | else |
282 | clrbit(clp->c_cq, i); |
283 | #else |
284 | q = clp->c_cq + i; |
285 | *q = (c & TTY_QUOTE) ? 1 : 0; |
286 | #endif |
287 | } |
288 | clp->c_cc++; |
289 | clp->c_cl++; |
290 | if (clp->c_cl == clp->c_ce) |
291 | clp->c_cl = clp->c_cs; |
292 | splx(s); |
293 | return 0; |
294 | } |
295 | |
296 | #ifdef QBITS |
297 | /* |
298 | * optimized version of |
299 | * |
300 | * for (i = 0; i < len; i++) |
301 | * clrbit(cp, off + len); |
302 | */ |
303 | static void |
304 | clrbits(u_char *cp, unsigned int off, unsigned int len) |
305 | { |
306 | unsigned int sbi, ebi; |
307 | u_char *scp, *ecp; |
308 | unsigned int end; |
309 | unsigned char mask; |
310 | |
311 | scp = cp + off / NBBY; |
312 | sbi = off % NBBY; |
313 | end = off + len + NBBY - 1; |
314 | ecp = cp + end / NBBY - 1; |
315 | ebi = end % NBBY + 1; |
316 | if (scp >= ecp) { |
317 | mask = ((1 << len) - 1) << sbi; |
318 | *scp &= ~mask; |
319 | } else { |
320 | mask = (1 << sbi) - 1; |
321 | *scp++ &= mask; |
322 | |
323 | mask = (1 << ebi) - 1; |
324 | *ecp &= ~mask; |
325 | |
326 | while (scp < ecp) |
327 | *scp++ = 0x00; |
328 | } |
329 | } |
330 | #endif |
331 | |
332 | /* |
333 | * Copy buffer to clist. |
334 | * Return number of bytes not transfered. |
335 | */ |
336 | int |
337 | b_to_q(const u_char *cp, int count, struct clist *clp) |
338 | { |
339 | int cc; |
340 | const u_char *p = cp; |
341 | int s; |
342 | |
343 | if (count <= 0) |
344 | return 0; |
345 | |
346 | s = spltty(); |
347 | if (clp->c_cc == clp->c_cn) |
348 | goto out; |
349 | |
350 | if (clp->c_cc == 0) { |
351 | if (!clp->c_cs) { |
352 | #if defined(DIAGNOSTIC) || 1 |
353 | printf("b_to_q: required clalloc\n" ); |
354 | #endif |
355 | if (clalloc(clp, clp->c_cn, 1)) |
356 | goto out; |
357 | } |
358 | clp->c_cf = clp->c_cl = clp->c_cs; |
359 | } |
360 | |
361 | /* optimize this while loop */ |
362 | while (count > 0 && clp->c_cc < clp->c_cn) { |
363 | cc = clp->c_ce - clp->c_cl; |
364 | if (clp->c_cf > clp->c_cl) |
365 | cc = clp->c_cf - clp->c_cl; |
366 | if (cc > count) |
367 | cc = count; |
368 | memcpy(clp->c_cl, p, cc); |
369 | if (clp->c_cq) { |
370 | #ifdef QBITS |
371 | clrbits(clp->c_cq, clp->c_cl - clp->c_cs, cc); |
372 | #else |
373 | memset(clp->c_cl - clp->c_cs + clp->c_cq, 0, cc); |
374 | #endif |
375 | } |
376 | p += cc; |
377 | count -= cc; |
378 | clp->c_cc += cc; |
379 | clp->c_cl += cc; |
380 | if (clp->c_cl == clp->c_ce) |
381 | clp->c_cl = clp->c_cs; |
382 | } |
383 | out: |
384 | splx(s); |
385 | return count; |
386 | } |
387 | |
388 | static int cc; |
389 | |
390 | /* |
391 | * Given a non-NULL pointer into the clist return the pointer |
392 | * to the next character in the list or return NULL if no more chars. |
393 | * |
394 | * Callers must not allow getc's to happen between firstc's and getc's |
395 | * so that the pointer becomes invalid. Note that interrupts are NOT |
396 | * masked. |
397 | */ |
398 | u_char * |
399 | nextc(struct clist *clp, u_char *cp, int *c) |
400 | { |
401 | |
402 | if (clp->c_cf == cp) { |
403 | /* |
404 | * First time initialization. |
405 | */ |
406 | cc = clp->c_cc; |
407 | } |
408 | if (cc == 0 || cp == NULL) |
409 | return NULL; |
410 | if (--cc == 0) |
411 | return NULL; |
412 | if (++cp == clp->c_ce) |
413 | cp = clp->c_cs; |
414 | *c = *cp & 0xff; |
415 | if (clp->c_cq) { |
416 | #ifdef QBITS |
417 | if (isset(clp->c_cq, cp - clp->c_cs)) |
418 | *c |= TTY_QUOTE; |
419 | #else |
420 | if (*(clp->c_cf - clp->c_cs + clp->c_cq)) |
421 | *c |= TTY_QUOTE; |
422 | #endif |
423 | } |
424 | return cp; |
425 | } |
426 | |
427 | /* |
428 | * Given a non-NULL pointer into the clist return the pointer |
429 | * to the first character in the list or return NULL if no more chars. |
430 | * |
431 | * Callers must not allow getc's to happen between firstc's and getc's |
432 | * so that the pointer becomes invalid. Note that interrupts are NOT |
433 | * masked. |
434 | * |
435 | * *c is set to the NEXT character |
436 | */ |
437 | u_char * |
438 | firstc(struct clist *clp, int *c) |
439 | { |
440 | u_char *cp; |
441 | |
442 | cc = clp->c_cc; |
443 | if (cc == 0) |
444 | return NULL; |
445 | cp = clp->c_cf; |
446 | *c = *cp & 0xff; |
447 | if (clp->c_cq) { |
448 | #ifdef QBITS |
449 | if (isset(clp->c_cq, cp - clp->c_cs)) |
450 | *c |= TTY_QUOTE; |
451 | #else |
452 | if (*(cp - clp->c_cs + clp->c_cq)) |
453 | *c |= TTY_QUOTE; |
454 | #endif |
455 | } |
456 | return clp->c_cf; |
457 | } |
458 | |
459 | /* |
460 | * Remove the last character in the clist and return it. |
461 | */ |
462 | int |
463 | unputc(struct clist *clp) |
464 | { |
465 | unsigned int c = -1; |
466 | int s; |
467 | |
468 | s = spltty(); |
469 | if (clp->c_cc == 0) |
470 | goto out; |
471 | |
472 | if (clp->c_cl == clp->c_cs) |
473 | clp->c_cl = clp->c_ce - 1; |
474 | else |
475 | --clp->c_cl; |
476 | clp->c_cc--; |
477 | |
478 | c = *clp->c_cl & 0xff; |
479 | if (clp->c_cq) { |
480 | #ifdef QBITS |
481 | if (isset(clp->c_cq, clp->c_cl - clp->c_cs)) |
482 | c |= TTY_QUOTE; |
483 | #else |
484 | if (*(clp->c_cf - clp->c_cs + clp->c_cq)) |
485 | c |= TTY_QUOTE; |
486 | #endif |
487 | } |
488 | if (clp->c_cc == 0) |
489 | clp->c_cf = clp->c_cl = (u_char *)0; |
490 | out: |
491 | splx(s); |
492 | return c; |
493 | } |
494 | |
495 | /* |
496 | * Put the chars in the from queue on the end of the to queue. |
497 | */ |
498 | void |
499 | catq(struct clist *from, struct clist *to) |
500 | { |
501 | int c; |
502 | |
503 | while ((c = getc(from)) != -1) |
504 | putc(c, to); |
505 | } |
506 | |