From 8ee913ba680dc520abcf08140436029e0e493220 Mon Sep 17 00:00:00 2001 From: Marcin Chrzanowski Date: Fri, 18 May 2018 14:26:35 +0200 Subject: Allocate dma buffer for surface --- surface.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 91 insertions(+), 9 deletions(-) (limited to 'surface.c') diff --git a/surface.c b/surface.c index 7dd4793..4020a31 100644 --- a/surface.c +++ b/surface.c @@ -1,9 +1,13 @@ #include #include #include +#include +#include #include "private_data.h" +#include "harddoom.h" #include "surface.h" +#include "util.h" long surface_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -21,63 +25,141 @@ loff_t surface_llseek(struct file *filp, loff_t offset, int origin) return -1; } +void free_surface_buffer(struct surface_data *surface_data); + +int surface_release (struct inode *inode, struct file *filp) +{ + struct surface_data *surface_data; + + surface_data = filp->private_data; + + free_surface_buffer(surface_data); + + return 0; +} + struct file_operations surface_fops = { .owner = THIS_MODULE, .unlocked_ioctl = surface_ioctl, .compat_ioctl = surface_ioctl, .llseek = surface_llseek, - .read = surface_read + .read = surface_read, + .release = surface_release + }; int verify_params(struct doomdev_ioctl_create_surface *params) { if (params->width < 64) { - printk(KERN_DEBUG "Surface width too small\n"); return -EINVAL; } if (params->width > 2048) { - printk(KERN_DEBUG "Surface width too large\n"); return -EOVERFLOW; } if (params->width % 64 != 0) { - printk(KERN_DEBUG "Surface width not a multiple of 64\n"); return -EINVAL; } if (params->height < 1) { - printk(KERN_DEBUG "Surface height too small\n"); return -EINVAL; } if (params->height > 2048) { - printk(KERN_DEBUG "Surface height too large\n"); return -EOVERFLOW; } return 0; } -int new_surface(struct doomdev_ioctl_create_surface *params) +int alloc_surface_buffer(struct doomdev_ioctl_create_surface *params, + struct surface_data *surface_data) +{ + int bytes_needed; + int pages_needed; + int i; + int err; + + bytes_needed = params->width * params->height; + surface_data->surface_size = bytes_needed; + pages_needed = (bytes_needed / HARDDOOM_PAGE_SIZE); + if (bytes_needed % HARDDOOM_PAGE_SIZE != 0) { + pages_needed += 1; + } + + if (pages_needed > 1024) { + return -ENOMEM; + } + + surface_data->pages = pages_needed; + bytes_needed += pages_needed * 4; + + surface_data->surface_cpu = + dma_alloc_coherent(surface_data->doom_data->pci_device, + bytes_needed, &surface_data->surface_dev, GFP_KERNEL); + ORFAIL_NULL(surface_data->surface_cpu, -ENOMEM, error_pt); + surface_data->page_table_cpu = (uint32_t *) (surface_data->surface_cpu + + HARDDOOM_PAGE_SIZE * pages_needed); + surface_data->page_table_dev = surface_data->surface_dev + + HARDDOOM_PAGE_SIZE * pages_needed; + + for (i = 0; i < pages_needed; i++) { + surface_data->page_table_cpu[i] = + (HARDDOOM_PTE_PHYS_MASK & + (surface_data->surface_dev + HARDDOOM_PAGE_SIZE * i)) | + HARDDOOM_PTE_VALID; + } + + return 0; + +error_pt: + return err; +} + +void free_surface_buffer(struct surface_data *surface_data) +{ + dma_free_coherent(surface_data->doom_data->device, + surface_data->surface_size + 4 * surface_data->pages, + surface_data->surface_cpu, surface_data->surface_dev); +} + +int new_surface(struct file *filp, struct doomdev_ioctl_create_surface *params) { int err; int fd; struct fd fd_s; + struct surface_data *surface_data; + struct doom_data *doom_data; err = verify_params(params); if (err < 0) { return err; } + surface_data = kmalloc(sizeof(*surface_data), GFP_KERNEL); + ORFAIL_NULL(surface_data, -ENOMEM, error_data); + doom_data = container_of(filp->f_inode->i_cdev, struct doom_data, cdev); + surface_data->doom_data = doom_data; + + ORFAIL(alloc_surface_buffer(params, surface_data), error_buffer); + fd = anon_inode_getfd("doom_surface", &surface_fops, NULL, 0); fd_s = fdget(fd); if (fd_s.file->f_op != &surface_fops) { - printk(KERN_DEBUG "fdget failed\n"); - return -ENOENT; + err = -ENOENT; + goto error_fdget; } + fd_s.file->private_data = surface_data; fd_s.file->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE; return fd; + +error_fdget: + free_surface_buffer(surface_data); +error_buffer: + kfree(surface_data); +error_data: + return err; } -- cgit v1.2.3