#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) { return -1; } ssize_t surface_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) { return -1; } 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, .release = surface_release }; int verify_params(struct doomdev_ioctl_create_surface *params) { if (params->width < 64) { return -EINVAL; } if (params->width > 2048) { return -EOVERFLOW; } if (params->width % 64 != 0) { return -EINVAL; } if (params->height < 1) { return -EINVAL; } if (params->height > 2048) { return -EOVERFLOW; } return 0; } 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) { 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; }