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