BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / blob / 1628fcfdfdf2978ed9ccac96ee7d13bb3dc43a01 / drivers / tty / tty.c

// Related

Orion

Barry Keyboard/Mouse drivers + POSIX names for structs 1628fcf (2 years, 4 months 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;
}