1 // SPDX-License-Identifier: GPL-2.0
3 #include <linux/compat.h>
4 #include <linux/console.h>
6 #include <linux/fbcon.h>
7 #include <linux/major.h>
9 #include "fb_internal.h"
12 * We hold a reference to the fb_info in file->private_data,
13 * but if the current registered fb has changed, we don't
14 * actually want to use it.
16 * So look up the fb_info using the inode minor number,
17 * and just verify it against the reference we have.
19 static struct fb_info *file_fb_info(struct file *file)
21 struct inode *inode = file_inode(file);
22 int fbidx = iminor(inode);
23 struct fb_info *info = registered_fb[fbidx];
25 if (info != file->private_data)
30 static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
32 struct fb_info *info = file_fb_info(file);
37 if (fb_WARN_ON_ONCE(info, !info->fbops->fb_read))
40 if (info->state != FBINFO_STATE_RUNNING)
43 return info->fbops->fb_read(info, buf, count, ppos);
46 static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
48 struct fb_info *info = file_fb_info(file);
53 if (fb_WARN_ON_ONCE(info, !info->fbops->fb_write))
56 if (info->state != FBINFO_STATE_RUNNING)
59 return info->fbops->fb_write(info, buf, count, ppos);
62 static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
65 const struct fb_ops *fb;
66 struct fb_var_screeninfo var;
67 struct fb_fix_screeninfo fix;
68 struct fb_cmap cmap_from;
69 struct fb_cmap_user cmap;
70 void __user *argp = (void __user *)arg;
74 case FBIOGET_VSCREENINFO:
79 ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
81 case FBIOPUT_VSCREENINFO:
82 if (copy_from_user(&var, argp, sizeof(var)))
84 /* only for kernel-internal use */
85 var.activate &= ~FB_ACTIVATE_KD_TEXT;
88 ret = fbcon_modechange_possible(info, &var);
90 ret = fb_set_var(info, &var);
92 fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL);
95 if (!ret && copy_to_user(argp, &var, sizeof(var)))
98 case FBIOGET_FSCREENINFO:
100 memcpy(&fix, &info->fix, sizeof(fix));
101 if (info->flags & FBINFO_HIDE_SMEM_START)
103 unlock_fb_info(info);
105 ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
108 if (copy_from_user(&cmap, argp, sizeof(cmap)))
110 ret = fb_set_user_cmap(&cmap, info);
113 if (copy_from_user(&cmap, argp, sizeof(cmap)))
116 cmap_from = info->cmap;
117 unlock_fb_info(info);
118 ret = fb_cmap_to_user(&cmap_from, &cmap);
120 case FBIOPAN_DISPLAY:
121 if (copy_from_user(&var, argp, sizeof(var)))
125 ret = fb_pan_display(info, &var);
126 unlock_fb_info(info);
128 if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
134 case FBIOGET_CON2FBMAP:
135 ret = fbcon_get_con2fb_map_ioctl(argp);
137 case FBIOPUT_CON2FBMAP:
138 ret = fbcon_set_con2fb_map_ioctl(argp);
141 if (arg > FB_BLANK_POWERDOWN)
145 ret = fb_blank(info, arg);
146 /* might again call into fb_blank */
147 fbcon_fb_blanked(info, arg);
148 unlock_fb_info(info);
155 ret = fb->fb_ioctl(info, cmd, arg);
158 unlock_fb_info(info);
163 static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
165 struct fb_info *info = file_fb_info(file);
169 return do_fb_ioctl(info, cmd, arg);
173 struct fb_fix_screeninfo32 {
175 compat_caddr_t smem_start;
184 compat_caddr_t mmio_start;
194 compat_caddr_t green;
196 compat_caddr_t transp;
199 static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
202 struct fb_cmap32 cmap32;
203 struct fb_cmap cmap_from;
204 struct fb_cmap_user cmap;
206 if (copy_from_user(&cmap32, compat_ptr(arg), sizeof(cmap32)))
209 cmap = (struct fb_cmap_user) {
210 .start = cmap32.start,
212 .red = compat_ptr(cmap32.red),
213 .green = compat_ptr(cmap32.green),
214 .blue = compat_ptr(cmap32.blue),
215 .transp = compat_ptr(cmap32.transp),
218 if (cmd == FBIOPUTCMAP)
219 return fb_set_user_cmap(&cmap, info);
222 cmap_from = info->cmap;
223 unlock_fb_info(info);
225 return fb_cmap_to_user(&cmap_from, &cmap);
228 static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix,
229 struct fb_fix_screeninfo32 __user *fix32)
234 err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id));
236 data = (__u32) (unsigned long) fix->smem_start;
237 err |= put_user(data, &fix32->smem_start);
239 err |= put_user(fix->smem_len, &fix32->smem_len);
240 err |= put_user(fix->type, &fix32->type);
241 err |= put_user(fix->type_aux, &fix32->type_aux);
242 err |= put_user(fix->visual, &fix32->visual);
243 err |= put_user(fix->xpanstep, &fix32->xpanstep);
244 err |= put_user(fix->ypanstep, &fix32->ypanstep);
245 err |= put_user(fix->ywrapstep, &fix32->ywrapstep);
246 err |= put_user(fix->line_length, &fix32->line_length);
248 data = (__u32) (unsigned long) fix->mmio_start;
249 err |= put_user(data, &fix32->mmio_start);
251 err |= put_user(fix->mmio_len, &fix32->mmio_len);
252 err |= put_user(fix->accel, &fix32->accel);
253 err |= copy_to_user(fix32->reserved, fix->reserved,
254 sizeof(fix->reserved));
261 static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
264 struct fb_fix_screeninfo fix;
268 if (info->flags & FBINFO_HIDE_SMEM_START)
270 unlock_fb_info(info);
271 return do_fscreeninfo_to_user(&fix, compat_ptr(arg));
274 static long fb_compat_ioctl(struct file *file, unsigned int cmd,
277 struct fb_info *info = file_fb_info(file);
278 const struct fb_ops *fb;
279 long ret = -ENOIOCTLCMD;
285 case FBIOGET_VSCREENINFO:
286 case FBIOPUT_VSCREENINFO:
287 case FBIOPAN_DISPLAY:
288 case FBIOGET_CON2FBMAP:
289 case FBIOPUT_CON2FBMAP:
290 arg = (unsigned long) compat_ptr(arg);
293 ret = do_fb_ioctl(info, cmd, arg);
296 case FBIOGET_FSCREENINFO:
297 ret = fb_get_fscreeninfo(info, cmd, arg);
302 ret = fb_getput_cmap(info, cmd, arg);
306 if (fb->fb_compat_ioctl)
307 ret = fb->fb_compat_ioctl(info, cmd, arg);
314 static int fb_mmap(struct file *file, struct vm_area_struct *vma)
316 struct fb_info *info = file_fb_info(file);
322 if (fb_WARN_ON_ONCE(info, !info->fbops->fb_mmap))
325 mutex_lock(&info->mm_lock);
326 res = info->fbops->fb_mmap(info, vma);
327 mutex_unlock(&info->mm_lock);
332 static int fb_open(struct inode *inode, struct file *file)
333 __acquires(&info->lock)
334 __releases(&info->lock)
336 int fbidx = iminor(inode);
337 struct fb_info *info;
340 info = get_fb_info(fbidx);
342 request_module("fb%d", fbidx);
343 info = get_fb_info(fbidx);
348 return PTR_ERR(info);
351 if (!try_module_get(info->fbops->owner)) {
355 file->private_data = info;
356 if (info->fbops->fb_open) {
357 res = info->fbops->fb_open(info, 1);
359 module_put(info->fbops->owner);
361 #ifdef CONFIG_FB_DEFERRED_IO
363 fb_deferred_io_open(info, inode, file);
366 unlock_fb_info(info);
372 static int fb_release(struct inode *inode, struct file *file)
373 __acquires(&info->lock)
374 __releases(&info->lock)
376 struct fb_info * const info = file->private_data;
379 #if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
381 fb_deferred_io_release(info);
383 if (info->fbops->fb_release)
384 info->fbops->fb_release(info, 1);
385 module_put(info->fbops->owner);
386 unlock_fb_info(info);
391 #if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU)
392 static unsigned long get_fb_unmapped_area(struct file *filp,
393 unsigned long addr, unsigned long len,
394 unsigned long pgoff, unsigned long flags)
396 struct fb_info * const info = filp->private_data;
397 unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len);
399 if (pgoff > fb_size || len > fb_size - pgoff)
402 return (unsigned long)info->screen_base + pgoff;
406 static const struct file_operations fb_fops = {
407 .owner = THIS_MODULE,
410 .unlocked_ioctl = fb_ioctl,
412 .compat_ioctl = fb_compat_ioctl,
416 .release = fb_release,
417 #if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \
418 (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \
419 !defined(CONFIG_MMU))
420 .get_unmapped_area = get_fb_unmapped_area,
422 #ifdef CONFIG_FB_DEFERRED_IO
423 .fsync = fb_deferred_io_fsync,
425 .llseek = default_llseek,
428 int fb_register_chrdev(void)
432 ret = register_chrdev(FB_MAJOR, "fb", &fb_fops);
434 pr_err("Unable to get major %d for fb devs\n", FB_MAJOR);
441 void fb_unregister_chrdev(void)
443 unregister_chrdev(FB_MAJOR, "fb");