173 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <linux/fb.h>4#include <linux/module.h>5#include <linux/uaccess.h>6 7ssize_t fb_io_read(struct fb_info *info, char __user *buf, size_t count, loff_t *ppos)8{9 unsigned long p = *ppos;10 u8 *buffer, *dst;11 u8 __iomem *src;12 int c, cnt = 0, err = 0;13 unsigned long total_size, trailing;14 15 if (info->flags & FBINFO_VIRTFB)16 fb_warn_once(info, "Framebuffer is not in I/O address space.");17 18 if (!info->screen_base)19 return -ENODEV;20 21 total_size = info->screen_size;22 23 if (total_size == 0)24 total_size = info->fix.smem_len;25 26 if (p >= total_size)27 return 0;28 29 if (count >= total_size)30 count = total_size;31 32 if (count + p > total_size)33 count = total_size - p;34 35 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,36 GFP_KERNEL);37 if (!buffer)38 return -ENOMEM;39 40 src = (u8 __iomem *) (info->screen_base + p);41 42 if (info->fbops->fb_sync)43 info->fbops->fb_sync(info);44 45 while (count) {46 c = (count > PAGE_SIZE) ? PAGE_SIZE : count;47 dst = buffer;48 fb_memcpy_fromio(dst, src, c);49 dst += c;50 src += c;51 52 trailing = copy_to_user(buf, buffer, c);53 if (trailing == c) {54 err = -EFAULT;55 break;56 }57 c -= trailing;58 59 *ppos += c;60 buf += c;61 cnt += c;62 count -= c;63 }64 65 kfree(buffer);66 67 return cnt ? cnt : err;68}69EXPORT_SYMBOL(fb_io_read);70 71ssize_t fb_io_write(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos)72{73 unsigned long p = *ppos;74 u8 *buffer, *src;75 u8 __iomem *dst;76 int c, cnt = 0, err = 0;77 unsigned long total_size, trailing;78 79 if (info->flags & FBINFO_VIRTFB)80 fb_warn_once(info, "Framebuffer is not in I/O address space.");81 82 if (!info->screen_base)83 return -ENODEV;84 85 total_size = info->screen_size;86 87 if (total_size == 0)88 total_size = info->fix.smem_len;89 90 if (p > total_size)91 return -EFBIG;92 93 if (count > total_size) {94 err = -EFBIG;95 count = total_size;96 }97 98 if (count + p > total_size) {99 if (!err)100 err = -ENOSPC;101 102 count = total_size - p;103 }104 105 buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,106 GFP_KERNEL);107 if (!buffer)108 return -ENOMEM;109 110 dst = (u8 __iomem *) (info->screen_base + p);111 112 if (info->fbops->fb_sync)113 info->fbops->fb_sync(info);114 115 while (count) {116 c = (count > PAGE_SIZE) ? PAGE_SIZE : count;117 src = buffer;118 119 trailing = copy_from_user(src, buf, c);120 if (trailing == c) {121 err = -EFAULT;122 break;123 }124 c -= trailing;125 126 fb_memcpy_toio(dst, src, c);127 dst += c;128 src += c;129 *ppos += c;130 buf += c;131 cnt += c;132 count -= c;133 }134 135 kfree(buffer);136 137 return (cnt) ? cnt : err;138}139EXPORT_SYMBOL(fb_io_write);140 141int fb_io_mmap(struct fb_info *info, struct vm_area_struct *vma)142{143 unsigned long start = info->fix.smem_start;144 u32 len = info->fix.smem_len;145 unsigned long mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;146 147 if (info->flags & FBINFO_VIRTFB)148 fb_warn_once(info, "Framebuffer is not in I/O address space.");149 150 /*151 * This can be either the framebuffer mapping, or if pgoff points152 * past it, the mmio mapping.153 */154 if (vma->vm_pgoff >= mmio_pgoff) {155 if (info->var.accel_flags)156 return -EINVAL;157 158 vma->vm_pgoff -= mmio_pgoff;159 start = info->fix.mmio_start;160 len = info->fix.mmio_len;161 }162 163 vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);164 vma->vm_page_prot = pgprot_framebuffer(vma->vm_page_prot, vma->vm_start,165 vma->vm_end, start);166 167 return vm_iomap_memory(vma, start, len);168}169EXPORT_SYMBOL(fb_io_mmap);170 171MODULE_DESCRIPTION("Fbdev helpers for framebuffers in I/O memory");172MODULE_LICENSE("GPL");173