Nucleus
Barry System headers (remove libc dependency) 18495cf (3 years, 2 months ago)
/*
* 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 <sys/errno.h>
#include <sys/fb.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <nucleus/driver.h>
#include <nucleus/io.h>
#include <nucleus/kernel.h>
#include <nucleus/lib.h>
#include <nucleus/memory.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;
if (!argp)
return -EFAULT;
switch (request) {
case FBIOGET_VSCREENINFO:
vinfo = (void *) argp;
if (!verify_access(vinfo, sizeof(FBVarInfo), PROT_WRITE))
return -EFAULT;
vinfo->xres = bgaDev.width;
vinfo->yres = bgaDev.height;
vinfo->bpp = bgaDev.bpp;
return 0;
case FBIOPUT_VSCREENINFO:
vinfo = (void *) argp;
if (!verify_access(vinfo, sizeof(FBVarInfo), PROT_WRITE))
return -EFAULT;
bga_set_mode(vinfo->xres, vinfo->yres, vinfo->bpp);
return 0;
case FBIOGET_FSCREENINFO:
finfo = (void *) argp;
if (!verify_access(finfo, sizeof(FBFixInfo), PROT_WRITE))
return -EFAULT;
finfo->fbmem = bgaDev.lfb;
finfo->fbmemLen = bgaDev.memSize;
return 0;
default:
return -EINVAL;
}
}
/* 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);
}