Nucleus
Barry Basic BGA driver 6f4db8d (3 years, 2 months ago)
diff --git a/drivers/devices.c b/drivers/devices.c
index a5873df..9e0203a 100644
--- a/drivers/devices.c
+++ b/drivers/devices.c
@@ -16,6 +16,7 @@ struct Device {
};
void pci_init_ide(uint8_t bus, uint8_t dev, uint8_t func);
+void pci_init_bga(uint8_t bus, uint8_t dev, uint8_t func);
/* Device database */
struct Device devices[] = {
@@ -32,7 +33,7 @@ struct Device devices[] = {
/* Virtual devices */
{0x1B36, 0x000D, "QEMU XHCI Host Adapter", NULL},
- {0x1234, 0x1111, "QEMU/Bochs VBE Framebuffer", NULL},
+ {0x1234, 0x1111, "QEMU/Bochs VBE Framebuffer", pci_init_bga},
};
/* Initialise a PCI device */
@@ -50,14 +51,8 @@ init_pci_device(int bus, int dev, int func)
if (devices[d].vendor != vendor
|| devices[d].device != device)
continue;
- kprintf("PCI(%d,%d,%d) %s", bus, dev, func,
- devices[d].name);
if (devices[d].init)
devices[d].init(bus, dev, func);
return;
}
-
- /* No match */
- kprintf("PCI(%d,%d,%d) = %#.4x:%#.4x", bus, dev, func,
- vendor, device);
}
diff --git a/drivers/video/bga.c b/drivers/video/bga.c
new file mode 100644
index 0000000..3354d01
--- /dev/null
+++ b/drivers/video/bga.c
@@ -0,0 +1,160 @@
+/*
+ * This file is the driver for the PCI BGA device. It controls the Bochs
+ * Graphics Adapter card on virtual machines. It creates a framebuffer device
+ * node in the devfs mount to give access to the rest of the system.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/fb.h>
+#include <fcntl.h>
+#include <io.h>
+#include <nucleus/driver.h>
+#include <nucleus/panic.h>
+#include <nucleus/pci.h>
+#include <nucleus/vfs.h>
+
+/* BGA Register Indicies */
+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_VIRTH_HEIGHT,
+ BGA_INDEX_XOFF,
+ BGA_INDEX_YOFF,
+};
+
+/* Structure for a Video Device */
+struct VideoDev {
+ int width, height, bpp;
+ size_t memSize;
+ uintptr_t lfb;
+};
+
+const uint16_t BGA_LFB = 0x40;
+/* BGA Ports */
+const uint16_t BGA_IO_INDEX = 0x1CE;
+const uint16_t BGA_IO_DATA = 0x1CF;
+
+size_t bga_dev_write(File *file, char *buf, size_t size, off_t offset);
+int bga_dev_ioctl(File *file, unsigned long request, uintptr_t argp);
+void bga_dev_mmap(File *file, void *addr, size_t len, off_t offset);
+
+/* File Operations for the BGA device nodes */
+FileOps bgaFileOps = {
+ .write = bga_dev_write,
+ .ioctl = bga_dev_ioctl,
+ .mmap = bga_dev_mmap,
+};
+
+static struct VideoDev bgaDev;
+
+/* Write a BGA register */
+static void
+bga_write_register(enum BGAIndex index, uint16_t data)
+{
+ outw(BGA_IO_INDEX, index);
+ outw(BGA_IO_DATA, data);
+}
+
+/* Set the video mode */
+static void
+bga_set_mode(uint32_t width, uint32_t height, uint32_t bpp)
+{
+ bgaDev.width = width;
+ bgaDev.height = height;
+ bgaDev.bpp = bpp;
+ bgaDev.memSize = width * height * (bpp / 8);
+ 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 | BGA_LFB);
+}
+
+/* Write to the BGA framebuffer */
+size_t
+bga_dev_write(File *file, char *buf, size_t size, off_t offset)
+{
+ Page *page;
+ char *data;
+ size_t min, count = 0;
+ off_t indent = offset % PAGE_SIZE;
+ off_t pgoff = PAGE_ADDR(offset + count);
+ while (size) {
+ min = (size < PAGE_SIZE) ? size : PAGE_SIZE;
+ page = create_page(file->inode->pages,
+ bgaDev.lfb + pgoff, pgoff);
+ data = map_page(page);
+ memcpy(data + indent, buf + count, min);
+ remove(file->inode->pages, page);
+ count += min;
+ size -= min;
+ indent = 0;
+ }
+ return count;
+}
+
+/* Control the BGA device */
+int
+bga_dev_ioctl(File *file, unsigned long request, uintptr_t argp)
+{
+ FBVarInfo *vinfo;
+ FBFixInfo *finfo;
+
+ switch (request) {
+ case FBIOGET_VSCREENINFO:
+ vinfo = (void *) argp;
+ vinfo->xres = bgaDev.width;
+ vinfo->yres = bgaDev.height;
+ vinfo->bpp = bgaDev.bpp;
+ 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 = bgaDev.lfb;
+ finfo->fbmemLen = bgaDev.memSize;
+ return 0;
+ }
+}
+
+/* Map the linear frame buffer into memory */
+void
+bga_dev_mmap(File *file, void *addr, size_t len, off_t offset)
+{
+ return;
+}
+
+/* Initialise a PCI BGA device */
+void
+pci_init_bga(uint8_t bus, uint8_t dev, uint8_t func)
+{
+ static int bgaMajor = 0;
+ if (!bgaMajor)
+ bgaMajor = register_driver(2, &bgaFileOps);
+
+ /* Setup device */
+ const int width = 1280;
+ const int height = 720;
+ const int bpp = 32;
+ bga_set_mode(width, height, bpp);
+ bgaDev.lfb = pci_read_dword(bus, dev, func, 0x10) & ~0xF;
+
+ mknod("/dev/fb", S_IFCHR | 0600, MKDEV(bgaMajor, 0));
+
+ uintptr_t ptr;
+ int fd = open("/dev/fb", O_RDONLY);
+ File *file = get(get_file_by_fd(fd));
+ close(fd);
+ for (ptr = 0; ptr < bgaDev.memSize; ptr += PAGE_SIZE)
+ create_page(file->inode->pages, bgaDev.lfb + ptr, ptr);
+}