1  // SPDX-License-Identifier: GPL-2.0
2  
3  #include <linux/fb.h>
4  #include <linux/module.h>
5  #include <linux/uaccess.h>
6  
fb_io_read(struct fb_info * info,char __user * buf,size_t count,loff_t * ppos)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((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  }
69  EXPORT_SYMBOL(fb_io_read);
70  
fb_io_write(struct fb_info * info,const char __user * buf,size_t count,loff_t * ppos)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((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  }
139  EXPORT_SYMBOL(fb_io_write);
140  
fb_io_mmap(struct fb_info * info,struct vm_area_struct * vma)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(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  }
169  EXPORT_SYMBOL(fb_io_mmap);
170  
171  MODULE_DESCRIPTION("Fbdev helpers for framebuffers in I/O memory");
172  MODULE_LICENSE("GPL");
173