]> Git Repo - J-linux.git/blob - drivers/video/fbdev/core/fb_chrdev.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / video / fbdev / core / fb_chrdev.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <linux/compat.h>
4 #include <linux/console.h>
5 #include <linux/fb.h>
6 #include <linux/fbcon.h>
7 #include <linux/major.h>
8
9 #include "fb_internal.h"
10
11 /*
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.
15  *
16  * So look up the fb_info using the inode minor number,
17  * and just verify it against the reference we have.
18  */
19 static struct fb_info *file_fb_info(struct file *file)
20 {
21         struct inode *inode = file_inode(file);
22         int fbidx = iminor(inode);
23         struct fb_info *info = registered_fb[fbidx];
24
25         if (info != file->private_data)
26                 info = NULL;
27         return info;
28 }
29
30 static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
31 {
32         struct fb_info *info = file_fb_info(file);
33
34         if (!info)
35                 return -ENODEV;
36
37         if (fb_WARN_ON_ONCE(info, !info->fbops->fb_read))
38                 return -EINVAL;
39
40         if (info->state != FBINFO_STATE_RUNNING)
41                 return -EPERM;
42
43         return info->fbops->fb_read(info, buf, count, ppos);
44 }
45
46 static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
47 {
48         struct fb_info *info = file_fb_info(file);
49
50         if (!info)
51                 return -ENODEV;
52
53         if (fb_WARN_ON_ONCE(info, !info->fbops->fb_write))
54                 return -EINVAL;
55
56         if (info->state != FBINFO_STATE_RUNNING)
57                 return -EPERM;
58
59         return info->fbops->fb_write(info, buf, count, ppos);
60 }
61
62 static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
63                         unsigned long arg)
64 {
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;
71         long ret = 0;
72
73         switch (cmd) {
74         case FBIOGET_VSCREENINFO:
75                 lock_fb_info(info);
76                 var = info->var;
77                 unlock_fb_info(info);
78
79                 ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
80                 break;
81         case FBIOPUT_VSCREENINFO:
82                 if (copy_from_user(&var, argp, sizeof(var)))
83                         return -EFAULT;
84                 /* only for kernel-internal use */
85                 var.activate &= ~FB_ACTIVATE_KD_TEXT;
86                 console_lock();
87                 lock_fb_info(info);
88                 ret = fbcon_modechange_possible(info, &var);
89                 if (!ret)
90                         ret = fb_set_var(info, &var);
91                 if (!ret)
92                         fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL);
93                 unlock_fb_info(info);
94                 console_unlock();
95                 if (!ret && copy_to_user(argp, &var, sizeof(var)))
96                         ret = -EFAULT;
97                 break;
98         case FBIOGET_FSCREENINFO:
99                 lock_fb_info(info);
100                 memcpy(&fix, &info->fix, sizeof(fix));
101                 if (info->flags & FBINFO_HIDE_SMEM_START)
102                         fix.smem_start = 0;
103                 unlock_fb_info(info);
104
105                 ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
106                 break;
107         case FBIOPUTCMAP:
108                 if (copy_from_user(&cmap, argp, sizeof(cmap)))
109                         return -EFAULT;
110                 ret = fb_set_user_cmap(&cmap, info);
111                 break;
112         case FBIOGETCMAP:
113                 if (copy_from_user(&cmap, argp, sizeof(cmap)))
114                         return -EFAULT;
115                 lock_fb_info(info);
116                 cmap_from = info->cmap;
117                 unlock_fb_info(info);
118                 ret = fb_cmap_to_user(&cmap_from, &cmap);
119                 break;
120         case FBIOPAN_DISPLAY:
121                 if (copy_from_user(&var, argp, sizeof(var)))
122                         return -EFAULT;
123                 console_lock();
124                 lock_fb_info(info);
125                 ret = fb_pan_display(info, &var);
126                 unlock_fb_info(info);
127                 console_unlock();
128                 if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
129                         return -EFAULT;
130                 break;
131         case FBIO_CURSOR:
132                 ret = -EINVAL;
133                 break;
134         case FBIOGET_CON2FBMAP:
135                 ret = fbcon_get_con2fb_map_ioctl(argp);
136                 break;
137         case FBIOPUT_CON2FBMAP:
138                 ret = fbcon_set_con2fb_map_ioctl(argp);
139                 break;
140         case FBIOBLANK:
141                 if (arg > FB_BLANK_POWERDOWN)
142                         return -EINVAL;
143                 console_lock();
144                 lock_fb_info(info);
145                 ret = fb_blank(info, arg);
146                 /* might again call into fb_blank */
147                 fbcon_fb_blanked(info, arg);
148                 unlock_fb_info(info);
149                 console_unlock();
150                 break;
151         default:
152                 lock_fb_info(info);
153                 fb = info->fbops;
154                 if (fb->fb_ioctl)
155                         ret = fb->fb_ioctl(info, cmd, arg);
156                 else
157                         ret = -ENOTTY;
158                 unlock_fb_info(info);
159         }
160         return ret;
161 }
162
163 static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
164 {
165         struct fb_info *info = file_fb_info(file);
166
167         if (!info)
168                 return -ENODEV;
169         return do_fb_ioctl(info, cmd, arg);
170 }
171
172 #ifdef CONFIG_COMPAT
173 struct fb_fix_screeninfo32 {
174         char                    id[16];
175         compat_caddr_t          smem_start;
176         u32                     smem_len;
177         u32                     type;
178         u32                     type_aux;
179         u32                     visual;
180         u16                     xpanstep;
181         u16                     ypanstep;
182         u16                     ywrapstep;
183         u32                     line_length;
184         compat_caddr_t          mmio_start;
185         u32                     mmio_len;
186         u32                     accel;
187         u16                     reserved[3];
188 };
189
190 struct fb_cmap32 {
191         u32                     start;
192         u32                     len;
193         compat_caddr_t  red;
194         compat_caddr_t  green;
195         compat_caddr_t  blue;
196         compat_caddr_t  transp;
197 };
198
199 static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
200                           unsigned long arg)
201 {
202         struct fb_cmap32 cmap32;
203         struct fb_cmap cmap_from;
204         struct fb_cmap_user cmap;
205
206         if (copy_from_user(&cmap32, compat_ptr(arg), sizeof(cmap32)))
207                 return -EFAULT;
208
209         cmap = (struct fb_cmap_user) {
210                 .start  = cmap32.start,
211                 .len    = cmap32.len,
212                 .red    = compat_ptr(cmap32.red),
213                 .green  = compat_ptr(cmap32.green),
214                 .blue   = compat_ptr(cmap32.blue),
215                 .transp = compat_ptr(cmap32.transp),
216         };
217
218         if (cmd == FBIOPUTCMAP)
219                 return fb_set_user_cmap(&cmap, info);
220
221         lock_fb_info(info);
222         cmap_from = info->cmap;
223         unlock_fb_info(info);
224
225         return fb_cmap_to_user(&cmap_from, &cmap);
226 }
227
228 static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix,
229                                   struct fb_fix_screeninfo32 __user *fix32)
230 {
231         __u32 data;
232         int err;
233
234         err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id));
235
236         data = (__u32) (unsigned long) fix->smem_start;
237         err |= put_user(data, &fix32->smem_start);
238
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);
247
248         data = (__u32) (unsigned long) fix->mmio_start;
249         err |= put_user(data, &fix32->mmio_start);
250
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));
255
256         if (err)
257                 return -EFAULT;
258         return 0;
259 }
260
261 static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
262                               unsigned long arg)
263 {
264         struct fb_fix_screeninfo fix;
265
266         lock_fb_info(info);
267         fix = info->fix;
268         if (info->flags & FBINFO_HIDE_SMEM_START)
269                 fix.smem_start = 0;
270         unlock_fb_info(info);
271         return do_fscreeninfo_to_user(&fix, compat_ptr(arg));
272 }
273
274 static long fb_compat_ioctl(struct file *file, unsigned int cmd,
275                             unsigned long arg)
276 {
277         struct fb_info *info = file_fb_info(file);
278         const struct fb_ops *fb;
279         long ret = -ENOIOCTLCMD;
280
281         if (!info)
282                 return -ENODEV;
283         fb = info->fbops;
284         switch (cmd) {
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);
291                 fallthrough;
292         case FBIOBLANK:
293                 ret = do_fb_ioctl(info, cmd, arg);
294                 break;
295
296         case FBIOGET_FSCREENINFO:
297                 ret = fb_get_fscreeninfo(info, cmd, arg);
298                 break;
299
300         case FBIOGETCMAP:
301         case FBIOPUTCMAP:
302                 ret = fb_getput_cmap(info, cmd, arg);
303                 break;
304
305         default:
306                 if (fb->fb_compat_ioctl)
307                         ret = fb->fb_compat_ioctl(info, cmd, arg);
308                 break;
309         }
310         return ret;
311 }
312 #endif
313
314 static int fb_mmap(struct file *file, struct vm_area_struct *vma)
315 {
316         struct fb_info *info = file_fb_info(file);
317         int res;
318
319         if (!info)
320                 return -ENODEV;
321
322         if (fb_WARN_ON_ONCE(info, !info->fbops->fb_mmap))
323                 return -ENODEV;
324
325         mutex_lock(&info->mm_lock);
326         res = info->fbops->fb_mmap(info, vma);
327         mutex_unlock(&info->mm_lock);
328
329         return res;
330 }
331
332 static int fb_open(struct inode *inode, struct file *file)
333 __acquires(&info->lock)
334 __releases(&info->lock)
335 {
336         int fbidx = iminor(inode);
337         struct fb_info *info;
338         int res = 0;
339
340         info = get_fb_info(fbidx);
341         if (!info) {
342                 request_module("fb%d", fbidx);
343                 info = get_fb_info(fbidx);
344                 if (!info)
345                         return -ENODEV;
346         }
347         if (IS_ERR(info))
348                 return PTR_ERR(info);
349
350         lock_fb_info(info);
351         if (!try_module_get(info->fbops->owner)) {
352                 res = -ENODEV;
353                 goto out;
354         }
355         file->private_data = info;
356         if (info->fbops->fb_open) {
357                 res = info->fbops->fb_open(info, 1);
358                 if (res)
359                         module_put(info->fbops->owner);
360         }
361 #ifdef CONFIG_FB_DEFERRED_IO
362         if (info->fbdefio)
363                 fb_deferred_io_open(info, inode, file);
364 #endif
365 out:
366         unlock_fb_info(info);
367         if (res)
368                 put_fb_info(info);
369         return res;
370 }
371
372 static int fb_release(struct inode *inode, struct file *file)
373 __acquires(&info->lock)
374 __releases(&info->lock)
375 {
376         struct fb_info * const info = file->private_data;
377
378         lock_fb_info(info);
379 #if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
380         if (info->fbdefio)
381                 fb_deferred_io_release(info);
382 #endif
383         if (info->fbops->fb_release)
384                 info->fbops->fb_release(info, 1);
385         module_put(info->fbops->owner);
386         unlock_fb_info(info);
387         put_fb_info(info);
388         return 0;
389 }
390
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)
395 {
396         struct fb_info * const info = filp->private_data;
397         unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len);
398
399         if (pgoff > fb_size || len > fb_size - pgoff)
400                 return -EINVAL;
401
402         return (unsigned long)info->screen_base + pgoff;
403 }
404 #endif
405
406 static const struct file_operations fb_fops = {
407         .owner = THIS_MODULE,
408         .read = fb_read,
409         .write = fb_write,
410         .unlocked_ioctl = fb_ioctl,
411 #ifdef CONFIG_COMPAT
412         .compat_ioctl = fb_compat_ioctl,
413 #endif
414         .mmap = fb_mmap,
415         .open = fb_open,
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,
421 #endif
422 #ifdef CONFIG_FB_DEFERRED_IO
423         .fsync = fb_deferred_io_fsync,
424 #endif
425         .llseek = default_llseek,
426 };
427
428 int fb_register_chrdev(void)
429 {
430         int ret;
431
432         ret = register_chrdev(FB_MAJOR, "fb", &fb_fops);
433         if (ret) {
434                 pr_err("Unable to get major %d for fb devs\n", FB_MAJOR);
435                 return ret;
436         }
437
438         return ret;
439 }
440
441 void fb_unregister_chrdev(void)
442 {
443         unregister_chrdev(FB_MAJOR, "fb");
444 }
This page took 0.052253 seconds and 4 git commands to generate.