1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #include <linux/fb.h> |
4 | #include <linux/module.h> |
5 | #include <linux/uaccess.h> |
6 | |
7 | ssize_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(size: (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(to: dst, from: src, n: c); |
49 | dst += c; |
50 | src += c; |
51 | |
52 | trailing = copy_to_user(to: buf, from: buffer, n: 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(objp: buffer); |
66 | |
67 | return cnt ? cnt : err; |
68 | } |
69 | EXPORT_SYMBOL(fb_io_read); |
70 | |
71 | ssize_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(size: (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(to: src, from: buf, n: c); |
120 | if (trailing == c) { |
121 | err = -EFAULT; |
122 | break; |
123 | } |
124 | c -= trailing; |
125 | |
126 | fb_memcpy_toio(to: dst, from: src, n: c); |
127 | dst += c; |
128 | src += c; |
129 | *ppos += c; |
130 | buf += c; |
131 | cnt += c; |
132 | count -= c; |
133 | } |
134 | |
135 | kfree(objp: buffer); |
136 | |
137 | return (cnt) ? cnt : err; |
138 | } |
139 | EXPORT_SYMBOL(fb_io_write); |
140 | |
141 | int 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 points |
152 | * 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(vm_flags: vma->vm_flags); |
164 | vma->vm_page_prot = pgprot_framebuffer(prot: vma->vm_page_prot, vm_start: vma->vm_start, |
165 | vm_end: vma->vm_end, offset: start); |
166 | |
167 | return vm_iomap_memory(vma, start, len); |
168 | } |
169 | EXPORT_SYMBOL(fb_io_mmap); |
170 | |
171 | MODULE_DESCRIPTION("Fbdev helpers for framebuffers in I/O memory" ); |
172 | MODULE_LICENSE("GPL" ); |
173 | |