summaryrefslogtreecommitdiff
path: root/sys/dev/cons
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/cons')
-rw-r--r--sys/dev/cons/cons.c414
-rw-r--r--sys/dev/cons/cons_ansi.c171
-rw-r--r--sys/dev/cons/cons_buf.c210
3 files changed, 718 insertions, 77 deletions
diff --git a/sys/dev/cons/cons.c b/sys/dev/cons/cons.c
index 398b6ca..8470a60 100644
--- a/sys/dev/cons/cons.c
+++ b/sys/dev/cons/cons.c
@@ -31,6 +31,8 @@
#include <sys/types.h>
#include <sys/ascii.h>
#include <sys/device.h>
+#include <sys/errno.h>
+#include <sys/panic.h>
#include <dev/video/fbdev.h>
#include <dev/cons/font.h>
#include <dev/cons/cons.h>
@@ -38,6 +40,12 @@
#include <vm/dynalloc.h>
#include <string.h>
+#define HIDE_CURSOR(SCR) \
+ cons_draw_cursor((SCR), (SCR)->bg)
+
+#define SHOW_CURSOR(SCR) \
+ cons_draw_cursor((SCR), rgb_invert((SCR)->bg))
+
/* Console background from kconf */
#if defined(__CONSOLE_BG)
#define CONSOLE_BG __CONSOLE_BG
@@ -52,29 +60,29 @@
#define CONSOLE_FG 0x00AA00
#endif /* __CONSOLE_FG */
-
struct cons_screen g_root_scr = {0};
static struct cdevsw cons_cdevsw;
-/*
- * Create a chracter descriptor for drawing
- * characters.
- *
- * @c: Character.
- * @fg: Foreground.
- * @bg: Background.
- */
-static inline struct cons_char
-cons_make_char(char c, uint32_t fg, uint32_t bg)
+static void cons_draw_cursor(struct cons_screen *scr, uint32_t color);
+static int cons_handle_special(struct cons_screen *scr, char c);
+
+static uint32_t
+rgb_invert(uint32_t rgb)
{
- struct cons_char ch;
+ uint8_t r, g, b;
+ uint32_t ret;
+
+ r = (rgb >> 16) & 0xFF;
+ g = (rgb >> 8) & 0xFF;
+ b = rgb & 0xFF;
- ch.fg = fg;
- ch.bg = bg;
- ch.c = c;
- return ch;
+ ret = (255 - r) << 16;
+ ret |= (255 - g) << 8;
+ ret |= 255 - b;
+ return ret;
}
+
/*
* Render a character onto the screen.
*
@@ -84,10 +92,10 @@ cons_make_char(char c, uint32_t fg, uint32_t bg)
* @y: Y position of char.
*/
static void
-cons_draw_char(struct cons_screen *scr, struct cons_char ch,
- uint32_t x, uint32_t y)
+cons_draw_char(struct cons_screen *scr, struct cons_char ch)
{
size_t idx;
+ uint32_t x, y;
const uint8_t *glyph;
if (scr->fb_mem == NULL) {
@@ -95,12 +103,131 @@ cons_draw_char(struct cons_screen *scr, struct cons_char ch,
}
glyph = &CONS_FONT[(int)ch.c*16];
+ x = ch.x;
+ y = ch.y;
+
for (uint32_t cy = 0; cy < FONT_HEIGHT; ++cy) {
+ idx = fbdev_get_index(&scr->fbdev, x + (FONT_WIDTH - 1), y + cy);
for (uint32_t cx = 0; cx < FONT_WIDTH; ++cx) {
- idx = fbdev_get_index(&scr->fbdev, x + (FONT_WIDTH - 1) - cx, y + cy);
- scr->fb_mem[idx] = ISSET(glyph[cy], BIT(cx)) ? ch.fg : ch.bg;
+ scr->fb_mem[idx--] = ISSET(glyph[cy], BIT(cx)) ? ch.fg : ch.bg;
+ }
+ }
+}
+
+/*
+ * Internal helper - flush console row
+ *
+ * @row: Row to flush.
+ */
+static int
+cons_flush_row(struct cons_screen *scr, uint32_t row)
+{
+ struct cons_buf *bp;
+ struct cons_char cc;
+
+ bp = scr->ob[row];
+ if (ISSET(bp->flags, CONS_BUF_CLEAN)) {
+ return -EIO;
+ }
+
+ for (int j = 0; j < bp->len; ++j) {
+ if (cons_obuf_pop(bp, &cc) != 0) {
+ continue;
}
+
+ cons_draw_char(scr, cc);
+ bp->flags |= CONS_BUF_CLEAN;
+ }
+
+ return 0;
+}
+
+/*
+ * Internal helper - flush console
+ *
+ */
+static int
+cons_flush(struct cons_screen *scr)
+{
+ for (int i = 0; i < scr->nrows; ++i) {
+ cons_flush_row(scr, i);
}
+ return 0;
+}
+
+/*
+ * Handle a special character (e.g "\t", "\n", etc)
+ *
+ * @scr: Screen to handle this on.
+ * @c: Char to handle.
+ *
+ * Returns 0 if handled, otherwise -1.
+ */
+static int
+cons_handle_special(struct cons_screen *scr, char c)
+{
+ struct cons_buf *bp;
+
+ if (scr->ch_col >= scr->ncols - 20) {
+ scr->ch_col = 0;
+ cons_handle_special(scr, '\n');
+ }
+
+ switch (c) {
+ case ASCII_HT:
+ HIDE_CURSOR(scr);
+ scr->curs_col += 4;
+ scr->ch_col += 4;
+ if (scr->ch_col >= scr->ncols - 1) {
+ cons_handle_special(scr, '\n');
+ }
+ SHOW_CURSOR(scr);
+ return 0;
+ case ASCII_NUL:
+ return 0;
+ case ASCII_BS:
+ bp = scr->ob[scr->ch_row];
+ if (bp->head > bp->tail) {
+ --bp->head;
+ }
+
+ HIDE_CURSOR(scr);
+ if (scr->ch_col > 0 && scr->curs_col > 0) {
+ --scr->ch_col;
+ --scr->curs_col;
+ }
+ SHOW_CURSOR(scr);
+ return 0;
+ case ASCII_LF:
+ /* Are we past screen width? */
+ if (scr->ch_row >= scr->nrows - 1) {
+ cons_clear_scr(scr, scr->bg);
+ return 0;
+ }
+
+ HIDE_CURSOR(scr);
+
+ /* Make a newline */
+ cons_flush(scr);
+ ++scr->ch_row;
+ scr->ch_col = 0;
+ cons_flush(scr);
+
+ /* Update cursor */
+ scr->curs_row += 1;
+ scr->curs_col = 0;
+ SHOW_CURSOR(scr);
+ return 0;
+ case ASCII_FF:
+ /*
+ * Fuck what they say, this is now the
+ * "flush" byte ::)
+ */
+ cons_flush(scr);
+ return 0;
+ }
+
+ return -1;
}
static void
@@ -108,10 +235,16 @@ cons_draw_cursor(struct cons_screen *scr, uint32_t color)
{
size_t idx;
+ /* Past screen width? */
+ if (scr->curs_col >= scr->ncols) {
+ scr->curs_col = 0;
+ scr->curs_row++;
+ }
+
for (uint32_t cy = 0; cy < FONT_HEIGHT; ++cy) {
+ idx = fbdev_get_index(&scr->fbdev, scr->curs_col * FONT_WIDTH, (scr->curs_row * FONT_HEIGHT) + cy);
for (uint32_t cx = 0; cx < FONT_WIDTH; ++cx) {
- idx = fbdev_get_index(&scr->fbdev, (scr->curs_col * FONT_WIDTH) + cx, (scr->curs_row * FONT_HEIGHT) + cy);
- scr->fb_mem[idx] = color;
+ scr->fb_mem[idx++] = color;
}
}
}
@@ -122,41 +255,78 @@ cons_draw_cursor(struct cons_screen *scr, uint32_t color)
* @scr: Screen to clear.
* @bg: Color to clear it to.
*/
-static void
+void
cons_clear_scr(struct cons_screen *scr, uint32_t bg)
{
struct fbdev fbdev = scr->fbdev;
+ cons_flush(scr);
+ HIDE_CURSOR(scr);
+
+ scr->ch_col = 0;
+ scr->ch_row = 0;
+ scr->curs_col = 0;
+ scr->curs_row = 0;
+
for (size_t i = 0; i < fbdev.height * fbdev.pitch; ++i) {
scr->fb_mem[i] = bg;
}
+
+ SHOW_CURSOR(scr);
+
}
/*
- * Handle a special character (e.g "\t", "\n", etc)
- *
- * @scr: Screen to handle this on.
- * @c: Char to handle.
+ * Quickly put a character on the screen.
+ * XXX: Does not acquire the screen's lock or show/hide the cursor.
*
- * Returns 0 if handled, otherwise -1.
+ * @scr: Screen.
+ * @c: Character to draw.
*/
-static int
-cons_handle_special(struct cons_screen *scr, char c)
+static void
+cons_fast_putch(struct cons_screen *scr, char c)
{
- switch (c) {
- case ASCII_LF:
- /* Make a newline */
+ struct cons_char cc;
+ struct cons_buf *bp;
+ int ansi;
+
+ ansi = ansi_feed(&scr->ansi_s, c);
+ if (ansi > 0) {
+ c = ASCII_NUL;
+ } else if (ansi < 0) {
+ c = ASCII_NUL;
+ }
+
+ /* Handle specials */
+ if (cons_handle_special(scr, c) == 0) {
+ return;
+ }
+
+ /* Create a new character */
+ cc.c = c;
+ cc.fg = scr->fg;
+ cc.bg = scr->bg;
+ cc.x = scr->ch_col * FONT_WIDTH;
+ cc.y = scr->ch_row * FONT_HEIGHT;
+
+ /* Push our new character */
+ bp = scr->ob[scr->ch_row];
+ bp->flags &= ~CONS_BUF_CLEAN;
+ cons_obuf_push(bp, cc);
+ ++scr->ch_col;
+
+ /* Check screen bounds */
+ if (cc.x >= (scr->ncols * FONT_WIDTH) - 1) {
scr->ch_col = 0;
++scr->ch_row;
+ }
- cons_draw_cursor(scr, scr->bg);
+ ++scr->curs_col;
+ if (scr->curs_col > scr->ncols - 1) {
scr->curs_col = 0;
- scr->curs_row++;
- cons_draw_cursor(scr, scr->last_chr.fg);
- return 0;
+ if (scr->curs_row < scr->nrows)
+ ++scr->curs_row;
}
-
- return -1;
}
/*
@@ -165,19 +335,111 @@ cons_handle_special(struct cons_screen *scr, char c)
static int
dev_write(dev_t dev, struct sio_txn *sio, int flags)
{
- char *p;
+ cons_putstr(&g_root_scr, sio->buf, sio->len);
+ cons_flush(&g_root_scr);
+ return sio->len;
+}
- p = sio->buf;
- spinlock_acquire(&g_root_scr.lock);
+/*
+ * Character device function.
+ */
+static int
+dev_read(dev_t dev, struct sio_txn *sio, int flags)
+{
+ struct cons_input input;
+ uint8_t *p;
+ int retval;
+ size_t n;
+
+ p = (uint8_t *)sio->buf;
+ n = sio->len;
+
+ /* Must be a power of two */
+ if ((n & 1) != 0) {
+ return -EFAULT;
+ }
- for (size_t i = 0; i < sio->len; ++i) {
- cons_putch(&g_root_scr, p[i]);
+ retval = cons_ibuf_pop(&g_root_scr, &input);
+ if (retval < 0) {
+ return -EAGAIN;
}
+ spinlock_acquire(&g_root_scr.lock);
+ for (;;) {
+ /* Buffer too small */
+ if (n == 0) {
+ break;
+ }
+
+ *p++ = input.chr;
+ *p++ = input.scancode;
+ n -= 2;
+
+ /* Try to get the next byte */
+ retval = cons_ibuf_pop(&g_root_scr, &input);
+ if (retval < 0) {
+ break;
+ }
+ }
spinlock_release(&g_root_scr.lock);
return sio->len;
}
+static int
+cons_init_bufs(struct cons_screen *scr)
+{
+ struct cons_buf *bp;
+ size_t ob_len;
+
+ scr->ib = cons_new_buf(CONS_BUF_INPUT, scr->ncols);
+ if (g_root_scr.ib == NULL) {
+ panic("out of memory\n");
+ }
+
+ ob_len = sizeof(*scr->ob) * scr->nrows;
+ scr->ob = dynalloc(ob_len);
+
+ /* Allocate all output buffers per line */
+ for (size_t i = 0; i < scr->nrows; ++i) {
+ bp = cons_new_buf(CONS_BUF_OUTPUT, scr->ncols);
+ if (bp == NULL) {
+ panic("out of memory\n");
+ }
+ bp->flags |= CONS_BUF_CLEAN;
+ scr->ob[i] = bp;
+ }
+
+ return 0;
+}
+
+/*
+ * Reset console color.
+ */
+void
+cons_reset_color(struct cons_screen *scr)
+{
+ g_root_scr.fg = CONSOLE_FG;
+ g_root_scr.bg = CONSOLE_BG;
+}
+
+void
+cons_update_color(struct cons_screen *scr, uint32_t fg, uint32_t bg)
+{
+ scr->fg = fg;
+ scr->bg = bg;
+}
+
+void
+cons_reset_cursor(struct cons_screen *scr)
+{
+ HIDE_CURSOR(scr);
+ scr->ch_col = 0;
+ scr->ch_row = 0;
+ scr->curs_col = 0;
+ scr->curs_row = 0;
+ SHOW_CURSOR(scr);
+}
+
/*
* Put a character on the screen.
*
@@ -187,44 +449,38 @@ dev_write(dev_t dev, struct sio_txn *sio, int flags)
int
cons_putch(struct cons_screen *scr, char c)
{
- struct cons_char cons_chr;
-
- if (scr->ch_col > scr->ncols) {
- /* Make a newline as we past the max col */
- scr->ch_col = 0;
- ++scr->ch_row;
- }
+ spinlock_acquire(&scr->lock);
+ HIDE_CURSOR(scr);
- if (scr->curs_row >= scr->nrows) {
- /* Went over the screen size */
- /* TODO: Scroll instead of just clearing the screen */
- scr->ch_col = 0;
- scr->ch_row = 0;
- cons_clear_scr(scr, scr->bg);
+ cons_fast_putch(scr, c);
- scr->curs_col = 0;
- scr->curs_row = 0;
- cons_draw_cursor(scr, scr->last_chr.fg);
- }
+ SHOW_CURSOR(scr);
+ spinlock_release(&scr->lock);
+ return 0;
+}
- /*
- * If this is a special char that we can handle
- * then handle it and return.
- */
- if (cons_handle_special(scr, c) == 0) {
- return 0;
- }
+/*
+ * Put a string on the screen.
+ *
+ * @scr: Screen.
+ * @s: String to draw.
+ * @l: Length of s.
+ */
+int
+cons_putstr(struct cons_screen *scr, const char *s, size_t len)
+{
+ const char *p = s;
- cons_chr = cons_make_char(c, scr->fg, scr->bg);
- scr->last_chr = cons_chr;
+ spinlock_acquire(&scr->lock);
+ HIDE_CURSOR(scr);
- /* Draw cursor and character */
- scr->curs_col++;
- cons_draw_cursor(scr, scr->last_chr.fg);
- cons_draw_char(scr, cons_chr, scr->ch_col * FONT_WIDTH,
- scr->ch_row * FONT_HEIGHT);
+ while (len--) {
+ cons_fast_putch(scr, *p);
+ ++p;
+ }
- ++scr->ch_col;
+ SHOW_CURSOR(scr);
+ spinlock_release(&scr->lock);
return 0;
}
@@ -233,6 +489,8 @@ cons_init(void)
{
struct fbdev fbdev = fbdev_get();
+ g_root_scr.ch_col = 0;
+ g_root_scr.ch_row = 0;
g_root_scr.fg = CONSOLE_FG;
g_root_scr.bg = CONSOLE_BG;
g_root_scr.fb_mem = fbdev.mem;
@@ -240,6 +498,8 @@ cons_init(void)
g_root_scr.ncols = fbdev.width / FONT_WIDTH;
g_root_scr.fbdev = fbdev;
memset(&g_root_scr.lock, 0, sizeof(g_root_scr.lock));
+ cons_init_bufs(&g_root_scr);
+ SHOW_CURSOR(&g_root_scr);
}
/*
@@ -267,6 +527,6 @@ cons_expose(void)
}
static struct cdevsw cons_cdevsw = {
- .read = noread,
+ .read = dev_read,
.write = dev_write
};
diff --git a/sys/dev/cons/cons_ansi.c b/sys/dev/cons/cons_ansi.c
new file mode 100644
index 0000000..ab1f22a
--- /dev/null
+++ b/sys/dev/cons/cons_ansi.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * 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,
+ * 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.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
+ */
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <dev/cons/cons.h>
+#include <dev/cons/ansi.h>
+#include <string.h>
+
+__always_inline static inline bool
+is_valid_color(int c)
+{
+ return c >= '0' && c <= '7';
+}
+
+static inline void
+ansi_reset(struct ansi_state *statep)
+{
+ memset(statep, 0, sizeof(*statep));
+}
+
+/*
+ * Feed a byte into the ANSI escape sequence
+ * state machine.
+ *
+ * @statep: State machine pointer.
+ * @c: Byte to feed.
+ *
+ * On success, `c' is returned. On failure,
+ * 0 is returned. Values less than 0 indicate
+ * success with console attributes updated
+ * (ANSI_UPDATE_*).
+ */
+int
+ansi_feed(struct ansi_state *statep, char c)
+{
+ /* Standard colors */
+ static uint32_t colortab[] = {
+ ANSI_BLACK, ANSI_RED,
+ ANSI_GREEN, ANSI_YELLOW,
+ ANSI_BLUE, ANSI_MAGENTA,
+ ANSI_CYAN, ANSI_WHITE
+ };
+
+ /*
+ * Handle the control sequence introducer
+ * bytes.
+ */
+ switch (statep->csi) {
+ case 0: /* '\033' */
+ if (c != '\033') {
+ return 0;
+ }
+ statep->csi = 1;
+ statep->prev = c;
+ return c;
+ case 1: /* '[' */
+ if (c != '[') {
+ ansi_reset(statep);
+ return 0;
+ }
+ statep->csi = 2;
+ statep->prev = c;
+ return c;
+ case 2:
+ if (c == 'H') {
+ cons_clear_scr(&g_root_scr, g_root_scr.bg);
+ return ANSI_UPDATE_CURSOR;
+ }
+ break;
+ }
+
+ if (!statep->set_fg && !statep->set_bg) {
+ /* Reset attributes? */
+ if (statep->reset_color) {
+ ansi_reset(statep);
+ cons_reset_color(&g_root_scr);
+ return ANSI_UPDATE_COLOR;
+ }
+
+ /* Mark attributes to be reset? */
+ if (c == '0') {
+ statep->reset_color = 1;
+ statep->prev = c;
+ return c;
+ }
+
+ /* Expect foreground */
+ if (c != '3') {
+ ansi_reset(statep);
+ return 0;
+ }
+ statep->set_fg = 1;
+ statep->prev = c;
+ return c;
+ }
+
+ if (statep->set_fg && c != ';') {
+ /* Make sure this is valid */
+ if (!is_valid_color(c)) {
+ ansi_reset(statep);
+ return 0;
+ }
+
+ /* Set the foreground */
+ statep->fg = colortab[c - '0'];
+ statep->set_bg = 1;
+ statep->set_fg = 0;
+ statep->prev = c;
+ return c;
+ }
+
+ if (statep->set_bg) {
+ if (c == ';') {
+ statep->prev = c;
+ return c;
+ }
+
+ /* Expect '4' after ';' */
+ if (statep->prev == ';' && c != '4') {
+ ansi_reset(statep);
+ return 0;
+ }
+
+ if (c == 'm') {
+ cons_update_color(&g_root_scr, statep->fg, statep->bg);
+ ansi_reset(statep);
+ return ANSI_UPDATE_COLOR;
+ }
+
+ /* Make sure this is valid */
+ if (!is_valid_color(c)) {
+ ansi_reset(statep);
+ return 0;
+ }
+
+ /* Set the background */
+ statep->bg = colortab[c - '0'];
+ statep->prev = c;
+ return c;
+ }
+
+ ansi_reset(statep);
+ return 0;
+}
diff --git a/sys/dev/cons/cons_buf.c b/sys/dev/cons/cons_buf.c
new file mode 100644
index 0000000..84a38ce
--- /dev/null
+++ b/sys/dev/cons/cons_buf.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team.
+ * 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,
+ * 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.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <dev/cons/consvar.h>
+#include <dev/cons/cons.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+#include <assert.h>
+
+/*
+ * Create a new console buffer.
+ *
+ * @type: Buffer type (CONS_BUF_*)
+ * @len: Max length of buffer.
+ */
+struct cons_buf *
+cons_new_buf(uint8_t type, size_t len)
+{
+ struct cons_buf *bp;
+ size_t alloc_len;
+
+ if ((bp = dynalloc(sizeof(*bp))) == NULL) {
+ return NULL;
+ }
+
+ memset(bp, 0, sizeof(*bp));
+ bp->type = type;
+ bp->len = len;
+
+ /* Create the actual buffers now */
+ switch (type) {
+ case CONS_BUF_INPUT:
+ alloc_len = sizeof(*bp->ibuf) * len;
+ bp->ibuf = dynalloc(alloc_len);
+ break;
+ case CONS_BUF_OUTPUT:
+ alloc_len = sizeof(*bp->obuf) * len;
+ bp->obuf = dynalloc(alloc_len);
+ break;
+ }
+
+ return bp;
+}
+
+/*
+ * Push a character to a console output
+ * buffer.
+ *
+ * @bp: Pointer to console buffer.
+ * @c: Character to push.
+ */
+int
+cons_obuf_push(struct cons_buf *bp, struct cons_char c)
+{
+ uint8_t next;
+ int retval = 0;
+
+ if (bp == NULL) {
+ return -EINVAL;
+ }
+
+ spinlock_acquire(&bp->lock);
+ __assert(bp->type == CONS_BUF_OUTPUT);
+ next = bp->head + 1;
+ if (next > bp->len) {
+ retval = -ENOSPC;
+ goto done;
+ }
+
+ bp->obuf[bp->head] = c;
+ bp->head = next;
+
+done:
+ spinlock_release(&bp->lock);
+ return retval;
+}
+
+/*
+ * Pop a character from the console
+ * buffer.
+ *
+ * @bp: Pointer to console buffer.
+ * @res: Result is written here.
+ */
+int
+cons_obuf_pop(struct cons_buf *bp, struct cons_char *res)
+{
+ uint8_t next;
+ int retval = 0;
+
+ if (bp == NULL || res == NULL) {
+ return -EINVAL;
+ }
+
+ __assert(bp->type == CONS_BUF_OUTPUT);
+ spinlock_acquire(&bp->lock);
+
+ /* Do we have any data left? */
+ if (bp->head == bp->tail) {
+ bp->head = 0;
+ bp->tail = 0;
+ retval = -EAGAIN;
+ goto done;
+ }
+
+ next = bp->tail + 1;
+ if (next > bp->len) {
+ next = 0;
+ }
+
+ *res = bp->obuf[bp->tail];
+ bp->tail = next;
+
+done:
+ spinlock_release(&bp->lock);
+ return retval;
+}
+
+int
+cons_ibuf_push(struct cons_screen *scr, struct cons_input in)
+{
+ struct cons_buf *bp;
+ uint8_t head_next;
+ int retval = 0;
+
+ if (scr == NULL) {
+ return -EINVAL;
+ }
+
+ bp = scr->ib;
+ spinlock_acquire(&bp->lock);
+ __assert(bp->type == CONS_BUF_INPUT);
+
+ head_next = bp->head + 1;
+ if (head_next > bp->len) {
+ retval = -ENOSPC;
+ goto done;
+ }
+
+ bp->ibuf[bp->head] = in;
+ bp->head = head_next;
+
+done:
+ spinlock_release(&bp->lock);
+ return retval;
+}
+
+int
+cons_ibuf_pop(struct cons_screen *scr, struct cons_input *res)
+{
+ uint8_t tail_next;
+ struct cons_buf *bp;
+ int retval = 0;
+
+ if (scr == NULL || res == NULL) {
+ return -EINVAL;
+ }
+
+ bp = scr->ib;
+ __assert(bp->type == CONS_BUF_INPUT);
+ spinlock_acquire(&bp->lock);
+
+ /* Do we have any data left? */
+ if (bp->head == bp->tail) {
+ bp->head = 0;
+ bp->tail = 0;
+ retval = -EAGAIN;
+ goto done;
+ }
+
+ tail_next = bp->tail + 1;
+ if (tail_next > bp->len) {
+ tail_next = 0;
+ }
+
+ *res = bp->ibuf[bp->tail];
+ bp->tail = tail_next;
+
+done:
+ spinlock_release(&bp->lock);
+ return retval;
+}