#include "char.h" #include #include #include #include #include #include "doomdev.h" #include "harddoom.h" #include "util.h" #include "pci.h" #include "surface.h" #define DOOMDEV_COUNT 256 #define DOOMDEV_NAME "doom" dev_t first; int major; 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(filp, params); } void free_texture(struct texture_data *texture_data); int texture_release(struct inode *inode, struct file *filp) { struct texture_data *texture_data; texture_data = filp->private_data; free_texture(texture_data); kfree(texture_data); return 0; } struct file_operations texture_fops = { .owner = THIS_MODULE, .release = texture_release }; int verify_texture_params(struct doomdev_ioctl_create_texture *params) { if (params->size > 4 * 1024 * 1024) { return -EOVERFLOW; } if (params->height > 1023) { return -EOVERFLOW; } return 0; } int alloc_texture(struct doomdev_ioctl_create_texture *params, struct texture_data *texture_data) { int err; int i; int not_written; int pages_needed; texture_data->size = params->size; texture_data->height = params->height; pages_needed = (params->size / HARDDOOM_PAGE_SIZE); if (params->size % HARDDOOM_PAGE_SIZE != 0) { pages_needed += 1; } texture_data->pages = pages_needed; texture_data->texture_cpu = dma_alloc_coherent(texture_data->doom_data->pci_device, params->size, &texture_data->texture_dev, GFP_KERNEL); ORFAIL_NULL(texture_data->texture_cpu, -ENOMEM, error_texture); texture_data->page_table_cpu = dma_alloc_coherent(texture_data->doom_data->pci_device, pages_needed * 4, &texture_data->page_table_dev, GFP_KERNEL); ORFAIL_NULL(texture_data->page_table_cpu, -ENOMEM, error_pt); for (i = 0; i < pages_needed; i++) { texture_data->page_table_cpu[i] = (HARDDOOM_PTE_PHYS_MASK & (texture_data->texture_dev + HARDDOOM_PAGE_SIZE * i)) | HARDDOOM_PTE_VALID; } not_written = copy_from_user(texture_data->texture_cpu, (void __user *) params->data_ptr, params->size); if (not_written) { err = -EFAULT; goto error_copy; } return 0; error_copy: dma_free_coherent(texture_data->doom_data->pci_device, texture_data->pages * 4, texture_data->page_table_cpu, texture_data->page_table_dev); error_pt: dma_free_coherent(texture_data->doom_data->pci_device, texture_data->size, texture_data->texture_cpu, texture_data->texture_dev); error_texture: return err; } void free_texture(struct texture_data *texture_data) { dma_free_coherent(texture_data->doom_data->pci_device, texture_data->pages * 4, texture_data->page_table_cpu, texture_data->page_table_dev); dma_free_coherent(texture_data->doom_data->pci_device, texture_data->size, texture_data->texture_cpu, texture_data->texture_dev); } long doom_create_texture(struct file *filp, unsigned long arg) { int err; struct doomdev_ioctl_create_texture *params; struct texture_data *texture_data; int fd; struct doom_data *doom_data; params = (struct doomdev_ioctl_create_texture *) arg; err = verify_texture_params(params); if (err < 0) { return err; } texture_data = kmalloc(sizeof(*texture_data), GFP_KERNEL); ORFAIL_NULL(texture_data, -ENOMEM, error_data); doom_data = container_of(filp->f_inode->i_cdev, struct doom_data, cdev); texture_data->doom_data = doom_data; ORFAIL(alloc_texture(params, texture_data), error_texture); fd = anon_inode_getfd("doom_texture", &texture_fops, texture_data, 0); ORFAIL(fd, error_inode); return fd; error_inode: free_texture(texture_data); error_texture: kfree(texture_data); error_data: return err; } void free_flat(struct flat_data *flat_data); int flat_release(struct inode *inode, struct file *filp) { struct flat_data *flat_data; flat_data = filp->private_data; free_flat(flat_data); kfree(flat_data); return 0; } struct file_operations flat_fops = { .owner = THIS_MODULE, .release = flat_release }; int alloc_flat(struct doomdev_ioctl_create_flat *params, struct flat_data *flat_data) { int err; int not_written; flat_data->flat_cpu = dma_alloc_coherent(flat_data->doom_data->pci_device, HARDDOOM_FLAT_SIZE, &flat_data->flat_dev, GFP_KERNEL); ORFAIL_NULL(flat_data->flat_cpu, -ENOMEM, error_flat); not_written = copy_from_user(flat_data->flat_cpu, (void __user *) params->data_ptr, HARDDOOM_FLAT_SIZE); if (not_written) { p("some bytes not copied\n"); err = -EFAULT; goto error_copy; } return 0; error_copy: dma_free_coherent(flat_data->doom_data->pci_device, HARDDOOM_FLAT_SIZE, flat_data->flat_cpu, flat_data->flat_dev); error_flat: return err; } void free_flat(struct flat_data *flat_data) { dma_free_coherent(flat_data->doom_data->pci_device, HARDDOOM_FLAT_SIZE, flat_data->flat_cpu, flat_data->flat_dev); } long doom_create_flat(struct file *filp, unsigned long arg) { int err; struct doomdev_ioctl_create_flat *params; struct flat_data *flat_data; int fd; struct doom_data *doom_data; params = (struct doomdev_ioctl_create_flat *) arg; flat_data = kmalloc(sizeof(*flat_data), GFP_KERNEL); ORFAIL_NULL(flat_data, -ENOMEM, error_data); doom_data = container_of(filp->f_inode->i_cdev, struct doom_data, cdev); flat_data->doom_data = doom_data; ORFAIL(alloc_flat(params, flat_data), error_flat); fd = anon_inode_getfd("doom_texture", &flat_fops, flat_data, 0); ORFAIL(fd, error_inode); return fd; error_inode: free_flat(flat_data); error_flat: kfree(flat_data); error_data: return err; } long doom_create_colormaps(struct file *filp, unsigned long arg) { return -1; } long doom_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { switch (cmd) { case DOOMDEV_IOCTL_CREATE_SURFACE: return doom_create_surface(filp, arg); case DOOMDEV_IOCTL_CREATE_TEXTURE: return doom_create_texture(filp, arg); case DOOMDEV_IOCTL_CREATE_FLAT: return doom_create_flat(filp, arg); case DOOMDEV_IOCTL_CREATE_COLORMAPS: return doom_create_colormaps(filp, arg); default: return -EINVAL; } } int doom_open(struct inode *inode, struct file *filp) { return 0; } struct file_operations doomdev_fops = { .owner = THIS_MODULE, .unlocked_ioctl = doom_ioctl, .compat_ioctl = doom_ioctl, .open = doom_open }; struct class *doom_class; int new_doomdev(struct pci_dev *dev) { int err = 0; int minor; struct doom_data *doom_data; dev_t devt; if (next_minor >= DOOMDEV_COUNT) { return -ENOMEM; } doom_data = pci_get_drvdata(dev); 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); ORFAIL_PTR(doom_data->device, error_create); return 0; error_create: cdev_del(&doom_data->cdev); error_add: return err; } void destroy_doomdev(struct doom_data *doom_data) { device_destroy(doom_class, doom_data->device->devt); cdev_del(&doom_data->cdev); } int char_init(void) { int err = 0; ORFAIL(alloc_chrdev_region(&first, 0, DOOMDEV_COUNT, DOOMDEV_NAME), error_region); major = MAJOR(first); doom_class = class_create(THIS_MODULE, "doom"); ORFAIL_PTR(doom_class, error_create); return 0; error_create: unregister_chrdev_region(first, DOOMDEV_COUNT); error_region: return err; } void char_cleanup(void) { unregister_chrdev_region(first, DOOMDEV_COUNT); class_unregister(doom_class); class_destroy(doom_class); }