xref: /linux/drivers/video/fbdev/core/fb_chrdev.c (revision a23e1966932464e1c5226cb9ac4ce1d5fc10ba22)
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