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