BarryServer : Git

All the code for all my projects
// BarryServer : Git / Nucleus / blob / master / drivers / video / bga.c

// Related

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);
}