summaryrefslogtreecommitdiffstats
path: root/sys/kern/tty_subr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/tty_subr.c')
-rw-r--r--sys/kern/tty_subr.c696
1 files changed, 696 insertions, 0 deletions
diff --git a/sys/kern/tty_subr.c b/sys/kern/tty_subr.c
new file mode 100644
index 0000000..78bb231
--- /dev/null
+++ b/sys/kern/tty_subr.c
@@ -0,0 +1,696 @@
+/*
+ * Copyright (c) 1994, David Greenman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * clist support routines
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/tty.h>
+#include <sys/clist.h>
+
+static void clist_init(void *);
+SYSINIT(clist, SI_SUB_CLIST, SI_ORDER_FIRST, clist_init, NULL)
+
+static struct cblock *cfreelist = 0;
+int cfreecount = 0;
+static int cslushcount;
+static int ctotcount;
+
+#ifndef INITIAL_CBLOCKS
+#define INITIAL_CBLOCKS 50
+#endif
+
+static struct cblock *cblock_alloc(void);
+static void cblock_alloc_cblocks(int number);
+static void cblock_free(struct cblock *cblockp);
+static void cblock_free_cblocks(int number);
+
+#include "opt_ddb.h"
+#ifdef DDB
+#include <ddb/ddb.h>
+
+DB_SHOW_COMMAND(cbstat, cbstat)
+{
+ int cbsize = CBSIZE;
+
+ printf(
+ "tot = %d (active = %d, free = %d (reserved = %d, slush = %d))\n",
+ ctotcount * cbsize, ctotcount * cbsize - cfreecount, cfreecount,
+ cfreecount - cslushcount * cbsize, cslushcount * cbsize);
+}
+#endif /* DDB */
+
+/*
+ * Called from init_main.c
+ */
+/* ARGSUSED*/
+static void
+clist_init(dummy)
+ void *dummy;
+{
+ /*
+ * Allocate an initial base set of cblocks as a 'slush'.
+ * We allocate non-slush cblocks with each initial ttyopen() and
+ * deallocate them with each ttyclose().
+ * We should adjust the slush allocation. This can't be done in
+ * the i/o routines because they are sometimes called from
+ * interrupt handlers when it may be unsafe to call malloc().
+ */
+ cblock_alloc_cblocks(cslushcount = INITIAL_CBLOCKS);
+}
+
+/*
+ * Remove a cblock from the cfreelist queue and return a pointer
+ * to it.
+ */
+static __inline struct cblock *
+cblock_alloc()
+{
+ struct cblock *cblockp;
+
+ cblockp = cfreelist;
+ if (cblockp == NULL)
+ panic("clist reservation botch");
+ cfreelist = cblockp->c_next;
+ cblockp->c_next = NULL;
+ cfreecount -= CBSIZE;
+ return (cblockp);
+}
+
+/*
+ * Add a cblock to the cfreelist queue.
+ */
+static __inline void
+cblock_free(cblockp)
+ struct cblock *cblockp;
+{
+ if (isset(cblockp->c_quote, CBQSIZE * NBBY - 1))
+ bzero(cblockp->c_quote, sizeof cblockp->c_quote);
+ cblockp->c_next = cfreelist;
+ cfreelist = cblockp;
+ cfreecount += CBSIZE;
+}
+
+/*
+ * Allocate some cblocks for the cfreelist queue.
+ */
+static void
+cblock_alloc_cblocks(number)
+ int number;
+{
+ int i;
+ struct cblock *cbp;
+
+ for (i = 0; i < number; ++i) {
+ cbp = malloc(sizeof *cbp, M_TTYS, M_NOWAIT);
+ if (cbp == NULL) {
+ printf(
+"cblock_alloc_cblocks: M_NOWAIT malloc failed, trying M_WAITOK\n");
+ cbp = malloc(sizeof *cbp, M_TTYS, M_WAITOK);
+ }
+ /*
+ * Freed cblocks have zero quotes and garbage elsewhere.
+ * Set the may-have-quote bit to force zeroing the quotes.
+ */
+ setbit(cbp->c_quote, CBQSIZE * NBBY - 1);
+ cblock_free(cbp);
+ }
+ ctotcount += number;
+}
+
+/*
+ * Set the cblock allocation policy for a a clist.
+ * Must be called in process context at spltty().
+ */
+void
+clist_alloc_cblocks(clistp, ccmax, ccreserved)
+ struct clist *clistp;
+ int ccmax;
+ int ccreserved;
+{
+ int dcbr;
+
+ /*
+ * Allow for wasted space at the head.
+ */
+ if (ccmax != 0)
+ ccmax += CBSIZE - 1;
+ if (ccreserved != 0)
+ ccreserved += CBSIZE - 1;
+
+ clistp->c_cbmax = roundup(ccmax, CBSIZE) / CBSIZE;
+ dcbr = roundup(ccreserved, CBSIZE) / CBSIZE - clistp->c_cbreserved;
+ if (dcbr >= 0)
+ cblock_alloc_cblocks(dcbr);
+ else {
+ if (clistp->c_cbreserved + dcbr < clistp->c_cbcount)
+ dcbr = clistp->c_cbcount - clistp->c_cbreserved;
+ cblock_free_cblocks(-dcbr);
+ }
+ clistp->c_cbreserved += dcbr;
+}
+
+/*
+ * Free some cblocks from the cfreelist queue back to the
+ * system malloc pool.
+ */
+static void
+cblock_free_cblocks(number)
+ int number;
+{
+ int i;
+
+ for (i = 0; i < number; ++i)
+ free(cblock_alloc(), M_TTYS);
+ ctotcount -= number;
+}
+
+/*
+ * Free the cblocks reserved for a clist.
+ * Must be called at spltty().
+ */
+void
+clist_free_cblocks(clistp)
+ struct clist *clistp;
+{
+ if (clistp->c_cbcount != 0)
+ panic("freeing active clist cblocks");
+ cblock_free_cblocks(clistp->c_cbreserved);
+ clistp->c_cbmax = 0;
+ clistp->c_cbreserved = 0;
+}
+
+/*
+ * Get a character from the head of a clist.
+ */
+int
+getc(clistp)
+ struct clist *clistp;
+{
+ int chr = -1;
+ int s;
+ struct cblock *cblockp;
+
+ s = spltty();
+
+ /* If there are characters in the list, get one */
+ if (clistp->c_cc) {
+ cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND);
+ chr = (u_char)*clistp->c_cf;
+
+ /*
+ * If this char is quoted, set the flag.
+ */
+ if (isset(cblockp->c_quote, clistp->c_cf - (char *)cblockp->c_info))
+ chr |= TTY_QUOTE;
+
+ /*
+ * Advance to next character.
+ */
+ clistp->c_cf++;
+ clistp->c_cc--;
+ /*
+ * If we have advanced the 'first' character pointer
+ * past the end of this cblock, advance to the next one.
+ * If there are no more characters, set the first and
+ * last pointers to NULL. In either case, free the
+ * current cblock.
+ */
+ if ((clistp->c_cf >= (char *)(cblockp+1)) || (clistp->c_cc == 0)) {
+ if (clistp->c_cc > 0) {
+ clistp->c_cf = cblockp->c_next->c_info;
+ } else {
+ clistp->c_cf = clistp->c_cl = NULL;
+ }
+ cblock_free(cblockp);
+ if (--clistp->c_cbcount >= clistp->c_cbreserved)
+ ++cslushcount;
+ }
+ }
+
+ splx(s);
+ return (chr);
+}
+
+/*
+ * Copy 'amount' of chars, beginning at head of clist 'clistp' to
+ * destination linear buffer 'dest'. Return number of characters
+ * actually copied.
+ */
+int
+q_to_b(clistp, dest, amount)
+ struct clist *clistp;
+ char *dest;
+ int amount;
+{
+ struct cblock *cblockp;
+ struct cblock *cblockn;
+ char *dest_orig = dest;
+ int numc;
+ int s;
+
+ s = spltty();
+
+ while (clistp && amount && (clistp->c_cc > 0)) {
+ cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND);
+ cblockn = cblockp + 1; /* pointer arithmetic! */
+ numc = min(amount, (char *)cblockn - clistp->c_cf);
+ numc = min(numc, clistp->c_cc);
+ bcopy(clistp->c_cf, dest, numc);
+ amount -= numc;
+ clistp->c_cf += numc;
+ clistp->c_cc -= numc;
+ dest += numc;
+ /*
+ * If this cblock has been emptied, advance to the next
+ * one. If there are no more characters, set the first
+ * and last pointer to NULL. In either case, free the
+ * current cblock.
+ */
+ if ((clistp->c_cf >= (char *)cblockn) || (clistp->c_cc == 0)) {
+ if (clistp->c_cc > 0) {
+ clistp->c_cf = cblockp->c_next->c_info;
+ } else {
+ clistp->c_cf = clistp->c_cl = NULL;
+ }
+ cblock_free(cblockp);
+ if (--clistp->c_cbcount >= clistp->c_cbreserved)
+ ++cslushcount;
+ }
+ }
+
+ splx(s);
+ return (dest - dest_orig);
+}
+
+/*
+ * Flush 'amount' of chars, beginning at head of clist 'clistp'.
+ */
+void
+ndflush(clistp, amount)
+ struct clist *clistp;
+ int amount;
+{
+ struct cblock *cblockp;
+ struct cblock *cblockn;
+ int numc;
+ int s;
+
+ s = spltty();
+
+ while (amount && (clistp->c_cc > 0)) {
+ cblockp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND);
+ cblockn = cblockp + 1; /* pointer arithmetic! */
+ numc = min(amount, (char *)cblockn - clistp->c_cf);
+ numc = min(numc, clistp->c_cc);
+ amount -= numc;
+ clistp->c_cf += numc;
+ clistp->c_cc -= numc;
+ /*
+ * If this cblock has been emptied, advance to the next
+ * one. If there are no more characters, set the first
+ * and last pointer to NULL. In either case, free the
+ * current cblock.
+ */
+ if ((clistp->c_cf >= (char *)cblockn) || (clistp->c_cc == 0)) {
+ if (clistp->c_cc > 0) {
+ clistp->c_cf = cblockp->c_next->c_info;
+ } else {
+ clistp->c_cf = clistp->c_cl = NULL;
+ }
+ cblock_free(cblockp);
+ if (--clistp->c_cbcount >= clistp->c_cbreserved)
+ ++cslushcount;
+ }
+ }
+
+ splx(s);
+}
+
+/*
+ * Add a character to the end of a clist. Return -1 is no
+ * more clists, or 0 for success.
+ */
+int
+putc(chr, clistp)
+ int chr;
+ struct clist *clistp;
+{
+ struct cblock *cblockp;
+ int s;
+
+ s = spltty();
+
+ if (clistp->c_cl == NULL) {
+ if (clistp->c_cbreserved < 1) {
+ splx(s);
+ printf("putc to a clist with no reserved cblocks\n");
+ return (-1); /* nothing done */
+ }
+ cblockp = cblock_alloc();
+ clistp->c_cbcount = 1;
+ clistp->c_cf = clistp->c_cl = cblockp->c_info;
+ clistp->c_cc = 0;
+ } else {
+ cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND);
+ if (((intptr_t)clistp->c_cl & CROUND) == 0) {
+ struct cblock *prev = (cblockp - 1);
+
+ if (clistp->c_cbcount >= clistp->c_cbreserved) {
+ if (clistp->c_cbcount >= clistp->c_cbmax
+ || cslushcount <= 0) {
+ splx(s);
+ return (-1);
+ }
+ --cslushcount;
+ }
+ cblockp = cblock_alloc();
+ clistp->c_cbcount++;
+ prev->c_next = cblockp;
+ clistp->c_cl = cblockp->c_info;
+ }
+ }
+
+ /*
+ * If this character is quoted, set the quote bit, if not, clear it.
+ */
+ if (chr & TTY_QUOTE) {
+ setbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info);
+ /*
+ * Use one of the spare quote bits to record that something
+ * may be quoted.
+ */
+ setbit(cblockp->c_quote, CBQSIZE * NBBY - 1);
+ } else
+ clrbit(cblockp->c_quote, clistp->c_cl - (char *)cblockp->c_info);
+
+ *clistp->c_cl++ = chr;
+ clistp->c_cc++;
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Copy data from linear buffer to clist chain. Return the
+ * number of characters not copied.
+ */
+int
+b_to_q(src, amount, clistp)
+ char *src;
+ int amount;
+ struct clist *clistp;
+{
+ struct cblock *cblockp;
+ char *firstbyte, *lastbyte;
+ u_char startmask, endmask;
+ int startbit, endbit, num_between, numc;
+ int s;
+
+ /*
+ * Avoid allocating an initial cblock and then not using it.
+ * c_cc == 0 must imply c_cbount == 0.
+ */
+ if (amount <= 0)
+ return (amount);
+
+ s = spltty();
+
+ /*
+ * If there are no cblocks assigned to this clist yet,
+ * then get one.
+ */
+ if (clistp->c_cl == NULL) {
+ if (clistp->c_cbreserved < 1) {
+ splx(s);
+ printf("b_to_q to a clist with no reserved cblocks.\n");
+ return (amount); /* nothing done */
+ }
+ cblockp = cblock_alloc();
+ clistp->c_cbcount = 1;
+ clistp->c_cf = clistp->c_cl = cblockp->c_info;
+ clistp->c_cc = 0;
+ } else {
+ cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND);
+ }
+
+ while (amount) {
+ /*
+ * Get another cblock if needed.
+ */
+ if (((intptr_t)clistp->c_cl & CROUND) == 0) {
+ struct cblock *prev = cblockp - 1;
+
+ if (clistp->c_cbcount >= clistp->c_cbreserved) {
+ if (clistp->c_cbcount >= clistp->c_cbmax
+ || cslushcount <= 0) {
+ splx(s);
+ return (amount);
+ }
+ --cslushcount;
+ }
+ cblockp = cblock_alloc();
+ clistp->c_cbcount++;
+ prev->c_next = cblockp;
+ clistp->c_cl = cblockp->c_info;
+ }
+
+ /*
+ * Copy a chunk of the linear buffer up to the end
+ * of this cblock.
+ */
+ numc = min(amount, (char *)(cblockp + 1) - clistp->c_cl);
+ bcopy(src, clistp->c_cl, numc);
+
+ /*
+ * Clear quote bits if they aren't known to be clear.
+ * The following could probably be made into a separate
+ * "bitzero()" routine, but why bother?
+ */
+ if (isset(cblockp->c_quote, CBQSIZE * NBBY - 1)) {
+ startbit = clistp->c_cl - (char *)cblockp->c_info;
+ endbit = startbit + numc - 1;
+
+ firstbyte = (u_char *)cblockp->c_quote + (startbit / NBBY);
+ lastbyte = (u_char *)cblockp->c_quote + (endbit / NBBY);
+
+ /*
+ * Calculate mask of bits to preserve in first and
+ * last bytes.
+ */
+ startmask = NBBY - (startbit % NBBY);
+ startmask = 0xff >> startmask;
+ endmask = (endbit % NBBY);
+ endmask = 0xff << (endmask + 1);
+
+ if (firstbyte != lastbyte) {
+ *firstbyte &= startmask;
+ *lastbyte &= endmask;
+
+ num_between = lastbyte - firstbyte - 1;
+ if (num_between)
+ bzero(firstbyte + 1, num_between);
+ } else {
+ *firstbyte &= (startmask | endmask);
+ }
+ }
+
+ /*
+ * ...and update pointer for the next chunk.
+ */
+ src += numc;
+ clistp->c_cl += numc;
+ clistp->c_cc += numc;
+ amount -= numc;
+ /*
+ * If we go through the loop again, it's always
+ * for data in the next cblock, so by adding one (cblock),
+ * (which makes the pointer 1 beyond the end of this
+ * cblock) we prepare for the assignment of 'prev'
+ * above.
+ */
+ cblockp += 1;
+
+ }
+
+ splx(s);
+ return (amount);
+}
+
+/*
+ * Get the next character in the clist. Store it at dst. Don't
+ * advance any clist pointers, but return a pointer to the next
+ * character position.
+ */
+char *
+nextc(clistp, cp, dst)
+ struct clist *clistp;
+ char *cp;
+ int *dst;
+{
+ struct cblock *cblockp;
+
+ ++cp;
+ /*
+ * See if the next character is beyond the end of
+ * the clist.
+ */
+ if (clistp->c_cc && (cp != clistp->c_cl)) {
+ /*
+ * If the next character is beyond the end of this
+ * cblock, advance to the next cblock.
+ */
+ if (((intptr_t)cp & CROUND) == 0)
+ cp = ((struct cblock *)cp - 1)->c_next->c_info;
+ cblockp = (struct cblock *)((intptr_t)cp & ~CROUND);
+
+ /*
+ * Get the character. Set the quote flag if this character
+ * is quoted.
+ */
+ *dst = (u_char)*cp | (isset(cblockp->c_quote, cp - (char *)cblockp->c_info) ? TTY_QUOTE : 0);
+
+ return (cp);
+ }
+
+ return (NULL);
+}
+
+/*
+ * "Unput" a character from a clist.
+ */
+int
+unputc(clistp)
+ struct clist *clistp;
+{
+ struct cblock *cblockp = 0, *cbp = 0;
+ int s;
+ int chr = -1;
+
+
+ s = spltty();
+
+ if (clistp->c_cc) {
+ --clistp->c_cc;
+ --clistp->c_cl;
+
+ chr = (u_char)*clistp->c_cl;
+
+ cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND);
+
+ /*
+ * Set quote flag if this character was quoted.
+ */
+ if (isset(cblockp->c_quote, (u_char *)clistp->c_cl - cblockp->c_info))
+ chr |= TTY_QUOTE;
+
+ /*
+ * If all of the characters have been unput in this
+ * cblock, then find the previous one and free this
+ * one.
+ */
+ if (clistp->c_cc && (clistp->c_cl <= (char *)cblockp->c_info)) {
+ cbp = (struct cblock *)((intptr_t)clistp->c_cf & ~CROUND);
+
+ while (cbp->c_next != cblockp)
+ cbp = cbp->c_next;
+
+ /*
+ * When the previous cblock is at the end, the 'last'
+ * pointer always points (invalidly) one past.
+ */
+ clistp->c_cl = (char *)(cbp+1);
+ cblock_free(cblockp);
+ if (--clistp->c_cbcount >= clistp->c_cbreserved)
+ ++cslushcount;
+ cbp->c_next = NULL;
+ }
+ }
+
+ /*
+ * If there are no more characters on the list, then
+ * free the last cblock.
+ */
+ if ((clistp->c_cc == 0) && clistp->c_cl) {
+ cblockp = (struct cblock *)((intptr_t)clistp->c_cl & ~CROUND);
+ cblock_free(cblockp);
+ if (--clistp->c_cbcount >= clistp->c_cbreserved)
+ ++cslushcount;
+ clistp->c_cf = clistp->c_cl = NULL;
+ }
+
+ splx(s);
+ return (chr);
+}
+
+/*
+ * Move characters in source clist to destination clist,
+ * preserving quote bits.
+ */
+void
+catq(src_clistp, dest_clistp)
+ struct clist *src_clistp, *dest_clistp;
+{
+ int chr, s;
+
+ s = spltty();
+ /*
+ * If the destination clist is empty (has no cblocks atttached),
+ * and there are no possible complications with the resource counters,
+ * then we simply assign the current clist to the destination.
+ */
+ if (!dest_clistp->c_cf
+ && src_clistp->c_cbcount <= src_clistp->c_cbmax
+ && src_clistp->c_cbcount <= dest_clistp->c_cbmax) {
+ dest_clistp->c_cf = src_clistp->c_cf;
+ dest_clistp->c_cl = src_clistp->c_cl;
+ src_clistp->c_cf = src_clistp->c_cl = NULL;
+
+ dest_clistp->c_cc = src_clistp->c_cc;
+ src_clistp->c_cc = 0;
+ dest_clistp->c_cbcount = src_clistp->c_cbcount;
+ src_clistp->c_cbcount = 0;
+
+ splx(s);
+ return;
+ }
+
+ splx(s);
+
+ /*
+ * XXX This should probably be optimized to more than one
+ * character at a time.
+ */
+ while ((chr = getc(src_clistp)) != -1)
+ putc(chr, dest_clistp);
+}
OpenPOWER on IntegriCloud