summaryrefslogtreecommitdiff
path: root/sys/dev/cons
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/cons')
-rw-r--r--sys/dev/cons/cons.c318
-rw-r--r--sys/dev/cons/cons_buf.c186
2 files changed, 430 insertions, 74 deletions
diff --git a/sys/dev/cons/cons.c b/sys/dev/cons/cons.c
index 4b85240..365aa0b 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,27 +40,32 @@
#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), (SCR)->fg)
+
+/* Console background from kconf */
+#if defined(__CONSOLE_BG)
+#define CONSOLE_BG __CONSOLE_BG
+#else
+#define CONSOLE_BG 0x000000
+#endif /* __CONSOLE_BG */
+
+/* Console foreground from kconf */
+#if defined(__CONSOLE_FG)
+#define CONSOLE_FG __CONSOLE_FG
+#else
+#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)
-{
- struct cons_char ch;
-
- ch.fg = fg;
- ch.bg = bg;
- ch.c = c;
- return ch;
-}
+static void cons_draw_cursor(struct cons_screen *scr, uint32_t color);
+static int cons_handle_special(struct cons_screen *scr, char c);
+static void cons_clear_scr(struct cons_screen *scr, uint32_t bg);
/*
* Render a character onto the screen.
@@ -69,10 +76,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) {
@@ -80,6 +87,9 @@ 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) {
for (uint32_t cx = 0; cx < FONT_WIDTH; ++cx) {
idx = fbdev_get_index(&scr->fbdev, x + (FONT_WIDTH - 1) - cx, y + cy);
@@ -88,33 +98,45 @@ cons_draw_char(struct cons_screen *scr, struct cons_char ch,
}
}
-static void
-cons_draw_cursor(struct cons_screen *scr, uint32_t color)
+/*
+ * Internal helper - flush console row
+ *
+ * @row: Row to flush.
+ */
+static int
+cons_flush_row(struct cons_screen *scr, uint32_t row)
{
- size_t idx;
+ struct cons_buf *bp;
+ struct cons_char cc;
- for (uint32_t cy = 0; cy < 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;
+ 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;
}
/*
- * Clear the screen.
+ * Internal helper - flush console
*
- * @scr: Screen to clear.
- * @bg: Color to clear it to.
*/
-static void
-cons_clear_scr(struct cons_screen *scr, uint32_t bg)
+static int
+cons_flush(struct cons_screen *scr)
{
- struct fbdev fbdev = scr->fbdev;
-
- for (size_t i = 0; i < fbdev.height * fbdev.pitch; ++i) {
- scr->fb_mem[i] = bg;
+ for (int i = 0; i < scr->nrows; ++i) {
+ cons_flush_row(scr, i);
}
+ return 0;
}
/*
@@ -128,22 +150,91 @@ cons_clear_scr(struct cons_screen *scr, uint32_t bg)
static int
cons_handle_special(struct cons_screen *scr, char c)
{
+ if (scr->ch_col >= scr->ncols - 20) {
+ scr->ch_col = 0;
+ cons_handle_special(scr, '\n');
+ }
+
switch (c) {
case ASCII_LF:
+ HIDE_CURSOR(scr);
+
+ /* Are we past screen width? */
+ if (scr->ch_row >= scr->nrows - 1) {
+ cons_clear_scr(scr, scr->bg);
+ cons_flush(scr);
+ scr->ch_col = 0;
+ scr->ch_row = 0;
+
+ /* Update cursor */
+ scr->curs_row = 0;
+ scr->curs_col = 0;
+ SHOW_CURSOR(scr);
+ return 0;
+ }
+
/* Make a newline */
- scr->ch_col = 0;
+ cons_flush(scr);
++scr->ch_row;
+ scr->ch_col = 0;
+ cons_flush(scr);
- cons_draw_cursor(scr, scr->bg);
+ /* Update cursor */
+ scr->curs_row += 1;
scr->curs_col = 0;
- scr->curs_row++;
- cons_draw_cursor(scr, scr->last_chr.fg);
+ 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
+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) {
+ 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;
+ }
+ }
+}
+
+/*
+ * Clear the screen.
+ *
+ * @scr: Screen to clear.
+ * @bg: Color to clear it to.
+ */
+static void
+cons_clear_scr(struct cons_screen *scr, uint32_t bg)
+{
+ struct fbdev fbdev = scr->fbdev;
+ struct cons_buf *bp;
+
+ for (size_t i = 0; i < fbdev.height * fbdev.pitch; ++i) {
+ scr->fb_mem[i] = bg;
+ }
+
+ bp = scr->ob[scr->nrows - 1];
+ bp->flags |= CONS_BUF_CLEAN;
+}
+
/*
* Character device function.
*/
@@ -153,16 +244,87 @@ dev_write(dev_t dev, struct sio_txn *sio, int flags)
char *p;
p = sio->buf;
- spinlock_acquire(&g_root_scr.lock);
for (size_t i = 0; i < sio->len; ++i) {
cons_putch(&g_root_scr, p[i]);
}
+ cons_flush(&g_root_scr);
+ return sio->len;
+}
+
+/*
+ * 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;
+ }
+
+ 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;
+}
+
/*
* Put a character on the screen.
*
@@ -172,44 +334,48 @@ 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;
+ struct cons_buf *bp;
+ struct cons_char cc;
+ size_t max_width;
- if (scr->ch_col > scr->ncols) {
- /* Make a newline as we past the max col */
- scr->ch_col = 0;
- ++scr->ch_row;
- }
-
- 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);
-
- scr->curs_col = 0;
- scr->curs_row = 0;
- cons_draw_cursor(scr, scr->last_chr.fg);
- }
+ spinlock_acquire(&scr->lock);
- /*
- * If this is a special char that we can handle
- * then handle it and return.
- */
+ /* Handle specials */
if (cons_handle_special(scr, c) == 0) {
- return 0;
+ goto done;
}
- cons_chr = cons_make_char(c, scr->fg, scr->bg);
- scr->last_chr = cons_chr;
+ 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);
+ /* 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 */
+ max_width = scr->ncols * FONT_WIDTH;
+ if (cc.x >= max_width - 1) {
+ scr->ch_col = 0;
+ ++scr->ch_row;
+ }
+
+ ++scr->curs_col;
+ if (scr->curs_col > scr->ncols - 1) {
+ scr->curs_col = 0;
+ if (scr->curs_row < scr->nrows)
+ ++scr->curs_row;
+ }
+ SHOW_CURSOR(scr);
+done:
+ spinlock_release(&scr->lock);
return 0;
}
@@ -218,13 +384,17 @@ cons_init(void)
{
struct fbdev fbdev = fbdev_get();
- g_root_scr.fg = 0x00AA00;
- g_root_scr.bg = 0x000000;
+ 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;
g_root_scr.nrows = fbdev.height / FONT_HEIGHT;
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);
}
/*
@@ -252,6 +422,6 @@ cons_expose(void)
}
static struct cdevsw cons_cdevsw = {
- .read = noread,
+ .read = dev_read,
.write = dev_write
};
diff --git a/sys/dev/cons/cons_buf.c b/sys/dev/cons/cons_buf.c
new file mode 100644
index 0000000..3bc45a1
--- /dev/null
+++ b/sys/dev/cons/cons_buf.c
@@ -0,0 +1,186 @@
+/*
+ * 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;
+
+ if (bp == NULL) {
+ return -EINVAL;
+ }
+
+ __assert(bp->type == CONS_BUF_OUTPUT);
+ next = bp->head + 1;
+ if (next > bp->len) {
+ return -ENOSPC;
+ }
+
+ bp->obuf[bp->head] = c;
+ bp->head = next;
+ return 0;
+}
+
+/*
+ * 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;
+
+ if (bp == NULL || res == NULL) {
+ return -EINVAL;
+ }
+
+ __assert(bp->type == CONS_BUF_OUTPUT);
+
+ /* Do we have any data left? */
+ if (bp->head == bp->tail) {
+ bp->head = 0;
+ bp->tail = 0;
+ return -EAGAIN;
+ }
+
+ next = bp->tail + 1;
+ if (next > bp->len) {
+ next = 0;
+ }
+
+ *res = bp->obuf[bp->tail];
+ bp->tail = next;
+ return 0;
+}
+
+int
+cons_ibuf_push(struct cons_screen *scr, struct cons_input in)
+{
+ struct cons_buf *bp;
+ uint8_t head_next;
+
+ if (scr == NULL) {
+ return -EINVAL;
+ }
+
+ bp = scr->ib;
+ __assert(bp->type == CONS_BUF_INPUT);
+
+ head_next = bp->head + 1;
+ if (head_next > bp->len) {
+ return -ENOSPC;
+ }
+
+ bp->ibuf[bp->head] = in;
+ bp->head = head_next;
+ return 0;
+}
+
+int
+cons_ibuf_pop(struct cons_screen *scr, struct cons_input *res)
+{
+ uint8_t tail_next;
+ struct cons_buf *bp;
+
+ if (scr == NULL || res == NULL) {
+ return -EINVAL;
+ }
+
+ bp = scr->ib;
+ __assert(bp->type == CONS_BUF_INPUT);
+
+ /* Do we have any data left? */
+ if (bp->head == bp->tail) {
+ bp->head = 0;
+ bp->tail = 0;
+ return -EAGAIN;
+ }
+
+ tail_next = bp->tail + 1;
+ if (tail_next > bp->len) {
+ tail_next = 0;
+ }
+
+ *res = bp->ibuf[bp->tail];
+ bp->tail = tail_next;
+ return 0;
+}