summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/cons/cons.c301
-rw-r--r--sys/dev/cons/cons_buf.c186
-rw-r--r--sys/include/dev/cons/cons.h5
-rw-r--r--sys/include/dev/cons/consvar.h84
4 files changed, 503 insertions, 73 deletions
diff --git a/sys/dev/cons/cons.c b/sys/dev/cons/cons.c
index 398b6ca..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,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), (SCR)->fg)
+
/* Console background from kconf */
#if defined(__CONSOLE_BG)
#define CONSOLE_BG __CONSOLE_BG
@@ -52,28 +60,12 @@
#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.
@@ -84,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) {
@@ -95,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);
@@ -103,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;
}
/*
@@ -143,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.
*/
@@ -168,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.
*
@@ -187,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;
}
@@ -233,6 +384,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 +393,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 +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;
+}
diff --git a/sys/include/dev/cons/cons.h b/sys/include/dev/cons/cons.h
index 8e2c2c6..3569c52 100644
--- a/sys/include/dev/cons/cons.h
+++ b/sys/include/dev/cons/cons.h
@@ -33,11 +33,14 @@
#include <sys/types.h>
#include <sys/spinlock.h>
#include <dev/video/fbdev.h>
+#include <dev/cons/consvar.h>
struct cons_char {
char c;
uint32_t fg;
uint32_t bg;
+ uint32_t x;
+ uint32_t y;
};
struct cons_screen {
@@ -53,6 +56,8 @@ struct cons_screen {
uint32_t ch_row; /* Current row */
uint32_t curs_col; /* Cursor col */
uint32_t curs_row; /* Cursor row */
+ struct cons_buf *ib; /* Input buffer */
+ struct cons_buf **ob; /* Output buffers */
struct cons_char last_chr;
struct spinlock lock;
};
diff --git a/sys/include/dev/cons/consvar.h b/sys/include/dev/cons/consvar.h
new file mode 100644
index 0000000..483d5f1
--- /dev/null
+++ b/sys/include/dev/cons/consvar.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#ifndef _DEV_CONSVAR_H_
+#define _DEV_CONSVAR_H_
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+/* Buffer types */
+#define CONS_BUF_INPUT 0
+#define CONS_BUF_OUTPUT 1
+
+/* Buffer flags */
+#define CONS_BUF_CLEAN BIT(0) /* Not recently written to */
+
+extern struct cons_screen scr;
+
+/*
+ * The keyboard packet is two bytes
+ * and the bits are as follows:
+ *
+ * - 0:7 ~ ASCII character
+ * - 8:15 ~ Scancode
+ */
+struct cons_input {
+ union {
+ uint8_t chr;
+ uint8_t scancode;
+ };
+ uint16_t data;
+};
+
+/*
+ * A circular buffer for buffering
+ * keyboard input or console output.
+ */
+struct cons_buf {
+ union {
+ struct cons_input *ibuf;
+ struct cons_char *obuf;
+ void *raw;
+ };
+ uint8_t tail;
+ uint8_t head;
+ uint8_t type;
+ uint8_t flags;
+ size_t len;
+};
+
+struct cons_buf *cons_new_buf(uint8_t type, size_t len);
+int cons_obuf_push(struct cons_buf *bp, struct cons_char c);
+int cons_obuf_pop(struct cons_buf *bp, struct cons_char *res);
+
+int cons_ibuf_push(struct cons_screen *scr, struct cons_input input);
+int cons_ibuf_pop(struct cons_screen *scr, struct cons_input *res);
+
+#endif /* !_DEV_CONSVAR_H_ */