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