m-chrzan.xyz
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcin Chrzanowski <marcin.j.chrzanowski@gmail.com>2018-05-18 14:26:35 +0200
committerMarcin Chrzanowski <marcin.j.chrzanowski@gmail.com>2018-05-18 14:26:35 +0200
commit8ee913ba680dc520abcf08140436029e0e493220 (patch)
treee4073b92a26ebe4c3280b0d5521f4a27aa13ae41
parentd7ac0204c65b576978354f3767e8a8f29211424d (diff)
Allocate dma buffer for surface
-rw-r--r--char.c19
-rw-r--r--pci.c1
-rw-r--r--surface.c100
-rw-r--r--surface.h2
-rw-r--r--util.h8
5 files changed, 108 insertions, 22 deletions
diff --git a/char.c b/char.c
index 881c3d0..a4ba095 100644
--- a/char.c
+++ b/char.c
@@ -19,8 +19,9 @@ int next_minor = 0;
long doom_create_surface(struct file *filp, unsigned long arg)
{
struct doomdev_ioctl_create_surface *params;
+
params = (struct doomdev_ioctl_create_surface *)arg;
- return new_surface(params);
+ return new_surface(filp, params);
}
long doom_create_texture(struct file *filp, unsigned long arg)
@@ -81,33 +82,27 @@ int new_doomdev(struct pci_dev *dev)
doom_data = pci_get_drvdata(dev);
- doom_data->cdev = cdev_alloc();
- if (doom_data->cdev == NULL) {
- err = -ENOMEM;
- goto error_cdev;
- }
- cdev_init(doom_data->cdev, &doomdev_fops);
- ORFAIL(cdev_add(doom_data->cdev, first, 1), error_add);
+ cdev_init(&doom_data->cdev, &doomdev_fops);
+ ORFAIL(cdev_add(&doom_data->cdev, first, 1), error_add);
minor = next_minor++;
devt = MKDEV(major, minor);
doom_data->device = device_create(doom_class, &dev->dev, devt, NULL,
- "doom%d", minor),
+ "doom%d", minor);
ORFAIL_PTR(doom_data->device, error_create);
return 0;
error_create:
- cdev_del(doom_data->cdev);
+ cdev_del(&doom_data->cdev);
error_add:
-error_cdev:
return err;
}
void destroy_doomdev(struct doom_data *doom_data)
{
device_destroy(doom_class, doom_data->device->devt);
- cdev_del(doom_data->cdev);
+ cdev_del(&doom_data->cdev);
}
int char_init(void)
diff --git a/pci.c b/pci.c
index e4cfde6..dd31f57 100644
--- a/pci.c
+++ b/pci.c
@@ -23,6 +23,7 @@ int init_pci(struct pci_dev *dev) {
}
pci_set_drvdata(dev, doom_data);
doom_data->iomem = pci_iomap(dev, 0, 0);
+ doom_data->pci_device = &dev->dev;
pci_set_master(dev);
pci_set_dma_mask(dev, DMA_BIT_MASK(32));
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;
}
diff --git a/surface.h b/surface.h
index db9c80c..bd65b66 100644
--- a/surface.h
+++ b/surface.h
@@ -3,6 +3,6 @@
#include "doomdev.h"
-int new_surface(struct doomdev_ioctl_create_surface *params);
+int new_surface(struct file *filp, struct doomdev_ioctl_create_surface *params);
#endif
diff --git a/util.h b/util.h
index 8c56f67..a5606ad 100644
--- a/util.h
+++ b/util.h
@@ -17,4 +17,12 @@
} \
})
+#define ORFAIL_NULL(ptr, error, label) \
+({ \
+ if (ptr == NULL) { \
+ err = error; \
+ goto label; \
+ } \
+})
+
#endif