Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)diff --git a/drivers/tty/tty.c b/drivers/tty/tty.c new file mode 100644 index 0000000..c65aeaf --- /dev/null +++ b/drivers/tty/tty.c @@ -0,0 +1,424 @@ +/* + * This file contains the implementation of the TTY Device. Generates a device + * /dev/tty, which displays to the default text video output. This file handles + * all the formatting required for a TTY. + */ + +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <sys/fb.h> +#include <termios.h> +#include "../drivers.h" +#include "../../task/task.h" +#include "../../proc/proc.h" +#include "../../mem/heap.h" +#include "../../io.h" + +size_t tty_read(File *file, char *buf, size_t size, off_t offset); +size_t tty_write(File *file, char *buf, size_t size, off_t offset); +int tty_ioctl(File *file, unsigned long request, uintptr_t argp); +int tty_open(File *file); + +FileOps ttyFileOps = { + .read = tty_read, + .write = tty_write, + .ioctl = tty_ioctl, + .open = tty_open, +}; + +Driver ttyDriver = { + .major = 5, + .ops = &ttyFileOps, + .next = NULL, +}; + +extern uint8_t font[]; + +Termios tty; +uintptr_t vgaLfb; +int vgaWidth, vgaHeight, vgaBpp; +int ttyMode; +int ttyX, ttyY; +int ttyW = 80, ttyH = 25; +char ttyAttr = 0x07; +char *lineBuf, *line; +int lineBufLen = 0, lineLen = 0; +unsigned char shift = 0, ctrl = 0; + +uint32_t colours[] = { + 0x000000, + 0x0000FF, + 0x00FF00, + 0x00FFFF, + 0xFF0000, + 0xFF00FF, + 0x884422, + 0xCCCCCC, + 0x666666, + 0x6666FF, + 0x66FF66, + 0x66FFFF, + 0xFF6666, + 0xFF66FF, + 0xFFFF66, + 0xFFFFFF, +}; + +TaskQueue ttyWait; + +/* Map of keys on the keyboard */ +char kbmap[128] = { + 0, 27, + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', + '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', + 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', + 0, '#', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, + '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, '\\', 0, 0, 0 +}; +char skbmap[128] = { + 0, 27, + '!', '"', '$', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', + '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', + 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '@', '|', + 0, '~', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, + '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, '|', 0, 0, 0 +}; + +/* Convert a string to a number */ +static int +number(char *str) +{ + int num = 0, col = 1, len; + for (len = 0; str[len] >= '0' && str[len] <= '9'; len++); + while (len--) { + num += (str[len] - '0') * col; + col *= 10; + } + return num; +} + +/* Put a pixel on the screen */ +static void +putpixel(int x, int y, uint32_t colour) +{ + /* This splits the framebuffer into bytes to be BPP agnostic */ + uint8_t *screen = (uint8_t *) vgaLfb; + unsigned where = x*(vgaBpp/8) + y*vgaWidth*(vgaBpp/8); + screen[where + 0] = (colour >> 0) & 0xFF; /* BLUE */ + screen[where + 1] = (colour >> 8) & 0xFF; /* GREEN */ + screen[where + 2] = (colour >> 16) & 0xFF; /* RED */ +} + +/* Draw a character to the framebuffer using the font */ +static void +draw_char(unsigned char c, int x, int y, uint32_t fgcolour, uint32_t bgcolour) +{ + int cx, cy; + unsigned char *glyph = font + ((int) c * 16); + for (cy = 0; cy < 16; cy++) { + for (cx = 0; cx < 8; cx++) { + putpixel(x + 7 - cx, y + cy, + (glyph[cy] & (1 << cx)) ? fgcolour : bgcolour); + } + } +} + +/* Set the ANSI attribute */ +static void +set_ansi_attribute(int mode, int foreground, int background) +{ + char bold; + + switch (mode) { + case 0: /* RESET */ + ttyAttr = 0x07; + return; + case 1: + bold = 1; + break; + } + + switch (foreground) { + case 0: /* DEFAULT */ + ttyAttr = (ttyAttr & 0xF0) + 7 + (bold ? 8 : 0); + break; + case 30: /* BLACK */ + ttyAttr = (ttyAttr & 0xF0) + 0; + break; + case 31: /* RED */ + ttyAttr = (ttyAttr & 0xF0) + 4 + (bold ? 8 : 0); + break; + case 32: /* GREEN */ + ttyAttr = (ttyAttr & 0xF0) + 2 + (bold ? 8 : 0); + break; + case 33: /* YELLOW */ + ttyAttr = (ttyAttr & 0xF0) + 14; + break; + case 34: /* BLUE */ + ttyAttr = (ttyAttr & 0xF0) + 1 + (bold ? 8 : 0); + break; + case 35: /* MAGENTA */ + ttyAttr = (ttyAttr & 0xF0) + 5 + (bold ? 8 : 0); + break; + case 36: /* CYAN */ + ttyAttr = (ttyAttr & 0xF0) + 3 + (bold ? 8 : 0); + break; + } + +} + +/* Parse ANSI escape codes */ +static size_t +ansi_escape(char *buf) +{ + int len; + char *end; + for (len = 0; buf[len] < 'A' || buf[len] > 'z'; len++); + end = buf + len + 1; + switch (buf[len]) { + case 'H': + /* Set the cursor position */ + ttyX = 0; + ttyY = 0; + break; + case 'J': + /* 2J clears screen */ + int jmode = 0; + jmode = number(buf); + if (jmode == 2) { + if (ttyMode == 0) + memset((void *) 0xB8000, '\0', ttyW * ttyH * 2); + else + memset((void *) vgaLfb, '\0', + vgaWidth * vgaHeight * (vgaBpp / 8)); + } + break; + case 'm': + /* Set attribute */ + int mode = 0, foreground = 0, background = 0; + mode = number(buf); + while (*buf >= '0' && *buf <= '9') buf++; + buf++; + if (buf == end) + goto set; + foreground = number(buf); + while (*buf >= '0' && *buf <= '9') buf++; + buf++; + if (buf == end) + goto set; + background = number(buf); +set: + set_ansi_attribute(mode, foreground, background); + break; + } + + return len + 1; +} + +/* Print a character to the screen */ +void +print_char(char c) +{ + char *screen = (char *) 0xB8000; + + if (ttyMode == 0) + screen[(((ttyY * ttyW) + ttyX) * 2) + 1] = ttyAttr; + else + draw_char(' ', ttyX*8, ttyY*16, 0xFFFFFF, 0x000000); + + /* Characters */ + if (!(tty.lflag & ECHO) && c != '\n') + goto curctl; + switch (c) { + case '\r': + ttyX = 0; + break; + case '\n': + ttyX = 0; + ttyY++; + break; + case '\b': + ttyX--; + if (ttyMode == 0) + screen[((ttyY * ttyW) + ttyX) * 2] = ' '; + else + draw_char(' ', ttyX*8, ttyY*16, colours[ttyAttr & 0xF], + colours[ttyAttr >> 4]); + break; + case '\t': + ttyX += 8 - (ttyX % 8); + break; + default: + if (ttyMode == 0) + screen[((ttyY * ttyW) + ttyX) * 2] = c; + else + draw_char(c, ttyX*8, ttyY*16, colours[ttyAttr & 0xF], + colours[ttyAttr >> 4]); + ttyX++; + } + + /* Control cursor */ +curctl: + if (ttyX >= ttyW) { + ttyX = 0; + ttyY++; + } + if (ttyX < 0) { + ttyX = ttyW - 1; + ttyY--; + } + if (ttyY >= ttyH) { + if (ttyMode == 0) { + memcpy(screen, screen + (ttyW*2), ttyW * (ttyH-1) * 2); + memset(screen + (ttyW * 2 * (ttyH-1)), 0, ttyW * 2); + } else { + memcpy((void *) vgaLfb, (void *) vgaLfb + (vgaWidth*(vgaBpp/8)*16), + vgaWidth*(vgaBpp/8)*(vgaHeight-16)); + memset((void *) vgaLfb + (vgaWidth*(vgaBpp/8)*(vgaHeight-16)), + '\0', vgaWidth*(vgaBpp/8)*16); + } + ttyY--; + } + if (ttyY < 0) + ttyY++; + + if (ttyMode == 0) + screen[(((ttyY * ttyW) + ttyX) * 2) + 1] = + ((ttyAttr & 0x0F) << 4) + ((ttyAttr >> 4) & 0x0F); + else + draw_char(' ', ttyX*8, ttyY*16, 0x000000, 0x888888); +} + +/* Handle the keyboard interrupt */ +void +keyboard_handler(InterruptFrame *frame) +{ + char c; + unsigned char key; + int i; + for (i = 0; i < 1000; i++) { + if ((inb(0x64) & 1) == 0) + continue; + key = inb(0x60); + break; + } + if (key == 0x1D) + ctrl = 128; + if (key == 0x9D) + ctrl = 0; + if (key == 0x2A || key == 0x36) + shift = 128; + if (key == 0xAA || key == 0xB6) + shift = 0; + + if (key >= 128) + return; + if (shift) + c = skbmap[key]; + else + c = kbmap[key]; + if (!c) + return; + + if (c == '\b' && lineBufLen == 0) + return; + if (c == '\b') + lineBuf[lineBufLen--] = '\0'; + else + lineBuf[lineBufLen++] = c; + print_char(c); + if (c == '\n') { + memcpy(line, lineBuf, lineBufLen); + memset(lineBuf, 0, lineBufLen); + lineLen = lineBufLen; + lineBufLen = 0; + for (Task *tmp = ttyWait.start; tmp; tmp = tmp->next) + unblock_task(pop_from_queue(&ttyWait)); + } +} + +/* Initialise the TTY */ +void +init_tty(void) +{ + register_driver(&ttyDriver); + mknod("/dev/tty", S_IFCHR | 0666, MKDEV(ttyDriver.major, 0)); + + tty.lflag = ICANON | ECHO; + + int fd = open("/dev/fb", O_RDWR); + if (fd < 0) { + ttyMode = 0; + int x, y; + uint16_t *screen = (uint16_t *) 0xB8000; + for (y = 0; y < 25; y++) + for (x = 0; x < 80; x++) + screen[(y * 80) + x] = 0x0000; + } else { + FBVarInfo vinfo; + FBFixInfo finfo; + ioctl(fd, FBIOGET_VSCREENINFO, &vinfo); + ioctl(fd, FBIOGET_FSCREENINFO, &finfo); + close(fd); + vgaWidth = vinfo.xres; + vgaHeight = vinfo.yres; + vgaBpp = vinfo.bpp; + vgaLfb = finfo.fbmem; + + ttyMode = 1; + ttyW = vgaWidth/8; + ttyH = vgaHeight/16; + } + + lineBuf = kmalloc(4096); + line = kmalloc(4096); + register_interrupt(1, keyboard_handler); +} + +/* Read from the TTY */ +size_t +tty_read(File *file, char *buf, size_t size, off_t offset) +{ + if (!lineLen) { + add_to_queue(&ttyWait, current); + block_task(WAITING_FOR_READ); + } + size_t count = (size < lineLen) ? size : lineLen; + memcpy(buf, line, count); + if (size < lineLen) { + memcpy(line, line + size, lineLen - size); + lineLen -= size; + } else { + memset(line, 0, lineLen); + lineLen = 0; + } + return count; +} + +/* Write to the TTY */ +size_t +tty_write(File *file, char *buf, size_t size, off_t offset) +{ + size_t skip, count = 0; + while (size--) { + if (buf[0] == '\033' && buf[1] == '[') { + skip = ansi_escape(buf + 2); + buf += skip + 2; + size -= skip + 1; + continue; + } + print_char(*buf++); + count++; + } + return count; +} + +/* Open the TTY */ +int +tty_open(File *file) +{ + return 0; +}