FreeBSD/Linux Kernel Cross Reference
sys/kern/tty_subr.c
1 /*
2 * Copyright (c) 1994, David Greenman
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
10 * disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: releng/5.0/sys/kern/tty_subr.c 92723 2002-03-19 21:25:46Z alfred $
28 */
29
30 /*
31 * clist support routines
32 */
33
34 #include <sys/param.h>
35 #include <sys/kernel.h>
36 #include <sys/systm.h>
37 #include <sys/malloc.h>
38 #include <sys/tty.h>
39 #include <sys/clist.h>
40
41 static void clist_init(void *);
42 SYSINIT(clist, SI_SUB_CLIST, SI_ORDER_FIRST, clist_init, NULL)
43
44 static struct cblock *cfreelist = 0;
45 int cfreecount = 0;
46 static int cslushcount;
47 static int ctotcount;
48
49 #ifndef INITIAL_CBLOCKS
50 #define INITIAL_CBLOCKS 50
51 #endif
52
53 static struct cblock *cblock_alloc(void);
54 static void cblock_alloc_cblocks(int number);
55 static void cblock_free(struct cblock *cblockp);
56 static void cblock_free_cblocks(int number);
57
58 #include "opt_ddb.h"
59 #ifdef DDB
60 #include <ddb/ddb.h>
61
62 DB_SHOW_COMMAND(cbstat, cbstat)
63 {
64 int cbsize = CBSIZE;
65
66 printf(
67 "tot = %d (active = %d, free = %d (reserved = %d, slush = %d))\n",
68 ctotcount * cbsize, ctotcount * cbsize - cfreecount, cfreecount,
69 cfreecount - cslushcount * cbsize, cslushcount * cbsize);
70 }
71 #endif /* DDB */
72
73 /*
74 * Called from init_main.c
75 */
76 /* ARGSUSED*/
77 static void
78 clist_init(dummy)
79 void *dummy;
80 {
81 /*
82 * Allocate an initial base set of cblocks as a 'slush'.
83 * We allocate non-slush cblocks with each initial ttyopen() and
84 * deallocate them with each ttyclose().
85 * We should adjust the slush allocation. This can't be done in
86 * the i/o routines because they are sometimes called from
87 * interrupt handlers when it may be unsafe to call malloc().
88 */
89 cblock_alloc_cblocks(cslushcount = INITIAL_CBLOCKS);
90 }
91
92 /*
93 * Remove a cblock from the cfreelist queue and return a pointer
94 * to it.
95 */
96 static __inline struct cblock *
97 cblock_alloc()
98 {
99 struct cblock *cblockp;
100
101 cblockp = cfreelist;
102 if (cblockp == NULL)
103 panic("clist reservation botch");
104 cfreelist = cblockp->c_next;
105 cblockp->c_next = NULL;
106 cfreecount -= CBSIZE;
107 return (cblockp);
108 }
109
110 /*
111 * Add a cblock to the cfreelist queue.
112 */
113 static __inline void
114 cblock_free(cblockp)
115 struct cblock *cblockp;
116 {
117 if (isset(cblockp->c_quote, CBQSIZE * NBBY - 1))
118 bzero(cblockp->c_quote, sizeof cblockp->c_quote);
119 cblockp->c_next = cfreelist;
120 cfreelist = cblockp;
121 cfreecount += CBSIZE;
122 }
123
124 /*
125 * Allocate some cblocks for the cfreelist queue.
126 */
127 static void
128 cblock_alloc_cblocks(number)
129 int number;
130 {
131 int i;
132 struct cblock *cbp;
133
134 for (i = 0; i < number; ++i) {
135 cbp = malloc(sizeof *cbp, M_TTYS, M_NOWAIT);
136 if (cbp == NULL) {
137 printf(
138 "cblock_alloc_cblocks: M_NOWAIT malloc failed, trying M_WAITOK\n");
139 cbp = malloc(sizeof *cbp, M_TTYS, M_WAITOK);
140 }
141 /*
142 * Freed cblocks have zero quotes and garbage elsewhere.
143 * Set the may-have-quote bit to force zeroing the quotes.
144 */
145 setbit(cbp->c_quote, CBQSIZE * NBBY - 1);
146 cblock_free(cbp);
147 }
148 ctotcount += number;
149 }
150
151 /*
152 * Set the cblock allocation policy for a a clist.
153 * Must be called in process context at spltty().
154 */
155 void
156 clist_alloc_cblocks(clistp, ccmax, ccreserved)
157 struct clist *clistp;
158 int ccmax;
159 int ccreserved;
160 {
161 int dcbr;
162
163 /*
164 * Allow for wasted space at the head.
165 */
166 if (ccmax != 0)
167 ccmax += CBSIZE - 1;
168 if (ccreserved != 0)
169 ccreserved += CBSIZE - 1;
170
171 clistp->c_cbmax = roundup(ccmax, CBSIZE) / CBSIZE;
172 dcbr = roundup(ccreserved, CBSIZE) / CBSIZE - clistp->c_cbreserved;
173 if (dcbr >= 0)
174 cblock_alloc_cblocks(dcbr);
175 else {
176 if (clistp->c_cbreserved + dcbr < clistp->c_cbcount)
177 dcbr = clistp->c_cbcount - clistp->c_cbreserved;
178 cblock_free_cblocks(-dcbr);
179 }
180 clistp->c_cbreserved += dcbr;
181 }
182
183 /*
184 * Free some cblocks from the cfreelist queue back to the
185 * system malloc pool.
186 */
187 static void
188 cblock_free_cblocks(number)
189 int number;
190 {
191 int i;
192
193 for (i = 0; i < number; ++i)
194 free(cblock_alloc(), M_TTYS);
195 ctotcount -= number;
196 }
197
198 /*
199 * Free the cblocks reserved for a clist.
200 * Must be called at spltty().
201 */
202 void
203 clist_free_cblocks(clistp)
204 struct clist *clistp;
205 {
206 if (clistp->c_cbcount != 0)
207 panic("freeing active clist cblocks");
208 cblock_free_cblocks(clistp->c_cbreserved);
209 clistp->c_cbmax = 0;
210 clistp->c_cbreserved = 0;
211 }
212
213 /*
214 * Get a character from the head of a clist.
215 */
216 int
217 getc(clistp)
218 struct clist *clistp;
219 {
220 int chr = -1;
221 int s;
222 struct cblock *cblockp;
223
224 s = spltty();
225
226 /* If there are characters in the list, get one */
227 if (clistp->c_cc) {
228 cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND);
229 chr = (u_char)*clistp->c_cf;
230
231 /*
232 * If this char is quoted, set the flag.
233 */
234 if (isset(cblockp->c_quote, clistp->c_cf - (char *)cblockp->c_info))
235 chr |= TTY_QUOTE;
236
237 /*
238 * Advance to next character.
239 */
240 clistp->c_cf++;
241 clistp->c_cc--;
242 /*
243 * If we have advanced the 'first' character pointer
244 * past the end of this cblock, advance to the next one.
245 * If there are no more characters, set the first and
246 * last pointers to NULL. In either case, free the
247 * current cblock.
248 */
249 if ((clistp->c_cf >= (char *)(cblockp+1)) || (clistp->c_cc == 0)) {
250 if (clistp->c_cc > 0) {
251 clistp->c_cf = cblockp->c_next->c_info;
252 } else {
253 clistp->c_cf = clistp->c_cl = NULL;
254 }
255 cblock_free(cblockp);
256 if (--clistp->c_cbcount >= clistp->c_cbreserved)
257 ++cslushcount;
258 }
259 }
260
261 splx(s);
262 return (chr);
263 }
264
265 /*
266 * Copy 'amount' of chars, beginning at head of clist 'clistp' to
267 * destination linear buffer 'dest'. Return number of characters
268 * actually copied.
269 */
270 int
271 q_to_b(clistp, dest, amount)
272 struct clist *clistp;
273 char *dest;
274 int amount;
275 {
276 struct cblock *cblockp;
277 struct cblock *cblockn;
278 char *dest_orig = dest;
279 int numc;
280 int s;
281
282 s = spltty();
283
284 while (clistp && amount && (clistp->c_cc > 0)) {
285 cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND);
286 cblockn = cblockp + 1; /* pointer arithmetic! */
287 numc = min(amount, (char *)cblockn - clistp->c_cf);
288 numc = min(numc, clistp->c_cc);
289 bcopy(clistp->c_cf, dest, numc);
290 amount -= numc;
291 clistp->c_cf += numc;
292 clistp->c_cc -= numc;
293 dest += numc;
294 /*
295 * If this cblock has been emptied, advance to the next
296 * one. If there are no more characters, set the first
297 * and last pointer to NULL. In either case, free the
298 * current cblock.
299 */
300 if ((clistp->c_cf >= (char *)cblockn) || (clistp->c_cc == 0)) {
301 if (clistp->c_cc > 0) {
302 clistp->c_cf = cblockp->c_next->c_info;
303 } else {
304 clistp->c_cf = clistp->c_cl = NULL;
305 }
306 cblock_free(cblockp);
307 if (--clistp->c_cbcount >= clistp->c_cbreserved)
308 ++cslushcount;
309 }
310 }
311
312 splx(s);
313 return (dest - dest_orig);
314 }
315
316 /*
317 * Flush 'amount' of chars, beginning at head of clist 'clistp'.
318 */
319 void
320 ndflush(clistp, amount)
321 struct clist *clistp;
322 int amount;
323 {
324 struct cblock *cblockp;
325 struct cblock *cblockn;
326 int numc;
327 int s;
328
329 s = spltty();
330
331 while (amount && (clistp->c_cc > 0)) {
332 cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND);
333 cblockn = cblockp + 1; /* pointer arithmetic! */
334 numc = min(amount, (char *)cblockn - clistp->c_cf);
335 numc = min(numc, clistp->c_cc);
336 amount -= numc;
337 clistp->c_cf += numc;
338 clistp->c_cc -= numc;
339 /*
340 * If this cblock has been emptied, advance to the next
341 * one. If there are no more characters, set the first
342 * and last pointer to NULL. In either case, free the
343 * current cblock.
344 */
345 if ((clistp->c_cf >= (char *)cblockn) || (clistp->c_cc == 0)) {
346 if (clistp->c_cc > 0) {
347 clistp->c_cf = cblockp->c_next->c_info;
348 } else {
349 clistp->c_cf = clistp->c_cl = NULL;
350 }
351 cblock_free(cblockp);
352 if (--clistp->c_cbcount >= clistp->c_cbreserved)
353 ++cslushcount;
354 }
355 }
356
357 splx(s);
358 }
359
360 /*
361 * Add a character to the end of a clist. Return -1 is no
362 * more clists, or 0 for success.
363 */
364 int
365 putc(chr, clistp)
366 int chr;
367 struct clist *clistp;
368 {
369 struct cblock *cblockp;
370 int s;
371
372 s = spltty();
373
374 if (clistp->c_cl == NULL) {
375 if (clistp->c_cbreserved < 1) {
376 splx(s);
377 printf("putc to a clist with no reserved cblocks\n");
378 return (-1); /* nothing done */
379 }
380 cblockp = cblock_alloc();
381 clistp->c_cbcount = 1;
382 clistp->c_cf = clistp->c_cl = cblockp->c_info;
383 clistp->c_cc = 0;
384 } else {
385 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND);
386 if (((intptr_t)clistp->c_cl & CROUND) == 0) {
387 struct cblock *prev = (cblockp - 1);
388
389 if (clistp->c_cbcount >= clistp->c_cbreserved) {
390 if (clistp->c_cbcount >= clistp->c_cbmax
391 || cslushcount <= 0) {
392 splx(s);
393 return (-1);
394 }
395 --cslushcount;
396 }
397 cblockp = cblock_alloc();
398 clistp->c_cbcount++;
399 prev->c_next = cblockp;
400 clistp->c_cl = cblockp->c_info;
401 }
402 }
403
404 /*
405 * If this character is quoted, set the quote bit, if not, clear it.
406 */
407 if (chr & TTY_QUOTE) {
408 setbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info);
409 /*
410 * Use one of the spare quote bits to record that something
411 * may be quoted.
412 */
413 setbit(cblockp->c_quote, CBQSIZE * NBBY - 1);
414 } else
415 clrbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info);
416
417 *clistp->c_cl++ = chr;
418 clistp->c_cc++;
419
420 splx(s);
421 return (0);
422 }
423
424 /*
425 * Copy data from linear buffer to clist chain. Return the
426 * number of characters not copied.
427 */
428 int
429 b_to_q(src, amount, clistp)
430 char *src;
431 int amount;
432 struct clist *clistp;
433 {
434 struct cblock *cblockp;
435 char *firstbyte, *lastbyte;
436 u_char startmask, endmask;
437 int startbit, endbit, num_between, numc;
438 int s;
439
440 /*
441 * Avoid allocating an initial cblock and then not using it.
442 * c_cc == 0 must imply c_cbount == 0.
443 */
444 if (amount <= 0)
445 return (amount);
446
447 s = spltty();
448
449 /*
450 * If there are no cblocks assigned to this clist yet,
451 * then get one.
452 */
453 if (clistp->c_cl == NULL) {
454 if (clistp->c_cbreserved < 1) {
455 splx(s);
456 printf("b_to_q to a clist with no reserved cblocks.\n");
457 return (amount); /* nothing done */
458 }
459 cblockp = cblock_alloc();
460 clistp->c_cbcount = 1;
461 clistp->c_cf = clistp->c_cl = cblockp->c_info;
462 clistp->c_cc = 0;
463 } else {
464 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND);
465 }
466
467 while (amount) {
468 /*
469 * Get another cblock if needed.
470 */
471 if (((intptr_t)clistp->c_cl & CROUND) == 0) {
472 struct cblock *prev = cblockp - 1;
473
474 if (clistp->c_cbcount >= clistp->c_cbreserved) {
475 if (clistp->c_cbcount >= clistp->c_cbmax
476 || cslushcount <= 0) {
477 splx(s);
478 return (amount);
479 }
480 --cslushcount;
481 }
482 cblockp = cblock_alloc();
483 clistp->c_cbcount++;
484 prev->c_next = cblockp;
485 clistp->c_cl = cblockp->c_info;
486 }
487
488 /*
489 * Copy a chunk of the linear buffer up to the end
490 * of this cblock.
491 */
492 numc = min(amount, (char *)(cblockp + 1) - clistp->c_cl);
493 bcopy(src, clistp->c_cl, numc);
494
495 /*
496 * Clear quote bits if they aren't known to be clear.
497 * The following could probably be made into a separate
498 * "bitzero()" routine, but why bother?
499 */
500 if (isset(cblockp->c_quote, CBQSIZE * NBBY - 1)) {
501 startbit = clistp->c_cl - (char *)cblockp->c_info;
502 endbit = startbit + numc - 1;
503
504 firstbyte = (u_char *)cblockp->c_quote + (startbit / NBBY);
505 lastbyte = (u_char *)cblockp->c_quote + (endbit / NBBY);
506
507 /*
508 * Calculate mask of bits to preserve in first and
509 * last bytes.
510 */
511 startmask = NBBY - (startbit % NBBY);
512 startmask = 0xff >> startmask;
513 endmask = (endbit % NBBY);
514 endmask = 0xff << (endmask + 1);
515
516 if (firstbyte != lastbyte) {
517 *firstbyte &= startmask;
518 *lastbyte &= endmask;
519
520 num_between = lastbyte - firstbyte - 1;
521 if (num_between)
522 bzero(firstbyte + 1, num_between);
523 } else {
524 *firstbyte &= (startmask | endmask);
525 }
526 }
527
528 /*
529 * ...and update pointer for the next chunk.
530 */
531 src += numc;
532 clistp->c_cl += numc;
533 clistp->c_cc += numc;
534 amount -= numc;
535 /*
536 * If we go through the loop again, it's always
537 * for data in the next cblock, so by adding one (cblock),
538 * (which makes the pointer 1 beyond the end of this
539 * cblock) we prepare for the assignment of 'prev'
540 * above.
541 */
542 cblockp += 1;
543
544 }
545
546 splx(s);
547 return (amount);
548 }
549
550 /*
551 * Get the next character in the clist. Store it at dst. Don't
552 * advance any clist pointers, but return a pointer to the next
553 * character position.
554 */
555 char *
556 nextc(clistp, cp, dst)
557 struct clist *clistp;
558 char *cp;
559 int *dst;
560 {
561 struct cblock *cblockp;
562
563 ++cp;
564 /*
565 * See if the next character is beyond the end of
566 * the clist.
567 */
568 if (clistp->c_cc && (cp != clistp->c_cl)) {
569 /*
570 * If the next character is beyond the end of this
571 * cblock, advance to the next cblock.
572 */
573 if (((intptr_t)cp & CROUND) == 0)
574 cp = ((struct cblock *)cp - 1)->c_next->c_info;
575 cblockp = (struct cblock *)((intptr_t)cp & ~CROUND);
576
577 /*
578 * Get the character. Set the quote flag if this character
579 * is quoted.
580 */
581 *dst = (u_char)*cp | (isset(cblockp->c_quote, cp - (char *)cblockp->c_info) ? TTY_QUOTE : 0);
582
583 return (cp);
584 }
585
586 return (NULL);
587 }
588
589 /*
590 * "Unput" a character from a clist.
591 */
592 int
593 unputc(clistp)
594 struct clist *clistp;
595 {
596 struct cblock *cblockp = 0, *cbp = 0;
597 int s;
598 int chr = -1;
599
600
601 s = spltty();
602
603 if (clistp->c_cc) {
604 --clistp->c_cc;
605 --clistp->c_cl;
606
607 chr = (u_char)*clistp->c_cl;
608
609 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND);
610
611 /*
612 * Set quote flag if this character was quoted.
613 */
614 if (isset(cblockp->c_quote, (u_char *)clistp->c_cl - cblockp->c_info))
615 chr |= TTY_QUOTE;
616
617 /*
618 * If all of the characters have been unput in this
619 * cblock, then find the previous one and free this
620 * one.
621 */
622 if (clistp->c_cc && (clistp->c_cl <= (char *)cblockp->c_info)) {
623 cbp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND);
624
625 while (cbp->c_next != cblockp)
626 cbp = cbp->c_next;
627
628 /*
629 * When the previous cblock is at the end, the 'last'
630 * pointer always points (invalidly) one past.
631 */
632 clistp->c_cl = (char *)(cbp+1);
633 cblock_free(cblockp);
634 if (--clistp->c_cbcount >= clistp->c_cbreserved)
635 ++cslushcount;
636 cbp->c_next = NULL;
637 }
638 }
639
640 /*
641 * If there are no more characters on the list, then
642 * free the last cblock.
643 */
644 if ((clistp->c_cc == 0) && clistp->c_cl) {
645 cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND);
646 cblock_free(cblockp);
647 if (--clistp->c_cbcount >= clistp->c_cbreserved)
648 ++cslushcount;
649 clistp->c_cf = clistp->c_cl = NULL;
650 }
651
652 splx(s);
653 return (chr);
654 }
655
656 /*
657 * Move characters in source clist to destination clist,
658 * preserving quote bits.
659 */
660 void
661 catq(src_clistp, dest_clistp)
662 struct clist *src_clistp, *dest_clistp;
663 {
664 int chr, s;
665
666 s = spltty();
667 /*
668 * If the destination clist is empty (has no cblocks atttached),
669 * and there are no possible complications with the resource counters,
670 * then we simply assign the current clist to the destination.
671 */
672 if (!dest_clistp->c_cf
673 && src_clistp->c_cbcount <= src_clistp->c_cbmax
674 && src_clistp->c_cbcount <= dest_clistp->c_cbmax) {
675 dest_clistp->c_cf = src_clistp->c_cf;
676 dest_clistp->c_cl = src_clistp->c_cl;
677 src_clistp->c_cf = src_clistp->c_cl = NULL;
678
679 dest_clistp->c_cc = src_clistp->c_cc;
680 src_clistp->c_cc = 0;
681 dest_clistp->c_cbcount = src_clistp->c_cbcount;
682 src_clistp->c_cbcount = 0;
683
684 splx(s);
685 return;
686 }
687
688 splx(s);
689
690 /*
691 * XXX This should probably be optimized to more than one
692 * character at a time.
693 */
694 while ((chr = getc(src_clistp)) != -1)
695 putc(chr, dest_clistp);
696 }
Cache object: eb243b13342244c25922ea10e6140df1
|