m-chrzan.xyz
aboutsummaryrefslogtreecommitdiff
path: root/surface.c
diff options
context:
space:
mode:
Diffstat (limited to 'surface.c')
-rw-r--r--surface.c100
1 files changed, 91 insertions, 9 deletions
diff --git a/surface.c b/surface.c
index 7dd4793..4020a31 100644
--- a/surface.c
+++ b/surface.c
@@ -1,9 +1,13 @@
#include <linux/anon_inodes.h>
#include <linux/file.h>
#include <linux/fs.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
#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;
}