diff options
-rw-r--r-- | char.c | 19 | ||||
-rw-r--r-- | pci.c | 1 | ||||
-rw-r--r-- | surface.c | 100 | ||||
-rw-r--r-- | surface.h | 2 | ||||
-rw-r--r-- | util.h | 8 |
5 files changed, 108 insertions, 22 deletions
@@ -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) @@ -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)); @@ -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; } @@ -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 @@ -17,4 +17,12 @@ } \ }) +#define ORFAIL_NULL(ptr, error, label) \ +({ \ + if (ptr == NULL) { \ + err = error; \ + goto label; \ + } \ +}) + #endif |