Initial import from FreeBSD RELENG_4:
[dragonfly.git] / 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.32 1999/08/28 00:46:21 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/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         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 "clist_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 seperate
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 }