Orion
Barry Importing existing Orion kernel d41a53c (3 years, 3 months ago)
diff --git a/drivers/vga/bga.c b/drivers/vga/bga.c
new file mode 100644
index 0000000..aff91aa
--- /dev/null
+++ b/drivers/vga/bga.c
@@ -0,0 +1,131 @@
+/*
+ * This file implements the driver for the Bochs Graphics Adapter video card.
+ * It is a simple card available in virtual machines. It creates a framebuffer
+ * device node, and handles the reading/writing to it, and also mapping the
+ * framebuffer into memory.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/fb.h>
+#include "../drivers.h"
+#include "../pci.h"
+#include "../../mem/paging.h"
+#include "../../vfs/vfs.h"
+#include "../../io.h"
+
+size_t bga_write(File *file, char *buf, size_t size, off_t offset);
+int bga_ioctl(File *file, unsigned long request, uintptr_t argp);
+
+FileOps bgaFileOps = {
+ .write = bga_write,
+ .ioctl = bga_ioctl,
+};
+
+Driver bgaDriver = {
+ .major = 3,
+ .ops = &bgaFileOps,
+ .next = NULL,
+};
+
+static uint32_t bgaWidth, bgaHeight, bgaBpp;
+static uintptr_t lfb;
+
+enum BGAIndex {
+ BGA_INDEX_ID,
+ BGA_INDEX_XRES,
+ BGA_INDEX_YRES,
+ BGA_INDEX_BPP,
+ BGA_INDEX_ENABLE,
+ BGA_INDEX_BANK,
+ BGA_INDEX_VIRT_WIDTH,
+ BGA_INDEX_VIRT_HEIGHT,
+ BGA_INDEX_XOFF,
+ BGA_INDEX_YOFF,
+};
+
+/* Write a BGA register */
+void
+bga_write_register(uint16_t index, uint16_t data)
+{
+ outw(0x1CE, index);
+ outw(0x1CF, data);
+}
+
+/* Set the video mode */
+void
+bga_set_mode(uint32_t width, uint32_t height, uint32_t bpp)
+{
+ bgaWidth = width;
+ bgaHeight = height;
+ bgaBpp = bpp;
+ bga_write_register(BGA_INDEX_ENABLE, 0);
+ bga_write_register(BGA_INDEX_XRES, width);
+ bga_write_register(BGA_INDEX_YRES, height);
+ bga_write_register(BGA_INDEX_BPP, bpp);
+ bga_write_register(BGA_INDEX_ENABLE, 1 | 0x40);
+ /* 0x40 for framebuffer */
+}
+
+/* Initialise the BGA card */
+void
+bga_init(uint8_t bus, uint8_t slot, uint8_t func)
+{
+ register_driver(&bgaDriver);
+
+ const int width = 1280;
+ const int height = 720;
+ const int bpp = 32;
+ bga_set_mode(width, height, bpp);
+ lfb = pci_read_dword(bus, slot, func, 0x10) & 0xFFFFFFF0;
+ for (uintptr_t i = 0; i < width*height*(bpp/8); i += 0x1000)
+ *get_page((void *) lfb + i) = (0xFD000000 + i) | PTE_PRESENT
+ | PTE_WRITE | PTE_GLOBAL;
+ /* use the MMAP method to map pages of it into a page dir */
+ mknod("/dev/fb", S_IFCHR | 0600, MKDEV(bgaDriver.major, 0));
+}
+
+/* Write to the BGA framebuffer */
+size_t
+bga_write(File *file, char *buf, size_t size, off_t offset)
+{
+ size_t max, count = 0;
+ page_t oldpg;
+ while (size) {
+ max = (size < 0x1000) ? size : 0x1000;
+ acquire(&quickPageLock);
+ oldpg = quick_page(lfb + PG_ADDR(offset) + count);
+ memcpy(QUICK_PAGE, buf + count, max);
+ quick_page(oldpg);
+ release(&quickPageLock);
+ count += max;
+ size -= max;
+ }
+ return count;
+}
+
+/* Control the BGA framebuffer/device */
+int
+bga_ioctl(File *file, unsigned long request, uintptr_t argp)
+{
+ FBVarInfo *vinfo;
+ FBFixInfo *finfo;
+
+ switch (request) {
+ case FBIOGET_VSCREENINFO:
+ vinfo = (void *) argp;
+ vinfo->xres = bgaWidth;
+ vinfo->yres = bgaHeight;
+ vinfo->bpp = bgaBpp;
+ return 0;
+ case FBIOPUT_VSCREENINFO:
+ vinfo = (void *) argp;
+ bga_set_mode(vinfo->xres, vinfo->yres, vinfo->bpp);
+ return 0;
+ case FBIOGET_FSCREENINFO:
+ finfo = (void *) argp;
+ finfo->fbmem = 0xFD000000; // lfb;
+ finfo->fbmemLen = bgaWidth*bgaHeight*(bgaBpp/8);
+ return 0;
+ }
+}