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");