Orion
Barry Keyboard/Mouse drivers + POSIX names for structs 1628fcf (3 years, 1 month ago)
/*
* 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.c_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
tty_keypress(unsigned char key)
{
char c;
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 (ctrl && c == 'c') {
kprintf("Killing foreground process");
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.c_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);
}
/* 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_IO);
}
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;
}