1588b3563SThomas Zimmermann // SPDX-License-Identifier: GPL-2.0
2588b3563SThomas Zimmermann
3588b3563SThomas Zimmermann #include <linux/compat.h>
4588b3563SThomas Zimmermann #include <linux/console.h>
5588b3563SThomas Zimmermann #include <linux/fb.h>
6588b3563SThomas Zimmermann #include <linux/fbcon.h>
7588b3563SThomas Zimmermann #include <linux/major.h>
8588b3563SThomas Zimmermann
9588b3563SThomas Zimmermann #include "fb_internal.h"
10588b3563SThomas Zimmermann
11588b3563SThomas Zimmermann /*
12588b3563SThomas Zimmermann * We hold a reference to the fb_info in file->private_data,
13588b3563SThomas Zimmermann * but if the current registered fb has changed, we don't
14588b3563SThomas Zimmermann * actually want to use it.
15588b3563SThomas Zimmermann *
16588b3563SThomas Zimmermann * So look up the fb_info using the inode minor number,
17588b3563SThomas Zimmermann * and just verify it against the reference we have.
18588b3563SThomas Zimmermann */
file_fb_info(struct file * file)19588b3563SThomas Zimmermann static struct fb_info *file_fb_info(struct file *file)
20588b3563SThomas Zimmermann {
21588b3563SThomas Zimmermann struct inode *inode = file_inode(file);
22588b3563SThomas Zimmermann int fbidx = iminor(inode);
23588b3563SThomas Zimmermann struct fb_info *info = registered_fb[fbidx];
24588b3563SThomas Zimmermann
25588b3563SThomas Zimmermann if (info != file->private_data)
26588b3563SThomas Zimmermann info = NULL;
27588b3563SThomas Zimmermann return info;
28588b3563SThomas Zimmermann }
29588b3563SThomas Zimmermann
fb_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)30588b3563SThomas Zimmermann static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
31588b3563SThomas Zimmermann {
32588b3563SThomas Zimmermann struct fb_info *info = file_fb_info(file);
33588b3563SThomas Zimmermann
34588b3563SThomas Zimmermann if (!info)
35588b3563SThomas Zimmermann return -ENODEV;
36588b3563SThomas Zimmermann
378813e86fSThomas Zimmermann if (fb_WARN_ON_ONCE(info, !info->fbops->fb_read))
388813e86fSThomas Zimmermann return -EINVAL;
398813e86fSThomas Zimmermann
40588b3563SThomas Zimmermann if (info->state != FBINFO_STATE_RUNNING)
41588b3563SThomas Zimmermann return -EPERM;
42588b3563SThomas Zimmermann
43588b3563SThomas Zimmermann return info->fbops->fb_read(info, buf, count, ppos);
44588b3563SThomas Zimmermann }
45588b3563SThomas Zimmermann
fb_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)46588b3563SThomas Zimmermann static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
47588b3563SThomas Zimmermann {
48588b3563SThomas Zimmermann struct fb_info *info = file_fb_info(file);
49588b3563SThomas Zimmermann
50588b3563SThomas Zimmermann if (!info)
51588b3563SThomas Zimmermann return -ENODEV;
52588b3563SThomas Zimmermann
538813e86fSThomas Zimmermann if (fb_WARN_ON_ONCE(info, !info->fbops->fb_write))
548813e86fSThomas Zimmermann return -EINVAL;
558813e86fSThomas Zimmermann
56588b3563SThomas Zimmermann if (info->state != FBINFO_STATE_RUNNING)
57588b3563SThomas Zimmermann return -EPERM;
58588b3563SThomas Zimmermann
59588b3563SThomas Zimmermann return info->fbops->fb_write(info, buf, count, ppos);
60588b3563SThomas Zimmermann }
61588b3563SThomas Zimmermann
do_fb_ioctl(struct fb_info * info,unsigned int cmd,unsigned long arg)62588b3563SThomas Zimmermann static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
63588b3563SThomas Zimmermann unsigned long arg)
64588b3563SThomas Zimmermann {
65588b3563SThomas Zimmermann const struct fb_ops *fb;
66588b3563SThomas Zimmermann struct fb_var_screeninfo var;
67588b3563SThomas Zimmermann struct fb_fix_screeninfo fix;
68588b3563SThomas Zimmermann struct fb_cmap cmap_from;
69588b3563SThomas Zimmermann struct fb_cmap_user cmap;
70588b3563SThomas Zimmermann void __user *argp = (void __user *)arg;
71588b3563SThomas Zimmermann long ret = 0;
72588b3563SThomas Zimmermann
73588b3563SThomas Zimmermann switch (cmd) {
74588b3563SThomas Zimmermann case FBIOGET_VSCREENINFO:
75588b3563SThomas Zimmermann lock_fb_info(info);
76588b3563SThomas Zimmermann var = info->var;
77588b3563SThomas Zimmermann unlock_fb_info(info);
78588b3563SThomas Zimmermann
79588b3563SThomas Zimmermann ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
80588b3563SThomas Zimmermann break;
81588b3563SThomas Zimmermann case FBIOPUT_VSCREENINFO:
82588b3563SThomas Zimmermann if (copy_from_user(&var, argp, sizeof(var)))
83588b3563SThomas Zimmermann return -EFAULT;
84588b3563SThomas Zimmermann /* only for kernel-internal use */
85588b3563SThomas Zimmermann var.activate &= ~FB_ACTIVATE_KD_TEXT;
86588b3563SThomas Zimmermann console_lock();
87588b3563SThomas Zimmermann lock_fb_info(info);
88588b3563SThomas Zimmermann ret = fbcon_modechange_possible(info, &var);
89588b3563SThomas Zimmermann if (!ret)
90588b3563SThomas Zimmermann ret = fb_set_var(info, &var);
91588b3563SThomas Zimmermann if (!ret)
92588b3563SThomas Zimmermann fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL);
93588b3563SThomas Zimmermann unlock_fb_info(info);
94588b3563SThomas Zimmermann console_unlock();
95588b3563SThomas Zimmermann if (!ret && copy_to_user(argp, &var, sizeof(var)))
96588b3563SThomas Zimmermann ret = -EFAULT;
97588b3563SThomas Zimmermann break;
98588b3563SThomas Zimmermann case FBIOGET_FSCREENINFO:
99588b3563SThomas Zimmermann lock_fb_info(info);
100588b3563SThomas Zimmermann memcpy(&fix, &info->fix, sizeof(fix));
101588b3563SThomas Zimmermann if (info->flags & FBINFO_HIDE_SMEM_START)
102588b3563SThomas Zimmermann fix.smem_start = 0;
103588b3563SThomas Zimmermann unlock_fb_info(info);
104588b3563SThomas Zimmermann
105588b3563SThomas Zimmermann ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
106588b3563SThomas Zimmermann break;
107588b3563SThomas Zimmermann case FBIOPUTCMAP:
108588b3563SThomas Zimmermann if (copy_from_user(&cmap, argp, sizeof(cmap)))
109588b3563SThomas Zimmermann return -EFAULT;
110588b3563SThomas Zimmermann ret = fb_set_user_cmap(&cmap, info);
111588b3563SThomas Zimmermann break;
112588b3563SThomas Zimmermann case FBIOGETCMAP:
113588b3563SThomas Zimmermann if (copy_from_user(&cmap, argp, sizeof(cmap)))
114588b3563SThomas Zimmermann return -EFAULT;
115588b3563SThomas Zimmermann lock_fb_info(info);
116588b3563SThomas Zimmermann cmap_from = info->cmap;
117588b3563SThomas Zimmermann unlock_fb_info(info);
118588b3563SThomas Zimmermann ret = fb_cmap_to_user(&cmap_from, &cmap);
119588b3563SThomas Zimmermann break;
120588b3563SThomas Zimmermann case FBIOPAN_DISPLAY:
121588b3563SThomas Zimmermann if (copy_from_user(&var, argp, sizeof(var)))
122588b3563SThomas Zimmermann return -EFAULT;
123588b3563SThomas Zimmermann console_lock();
124588b3563SThomas Zimmermann lock_fb_info(info);
125588b3563SThomas Zimmermann ret = fb_pan_display(info, &var);
126588b3563SThomas Zimmermann unlock_fb_info(info);
127588b3563SThomas Zimmermann console_unlock();
128588b3563SThomas Zimmermann if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
129588b3563SThomas Zimmermann return -EFAULT;
130588b3563SThomas Zimmermann break;
131588b3563SThomas Zimmermann case FBIO_CURSOR:
132588b3563SThomas Zimmermann ret = -EINVAL;
133588b3563SThomas Zimmermann break;
134588b3563SThomas Zimmermann case FBIOGET_CON2FBMAP:
135588b3563SThomas Zimmermann ret = fbcon_get_con2fb_map_ioctl(argp);
136588b3563SThomas Zimmermann break;
137588b3563SThomas Zimmermann case FBIOPUT_CON2FBMAP:
138588b3563SThomas Zimmermann ret = fbcon_set_con2fb_map_ioctl(argp);
139588b3563SThomas Zimmermann break;
140588b3563SThomas Zimmermann case FBIOBLANK:
141588b3563SThomas Zimmermann if (arg > FB_BLANK_POWERDOWN)
142588b3563SThomas Zimmermann return -EINVAL;
143588b3563SThomas Zimmermann console_lock();
144588b3563SThomas Zimmermann lock_fb_info(info);
145588b3563SThomas Zimmermann ret = fb_blank(info, arg);
146588b3563SThomas Zimmermann /* might again call into fb_blank */
147588b3563SThomas Zimmermann fbcon_fb_blanked(info, arg);
148588b3563SThomas Zimmermann unlock_fb_info(info);
149588b3563SThomas Zimmermann console_unlock();
150588b3563SThomas Zimmermann break;
151588b3563SThomas Zimmermann default:
152588b3563SThomas Zimmermann lock_fb_info(info);
153588b3563SThomas Zimmermann fb = info->fbops;
154588b3563SThomas Zimmermann if (fb->fb_ioctl)
155588b3563SThomas Zimmermann ret = fb->fb_ioctl(info, cmd, arg);
156588b3563SThomas Zimmermann else
157588b3563SThomas Zimmermann ret = -ENOTTY;
158588b3563SThomas Zimmermann unlock_fb_info(info);
159588b3563SThomas Zimmermann }
160588b3563SThomas Zimmermann return ret;
161588b3563SThomas Zimmermann }
162588b3563SThomas Zimmermann
fb_ioctl(struct file * file,unsigned int cmd,unsigned long arg)163588b3563SThomas Zimmermann static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
164588b3563SThomas Zimmermann {
165588b3563SThomas Zimmermann struct fb_info *info = file_fb_info(file);
166588b3563SThomas Zimmermann
167588b3563SThomas Zimmermann if (!info)
168588b3563SThomas Zimmermann return -ENODEV;
169588b3563SThomas Zimmermann return do_fb_ioctl(info, cmd, arg);
170588b3563SThomas Zimmermann }
171588b3563SThomas Zimmermann
172588b3563SThomas Zimmermann #ifdef CONFIG_COMPAT
173588b3563SThomas Zimmermann struct fb_fix_screeninfo32 {
174588b3563SThomas Zimmermann char id[16];
175588b3563SThomas Zimmermann compat_caddr_t smem_start;
176588b3563SThomas Zimmermann u32 smem_len;
177588b3563SThomas Zimmermann u32 type;
178588b3563SThomas Zimmermann u32 type_aux;
179588b3563SThomas Zimmermann u32 visual;
180588b3563SThomas Zimmermann u16 xpanstep;
181588b3563SThomas Zimmermann u16 ypanstep;
182588b3563SThomas Zimmermann u16 ywrapstep;
183588b3563SThomas Zimmermann u32 line_length;
184588b3563SThomas Zimmermann compat_caddr_t mmio_start;
185588b3563SThomas Zimmermann u32 mmio_len;
186588b3563SThomas Zimmermann u32 accel;
187588b3563SThomas Zimmermann u16 reserved[3];
188588b3563SThomas Zimmermann };
189588b3563SThomas Zimmermann
190588b3563SThomas Zimmermann struct fb_cmap32 {
191588b3563SThomas Zimmermann u32 start;
192588b3563SThomas Zimmermann u32 len;
193588b3563SThomas Zimmermann compat_caddr_t red;
194588b3563SThomas Zimmermann compat_caddr_t green;
195588b3563SThomas Zimmermann compat_caddr_t blue;
196588b3563SThomas Zimmermann compat_caddr_t transp;
197588b3563SThomas Zimmermann };
198588b3563SThomas Zimmermann
fb_getput_cmap(struct fb_info * info,unsigned int cmd,unsigned long arg)199588b3563SThomas Zimmermann static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
200588b3563SThomas Zimmermann unsigned long arg)
201588b3563SThomas Zimmermann {
202588b3563SThomas Zimmermann struct fb_cmap32 cmap32;
203588b3563SThomas Zimmermann struct fb_cmap cmap_from;
204588b3563SThomas Zimmermann struct fb_cmap_user cmap;
205588b3563SThomas Zimmermann
206588b3563SThomas Zimmermann if (copy_from_user(&cmap32, compat_ptr(arg), sizeof(cmap32)))
207588b3563SThomas Zimmermann return -EFAULT;
208588b3563SThomas Zimmermann
209588b3563SThomas Zimmermann cmap = (struct fb_cmap_user) {
210588b3563SThomas Zimmermann .start = cmap32.start,
211588b3563SThomas Zimmermann .len = cmap32.len,
212588b3563SThomas Zimmermann .red = compat_ptr(cmap32.red),
213588b3563SThomas Zimmermann .green = compat_ptr(cmap32.green),
214588b3563SThomas Zimmermann .blue = compat_ptr(cmap32.blue),
215588b3563SThomas Zimmermann .transp = compat_ptr(cmap32.transp),
216588b3563SThomas Zimmermann };
217588b3563SThomas Zimmermann
218588b3563SThomas Zimmermann if (cmd == FBIOPUTCMAP)
219588b3563SThomas Zimmermann return fb_set_user_cmap(&cmap, info);
220588b3563SThomas Zimmermann
221588b3563SThomas Zimmermann lock_fb_info(info);
222588b3563SThomas Zimmermann cmap_from = info->cmap;
223588b3563SThomas Zimmermann unlock_fb_info(info);
224588b3563SThomas Zimmermann
225588b3563SThomas Zimmermann return fb_cmap_to_user(&cmap_from, &cmap);
226588b3563SThomas Zimmermann }
227588b3563SThomas Zimmermann
do_fscreeninfo_to_user(struct fb_fix_screeninfo * fix,struct fb_fix_screeninfo32 __user * fix32)228588b3563SThomas Zimmermann static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix,
229588b3563SThomas Zimmermann struct fb_fix_screeninfo32 __user *fix32)
230588b3563SThomas Zimmermann {
231588b3563SThomas Zimmermann __u32 data;
232588b3563SThomas Zimmermann int err;
233588b3563SThomas Zimmermann
234588b3563SThomas Zimmermann err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id));
235588b3563SThomas Zimmermann
236588b3563SThomas Zimmermann data = (__u32) (unsigned long) fix->smem_start;
237588b3563SThomas Zimmermann err |= put_user(data, &fix32->smem_start);
238588b3563SThomas Zimmermann
239588b3563SThomas Zimmermann err |= put_user(fix->smem_len, &fix32->smem_len);
240588b3563SThomas Zimmermann err |= put_user(fix->type, &fix32->type);
241588b3563SThomas Zimmermann err |= put_user(fix->type_aux, &fix32->type_aux);
242588b3563SThomas Zimmermann err |= put_user(fix->visual, &fix32->visual);
243588b3563SThomas Zimmermann err |= put_user(fix->xpanstep, &fix32->xpanstep);
244588b3563SThomas Zimmermann err |= put_user(fix->ypanstep, &fix32->ypanstep);
245588b3563SThomas Zimmermann err |= put_user(fix->ywrapstep, &fix32->ywrapstep);
246588b3563SThomas Zimmermann err |= put_user(fix->line_length, &fix32->line_length);
247588b3563SThomas Zimmermann
248588b3563SThomas Zimmermann data = (__u32) (unsigned long) fix->mmio_start;
249588b3563SThomas Zimmermann err |= put_user(data, &fix32->mmio_start);
250588b3563SThomas Zimmermann
251588b3563SThomas Zimmermann err |= put_user(fix->mmio_len, &fix32->mmio_len);
252588b3563SThomas Zimmermann err |= put_user(fix->accel, &fix32->accel);
253588b3563SThomas Zimmermann err |= copy_to_user(fix32->reserved, fix->reserved,
254588b3563SThomas Zimmermann sizeof(fix->reserved));
255588b3563SThomas Zimmermann
256588b3563SThomas Zimmermann if (err)
257588b3563SThomas Zimmermann return -EFAULT;
258588b3563SThomas Zimmermann return 0;
259588b3563SThomas Zimmermann }
260588b3563SThomas Zimmermann
fb_get_fscreeninfo(struct fb_info * info,unsigned int cmd,unsigned long arg)261588b3563SThomas Zimmermann static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
262588b3563SThomas Zimmermann unsigned long arg)
263588b3563SThomas Zimmermann {
264588b3563SThomas Zimmermann struct fb_fix_screeninfo fix;
265588b3563SThomas Zimmermann
266588b3563SThomas Zimmermann lock_fb_info(info);
267588b3563SThomas Zimmermann fix = info->fix;
268588b3563SThomas Zimmermann if (info->flags & FBINFO_HIDE_SMEM_START)
269588b3563SThomas Zimmermann fix.smem_start = 0;
270588b3563SThomas Zimmermann unlock_fb_info(info);
271588b3563SThomas Zimmermann return do_fscreeninfo_to_user(&fix, compat_ptr(arg));
272588b3563SThomas Zimmermann }
273588b3563SThomas Zimmermann
fb_compat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)274588b3563SThomas Zimmermann static long fb_compat_ioctl(struct file *file, unsigned int cmd,
275588b3563SThomas Zimmermann unsigned long arg)
276588b3563SThomas Zimmermann {
277588b3563SThomas Zimmermann struct fb_info *info = file_fb_info(file);
278588b3563SThomas Zimmermann const struct fb_ops *fb;
279588b3563SThomas Zimmermann long ret = -ENOIOCTLCMD;
280588b3563SThomas Zimmermann
281588b3563SThomas Zimmermann if (!info)
282588b3563SThomas Zimmermann return -ENODEV;
283588b3563SThomas Zimmermann fb = info->fbops;
284588b3563SThomas Zimmermann switch (cmd) {
285588b3563SThomas Zimmermann case FBIOGET_VSCREENINFO:
286588b3563SThomas Zimmermann case FBIOPUT_VSCREENINFO:
287588b3563SThomas Zimmermann case FBIOPAN_DISPLAY:
288588b3563SThomas Zimmermann case FBIOGET_CON2FBMAP:
289588b3563SThomas Zimmermann case FBIOPUT_CON2FBMAP:
290588b3563SThomas Zimmermann arg = (unsigned long) compat_ptr(arg);
291588b3563SThomas Zimmermann fallthrough;
292588b3563SThomas Zimmermann case FBIOBLANK:
293588b3563SThomas Zimmermann ret = do_fb_ioctl(info, cmd, arg);
294588b3563SThomas Zimmermann break;
295588b3563SThomas Zimmermann
296588b3563SThomas Zimmermann case FBIOGET_FSCREENINFO:
297588b3563SThomas Zimmermann ret = fb_get_fscreeninfo(info, cmd, arg);
298588b3563SThomas Zimmermann break;
299588b3563SThomas Zimmermann
300588b3563SThomas Zimmermann case FBIOGETCMAP:
301588b3563SThomas Zimmermann case FBIOPUTCMAP:
302588b3563SThomas Zimmermann ret = fb_getput_cmap(info, cmd, arg);
303588b3563SThomas Zimmermann break;
304588b3563SThomas Zimmermann
305588b3563SThomas Zimmermann default:
306588b3563SThomas Zimmermann if (fb->fb_compat_ioctl)
307588b3563SThomas Zimmermann ret = fb->fb_compat_ioctl(info, cmd, arg);
308588b3563SThomas Zimmermann break;
309588b3563SThomas Zimmermann }
310588b3563SThomas Zimmermann return ret;
311588b3563SThomas Zimmermann }
312588b3563SThomas Zimmermann #endif
313588b3563SThomas Zimmermann
fb_mmap(struct file * file,struct vm_area_struct * vma)314588b3563SThomas Zimmermann static int fb_mmap(struct file *file, struct vm_area_struct *vma)
315588b3563SThomas Zimmermann {
316588b3563SThomas Zimmermann struct fb_info *info = file_fb_info(file);
31733253d9eSThomas Zimmermann int res;
318588b3563SThomas Zimmermann
319588b3563SThomas Zimmermann if (!info)
320588b3563SThomas Zimmermann return -ENODEV;
32133253d9eSThomas Zimmermann
3228813e86fSThomas Zimmermann if (fb_WARN_ON_ONCE(info, !info->fbops->fb_mmap))
3238813e86fSThomas Zimmermann return -ENODEV;
3248813e86fSThomas Zimmermann
325588b3563SThomas Zimmermann mutex_lock(&info->mm_lock);
326588b3563SThomas Zimmermann res = info->fbops->fb_mmap(info, vma);
327588b3563SThomas Zimmermann mutex_unlock(&info->mm_lock);
328588b3563SThomas Zimmermann
32933253d9eSThomas Zimmermann return res;
330588b3563SThomas Zimmermann }
331588b3563SThomas Zimmermann
fb_open(struct inode * inode,struct file * file)332588b3563SThomas Zimmermann static int fb_open(struct inode *inode, struct file *file)
333588b3563SThomas Zimmermann __acquires(&info->lock)
334588b3563SThomas Zimmermann __releases(&info->lock)
335588b3563SThomas Zimmermann {
336588b3563SThomas Zimmermann int fbidx = iminor(inode);
337588b3563SThomas Zimmermann struct fb_info *info;
338588b3563SThomas Zimmermann int res = 0;
339588b3563SThomas Zimmermann
340588b3563SThomas Zimmermann info = get_fb_info(fbidx);
341588b3563SThomas Zimmermann if (!info) {
342588b3563SThomas Zimmermann request_module("fb%d", fbidx);
343588b3563SThomas Zimmermann info = get_fb_info(fbidx);
344588b3563SThomas Zimmermann if (!info)
345588b3563SThomas Zimmermann return -ENODEV;
346588b3563SThomas Zimmermann }
347588b3563SThomas Zimmermann if (IS_ERR(info))
348588b3563SThomas Zimmermann return PTR_ERR(info);
349588b3563SThomas Zimmermann
350588b3563SThomas Zimmermann lock_fb_info(info);
351588b3563SThomas Zimmermann if (!try_module_get(info->fbops->owner)) {
352588b3563SThomas Zimmermann res = -ENODEV;
353588b3563SThomas Zimmermann goto out;
354588b3563SThomas Zimmermann }
355588b3563SThomas Zimmermann file->private_data = info;
356588b3563SThomas Zimmermann if (info->fbops->fb_open) {
357588b3563SThomas Zimmermann res = info->fbops->fb_open(info, 1);
358588b3563SThomas Zimmermann if (res)
359588b3563SThomas Zimmermann module_put(info->fbops->owner);
360588b3563SThomas Zimmermann }
361588b3563SThomas Zimmermann #ifdef CONFIG_FB_DEFERRED_IO
362588b3563SThomas Zimmermann if (info->fbdefio)
363588b3563SThomas Zimmermann fb_deferred_io_open(info, inode, file);
364588b3563SThomas Zimmermann #endif
365588b3563SThomas Zimmermann out:
366588b3563SThomas Zimmermann unlock_fb_info(info);
367588b3563SThomas Zimmermann if (res)
368588b3563SThomas Zimmermann put_fb_info(info);
369588b3563SThomas Zimmermann return res;
370588b3563SThomas Zimmermann }
371588b3563SThomas Zimmermann
fb_release(struct inode * inode,struct file * file)372588b3563SThomas Zimmermann static int fb_release(struct inode *inode, struct file *file)
373588b3563SThomas Zimmermann __acquires(&info->lock)
374588b3563SThomas Zimmermann __releases(&info->lock)
375588b3563SThomas Zimmermann {
376588b3563SThomas Zimmermann struct fb_info * const info = file->private_data;
377588b3563SThomas Zimmermann
378588b3563SThomas Zimmermann lock_fb_info(info);
379588b3563SThomas Zimmermann #if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
380588b3563SThomas Zimmermann if (info->fbdefio)
381588b3563SThomas Zimmermann fb_deferred_io_release(info);
382588b3563SThomas Zimmermann #endif
383588b3563SThomas Zimmermann if (info->fbops->fb_release)
384588b3563SThomas Zimmermann info->fbops->fb_release(info, 1);
385588b3563SThomas Zimmermann module_put(info->fbops->owner);
386588b3563SThomas Zimmermann unlock_fb_info(info);
387588b3563SThomas Zimmermann put_fb_info(info);
388588b3563SThomas Zimmermann return 0;
389588b3563SThomas Zimmermann }
390588b3563SThomas Zimmermann
391588b3563SThomas Zimmermann #if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU)
get_fb_unmapped_area(struct file * filp,unsigned long addr,unsigned long len,unsigned long pgoff,unsigned long flags)392588b3563SThomas Zimmermann static unsigned long get_fb_unmapped_area(struct file *filp,
393588b3563SThomas Zimmermann unsigned long addr, unsigned long len,
394588b3563SThomas Zimmermann unsigned long pgoff, unsigned long flags)
395588b3563SThomas Zimmermann {
396588b3563SThomas Zimmermann struct fb_info * const info = filp->private_data;
397588b3563SThomas Zimmermann unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len);
398588b3563SThomas Zimmermann
399588b3563SThomas Zimmermann if (pgoff > fb_size || len > fb_size - pgoff)
400588b3563SThomas Zimmermann return -EINVAL;
401588b3563SThomas Zimmermann
402588b3563SThomas Zimmermann return (unsigned long)info->screen_base + pgoff;
403588b3563SThomas Zimmermann }
404588b3563SThomas Zimmermann #endif
405588b3563SThomas Zimmermann
406588b3563SThomas Zimmermann static const struct file_operations fb_fops = {
407588b3563SThomas Zimmermann .owner = THIS_MODULE,
408588b3563SThomas Zimmermann .read = fb_read,
409588b3563SThomas Zimmermann .write = fb_write,
410588b3563SThomas Zimmermann .unlocked_ioctl = fb_ioctl,
411588b3563SThomas Zimmermann #ifdef CONFIG_COMPAT
412588b3563SThomas Zimmermann .compat_ioctl = fb_compat_ioctl,
413588b3563SThomas Zimmermann #endif
414588b3563SThomas Zimmermann .mmap = fb_mmap,
415588b3563SThomas Zimmermann .open = fb_open,
416588b3563SThomas Zimmermann .release = fb_release,
417588b3563SThomas Zimmermann #if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \
418588b3563SThomas Zimmermann (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \
419588b3563SThomas Zimmermann !defined(CONFIG_MMU))
420588b3563SThomas Zimmermann .get_unmapped_area = get_fb_unmapped_area,
421588b3563SThomas Zimmermann #endif
422588b3563SThomas Zimmermann #ifdef CONFIG_FB_DEFERRED_IO
423588b3563SThomas Zimmermann .fsync = fb_deferred_io_fsync,
424588b3563SThomas Zimmermann #endif
425588b3563SThomas Zimmermann .llseek = default_llseek,
426588b3563SThomas Zimmermann };
427588b3563SThomas Zimmermann
fb_register_chrdev(void)428588b3563SThomas Zimmermann int fb_register_chrdev(void)
429588b3563SThomas Zimmermann {
430588b3563SThomas Zimmermann int ret;
431588b3563SThomas Zimmermann
432588b3563SThomas Zimmermann ret = register_chrdev(FB_MAJOR, "fb", &fb_fops);
433588b3563SThomas Zimmermann if (ret) {
434588b3563SThomas Zimmermann pr_err("Unable to get major %d for fb devs\n", FB_MAJOR);
435588b3563SThomas Zimmermann return ret;
436588b3563SThomas Zimmermann }
437588b3563SThomas Zimmermann
438588b3563SThomas Zimmermann return ret;
439588b3563SThomas Zimmermann }
440588b3563SThomas Zimmermann
fb_unregister_chrdev(void)441588b3563SThomas Zimmermann void fb_unregister_chrdev(void)
442588b3563SThomas Zimmermann {
443588b3563SThomas Zimmermann unregister_chrdev(FB_MAJOR, "fb");
444588b3563SThomas Zimmermann }
445