#include #include #include #include #include #include #include "private_data.h" #include "harddoom.h" #include "surface.h" #include "util.h" #include "harddoomdev.h" long draw_lines(struct file *filp, unsigned long arg) { struct surface_data *surface_data; struct doomdev_surf_ioctl_draw_lines *param; struct doomdev_line *lines; int i; surface_data = filp->private_data; param = (struct doomdev_surf_ioctl_draw_lines *) arg; lines = (struct doomdev_line *) param->lines_ptr; mutex_lock(&surface_data->doom_data->cmd_mutex); for (i = 0; i < param->lines_num; i++) { draw_line(surface_data, lines[i]); } mutex_unlock(&surface_data->doom_data->cmd_mutex); return param->lines_num; } long fill_rects(struct file *filp, unsigned long arg) { struct surface_data *surface_data; struct doomdev_surf_ioctl_fill_rects *param; struct doomdev_fill_rect *rects; int i; surface_data = filp->private_data; param = (struct doomdev_surf_ioctl_fill_rects *) arg; rects = (struct doomdev_fill_rect *) param->rects_ptr; mutex_lock(&surface_data->doom_data->cmd_mutex); for (i = 0; i < param->rects_num; i++) { fill_rect(surface_data, rects[i]); } mutex_unlock(&surface_data->doom_data->cmd_mutex); return param->rects_num; } long copy_rects(struct file *filp, unsigned long arg) { struct surface_data *dst_data; struct surface_data *src_data; struct doomdev_surf_ioctl_copy_rects *param; struct doomdev_copy_rect *rects; struct fd src_fds; int i; p("in copy rects\n"); dst_data = filp->private_data; param = (struct doomdev_surf_ioctl_copy_rects *) arg; rects = (struct doomdev_copy_rect *) param->rects_ptr; src_fds = fdget(param->surf_src_fd); src_data = src_fds.file->private_data; if (dst_data->doom_data != src_data->doom_data) { p("copying between devices"); return -EINVAL; } mutex_lock(&dst_data->doom_data->cmd_mutex); for (i = 0; i < param->rects_num; i++) { copy_rect(dst_data, src_data, rects[i]); } mutex_unlock(&dst_data->doom_data->cmd_mutex); return param->rects_num; } long draw_columns(struct file *filp, unsigned long arg) { struct doomdev_surf_ioctl_draw_columns *param; struct surface_data *surface_data; struct texture_data *texture_data; struct doomdev_column *columns; struct fd texture_fds; int i; surface_data = filp->private_data; param = (struct doomdev_surf_ioctl_draw_columns *) arg; // temp if (param->draw_flags & DOOMDEV_DRAW_FLAGS_FUZZ) { return 0x400; } texture_fds = fdget(param->texture_fd); texture_data = texture_fds.file->private_data; if (surface_data->doom_data != texture_data->doom_data) { p("texture from different device\n"); return -EINVAL; } columns = (struct doomdev_column *) param->columns_ptr; mutex_lock(&surface_data->doom_data->cmd_mutex); for (i = 0; i < param->columns_num; i++) { draw_column(surface_data, texture_data, columns[i]); } mutex_unlock(&surface_data->doom_data->cmd_mutex); return param->columns_num; } long surface_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { switch (cmd) { case DOOMDEV_SURF_IOCTL_DRAW_LINES: return draw_lines(filp, arg); case DOOMDEV_SURF_IOCTL_FILL_RECTS: return fill_rects(filp, arg); case DOOMDEV_SURF_IOCTL_COPY_RECTS: return copy_rects(filp, arg); case DOOMDEV_SURF_IOCTL_DRAW_COLUMNS: return draw_columns(filp, arg); default: return -1; } } ssize_t surface_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) { struct surface_data *surface_data; unsigned long not_written; surface_data = (struct surface_data *) filp->private_data; if (*offset >= surface_data->surface_size || *offset < 0) { return 0; } if (*offset + count > surface_data->surface_size) { count = surface_data->surface_size - *offset; } mutex_lock(&surface_data->doom_data->ping_mutex); ping_sync(surface_data->doom_data->iomem); down(&surface_data->doom_data->pong_sem); not_written = copy_to_user(buf, surface_data->surface_cpu + (*offset), count); *offset += count - not_written; mutex_unlock(&surface_data->doom_data->ping_mutex); return count - not_written; } loff_t surface_llseek(struct file *filp, loff_t offset, int origin) { struct surface_data *surface_data; loff_t new_pos; surface_data = filp->private_data; switch (origin) { case SEEK_SET: new_pos = offset; break; case SEEK_CUR: new_pos = filp->f_pos + offset; break; case SEEK_END: new_pos = surface_data->surface_size + offset; break; default: return -EINVAL; } if (new_pos < 0) { return -EINVAL; } filp->f_pos = new_pos; return new_pos; } 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); kfree(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; surface_data->width = params->width; surface_data->height = params->height; 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; 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_surface); surface_data->page_table_cpu = dma_alloc_coherent(surface_data->doom_data->pci_device, pages_needed * 4, &surface_data->page_table_dev, GFP_KERNEL); ORFAIL_NULL(surface_data->page_table_cpu, -ENOMEM, error_pt); 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: dma_free_coherent(surface_data->doom_data->pci_device, surface_data->surface_size, surface_data->surface_cpu, surface_data->surface_dev); error_surface: return err; } void free_surface_buffer(struct surface_data *surface_data) { dma_free_coherent(surface_data->doom_data->pci_device, surface_data->surface_size, surface_data->surface_cpu, surface_data->surface_dev); dma_free_coherent(surface_data->doom_data->pci_device, surface_data->pages * 4, surface_data->page_table_cpu, surface_data->page_table_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, surface_data, 0); fd_s = fdget(fd); if (fd_s.file->f_op != &surface_fops) { err = -ENOENT; goto error_fdget; } 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; }