xref: /netbsd-src/sys/external/bsd/drm2/drm/drm_cdevsw.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: drm_cdevsw.c,v 1.15 2020/12/19 22:09:15 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 2013 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Taylor R. Campbell.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: drm_cdevsw.c,v 1.15 2020/12/19 22:09:15 thorpej Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/conf.h>
38 #include <sys/device.h>
39 #include <sys/file.h>
40 #include <sys/filedesc.h>
41 #include <sys/ioccom.h>
42 #include <sys/kauth.h>
43 #ifndef _MODULE
44 /* XXX Mega-kludge because modules are broken.  */
45 #include <sys/once.h>
46 #endif
47 #include <sys/pmf.h>
48 #include <sys/poll.h>
49 #ifndef _MODULE
50 #include <sys/reboot.h>		/* XXX drm_init kludge */
51 #endif
52 #include <sys/select.h>
53 
54 #include <uvm/uvm_extern.h>
55 
56 #include <linux/err.h>
57 
58 #include <linux/pm.h>
59 
60 #include <drm/drmP.h>
61 #include <drm/drm_internal.h>
62 #include "../dist/drm/drm_legacy.h"
63 
64 static dev_type_open(drm_open);
65 
66 static int	drm_firstopen(struct drm_device *);
67 
68 static int	drm_close(struct file *);
69 static int	drm_read(struct file *, off_t *, struct uio *, kauth_cred_t,
70 		    int);
71 static int	drm_dequeue_event(struct drm_file *, size_t,
72 		    struct drm_pending_event **, int);
73 static int	drm_ioctl_shim(struct file *, unsigned long, void *);
74 static int	drm_poll(struct file *, int);
75 static int	drm_kqfilter(struct file *, struct knote *);
76 static int	drm_stat(struct file *, struct stat *);
77 static int	drm_fop_mmap(struct file *, off_t *, size_t, int, int *, int *,
78 			     struct uvm_object **, int *);
79 static paddr_t	drm_legacy_mmap(dev_t, off_t, int);
80 
81 const struct cdevsw drm_cdevsw = {
82 	.d_open = drm_open,
83 	.d_close = noclose,
84 	.d_read = noread,
85 	.d_write = nowrite,
86 	.d_ioctl = noioctl,
87 	.d_stop = nostop,
88 	.d_tty = notty,
89 	.d_poll = nopoll,
90 	.d_mmap = drm_legacy_mmap,
91 	.d_kqfilter = nokqfilter,
92 	.d_discard = nodiscard,
93 	/* XXX was D_TTY | D_NEGOFFSAFE */
94 	/* XXX Add D_MPSAFE some day... */
95 	.d_flag = D_NEGOFFSAFE,
96 };
97 
98 static const struct fileops drm_fileops = {
99 	.fo_name = "drm",
100 	.fo_read = drm_read,
101 	.fo_write = fbadop_write,
102 	.fo_ioctl = drm_ioctl_shim,
103 	.fo_fcntl = fnullop_fcntl,
104 	.fo_poll = drm_poll,
105 	.fo_stat = drm_stat,
106 	.fo_close = drm_close,
107 	.fo_kqfilter = drm_kqfilter,
108 	.fo_restart = fnullop_restart,
109 	.fo_mmap = drm_fop_mmap,
110 };
111 
112 static int
113 drm_open(dev_t d, int flags, int fmt, struct lwp *l)
114 {
115 	struct drm_minor *dminor;
116 	struct drm_device *dev;
117 	bool firstopen, lastclose;
118 	int fd;
119 	struct file *fp;
120 	int error;
121 
122 	error = drm_guarantee_initialized();
123 	if (error)
124 		goto fail0;
125 
126 	if (flags & O_EXCL) {
127 		error = EBUSY;
128 		goto fail0;
129 	}
130 
131 	dminor = drm_minor_acquire(minor(d));
132 	if (IS_ERR(dminor)) {
133 		/* XXX errno Linux->NetBSD */
134 		error = -PTR_ERR(dminor);
135 		goto fail0;
136 	}
137 	dev = dminor->dev;
138 	if (dev->switch_power_state != DRM_SWITCH_POWER_ON) {
139 		error = EINVAL;
140 		goto fail1;
141 	}
142 
143 	mutex_lock(&drm_global_mutex);
144 	if (dev->open_count == INT_MAX) {
145 		mutex_unlock(&drm_global_mutex);
146 		error = EBUSY;
147 		goto fail1;
148 	}
149 	firstopen = (dev->open_count == 0);
150 	dev->open_count++;
151 	mutex_unlock(&drm_global_mutex);
152 
153 	if (firstopen) {
154 		/* XXX errno Linux->NetBSD */
155 		error = -drm_firstopen(dev);
156 		if (error)
157 			goto fail2;
158 	}
159 
160 	error = fd_allocfile(&fp, &fd);
161 	if (error)
162 		goto fail2;
163 
164 	struct drm_file *const file = kmem_zalloc(sizeof(*file), KM_SLEEP);
165 	/* XXX errno Linux->NetBSD */
166 	error = -drm_open_file(file, fp, dminor);
167 	if (error)
168 		goto fail3;
169 
170 	error = fd_clone(fp, fd, flags, &drm_fileops, file);
171 	KASSERT(error == EMOVEFD); /* XXX */
172 
173 	/* Success!  (But error has to be EMOVEFD, not 0.)  */
174 	return error;
175 
176 fail3:	kmem_free(file, sizeof(*file));
177 	fd_abort(curproc, fp, fd);
178 fail2:	mutex_lock(&drm_global_mutex);
179 	KASSERT(0 < dev->open_count);
180 	--dev->open_count;
181 	lastclose = (dev->open_count == 0);
182 	mutex_unlock(&drm_global_mutex);
183 	if (lastclose)
184 		(void)drm_lastclose(dev);
185 fail1:	drm_minor_release(dminor);
186 fail0:	KASSERT(error);
187 	if (error == ERESTARTSYS)
188 		error = ERESTART;
189 	return error;
190 }
191 
192 static int
193 drm_close(struct file *fp)
194 {
195 	struct drm_file *const file = fp->f_data;
196 	struct drm_minor *const dminor = file->minor;
197 	struct drm_device *const dev = dminor->dev;
198 	bool lastclose;
199 
200 	drm_close_file(file);
201 	kmem_free(file, sizeof(*file));
202 
203 	mutex_lock(&drm_global_mutex);
204 	KASSERT(0 < dev->open_count);
205 	--dev->open_count;
206 	lastclose = (dev->open_count == 0);
207 	mutex_unlock(&drm_global_mutex);
208 
209 	if (lastclose)
210 		(void)drm_lastclose(dev);
211 
212 	drm_minor_release(dminor);
213 
214 	return 0;
215 }
216 
217 static int
218 drm_firstopen(struct drm_device *dev)
219 {
220 	int ret;
221 
222 	if (drm_core_check_feature(dev, DRIVER_MODESET))
223 		return 0;
224 
225 	if (dev->driver->firstopen) {
226 		ret = (*dev->driver->firstopen)(dev);
227 		if (ret)
228 			goto fail0;
229 	}
230 
231 	ret = drm_legacy_dma_setup(dev);
232 	if (ret)
233 		goto fail1;
234 
235 	return 0;
236 
237 fail2: __unused
238 	drm_legacy_dma_takedown(dev);
239 fail1:	if (dev->driver->lastclose)
240 		(*dev->driver->lastclose)(dev);
241 fail0:	KASSERT(ret);
242 	return ret;
243 }
244 
245 int
246 drm_lastclose(struct drm_device *dev)
247 {
248 
249 	/* XXX Order is sketchy here...  */
250 	if (dev->driver->lastclose)
251 		(*dev->driver->lastclose)(dev);
252 	if (dev->irq_enabled && !drm_core_check_feature(dev, DRIVER_MODESET))
253 		drm_irq_uninstall(dev);
254 
255 	mutex_lock(&dev->struct_mutex);
256 	if (dev->agp)
257 		drm_agp_clear(dev);
258 	drm_legacy_sg_cleanup(dev);
259 	drm_legacy_dma_takedown(dev);
260 	mutex_unlock(&dev->struct_mutex);
261 
262 	/* XXX Synchronize with drm_legacy_dev_reinit.  */
263 	if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
264 		dev->sigdata.lock = NULL;
265 		dev->context_flag = 0;
266 		dev->last_context = 0;
267 		dev->if_version = 0;
268 	}
269 
270 	return 0;
271 }
272 
273 static int
274 drm_read(struct file *fp, off_t *off, struct uio *uio, kauth_cred_t cred,
275     int flags)
276 {
277 	struct drm_file *const file = fp->f_data;
278 	struct drm_pending_event *event;
279 	bool first;
280 	int error = 0;
281 
282 	for (first = true; ; first = false) {
283 		int f = 0;
284 
285 		if (!first || ISSET(fp->f_flag, FNONBLOCK))
286 			f |= FNONBLOCK;
287 
288 		/* XXX errno Linux->NetBSD */
289 		error = -drm_dequeue_event(file, uio->uio_resid, &event, f);
290 		if (error) {
291 			if ((error == EWOULDBLOCK) && !first)
292 				error = 0;
293 			break;
294 		}
295 		if (event == NULL)
296 			break;
297 		error = uiomove(event->event, event->event->length, uio);
298 		if (error)	/* XXX Requeue the event?  */
299 			break;
300 		(*event->destroy)(event);
301 	}
302 
303 	/* Success!  */
304 	if (error == ERESTARTSYS)
305 		error = ERESTART;
306 	return error;
307 }
308 
309 static int
310 drm_dequeue_event(struct drm_file *file, size_t max_length,
311     struct drm_pending_event **eventp, int flags)
312 {
313 	struct drm_device *const dev = file->minor->dev;
314 	struct drm_pending_event *event = NULL;
315 	unsigned long irqflags;
316 	int ret = 0;
317 
318 	spin_lock_irqsave(&dev->event_lock, irqflags);
319 
320 	if (ISSET(flags, FNONBLOCK)) {
321 		if (list_empty(&file->event_list))
322 			ret = -EWOULDBLOCK;
323 	} else {
324 		DRM_SPIN_WAIT_UNTIL(ret, &file->event_wait, &dev->event_lock,
325 		    !list_empty(&file->event_list));
326 	}
327 	if (ret)
328 		goto out;
329 
330 	event = list_first_entry(&file->event_list, struct drm_pending_event,
331 	    link);
332 	if (event->event->length > max_length) {
333 		/* Event is too large, can't return it.  */
334 		event = NULL;
335 		ret = 0;
336 		goto out;
337 	}
338 
339 	file->event_space += event->event->length;
340 	list_del(&event->link);
341 
342 out:	spin_unlock_irqrestore(&dev->event_lock, irqflags);
343 	*eventp = event;
344 	return ret;
345 }
346 
347 static int
348 drm_ioctl_shim(struct file *fp, unsigned long cmd, void *data)
349 {
350 	struct drm_file *file = fp->f_data;
351 	struct drm_driver *driver = file->minor->dev->driver;
352 	int error;
353 
354 	if (driver->ioctl_override)
355 		error = driver->ioctl_override(fp, cmd, data);
356 	else
357 		error = drm_ioctl(fp, cmd, data);
358 	if (error == ERESTARTSYS)
359 		error = ERESTART;
360 
361 	return error;
362 }
363 
364 static int
365 drm_poll(struct file *fp __unused, int events __unused)
366 {
367 	struct drm_file *const file = fp->f_data;
368 	struct drm_device *const dev = file->minor->dev;
369 	int revents = 0;
370 	unsigned long irqflags;
371 
372 	if (!ISSET(events, (POLLIN | POLLRDNORM)))
373 		return 0;
374 
375 	spin_lock_irqsave(&dev->event_lock, irqflags);
376 	if (list_empty(&file->event_list))
377 		selrecord(curlwp, &file->event_selq);
378 	else
379 		revents |= (events & (POLLIN | POLLRDNORM));
380 	spin_unlock_irqrestore(&dev->event_lock, irqflags);
381 
382 	return revents;
383 }
384 
385 static void	filt_drm_detach(struct knote *);
386 static int	filt_drm_event(struct knote *, long);
387 
388 static const struct filterops drm_filtops = {
389 	.f_isfd = 1,
390 	.f_attach = NULL,
391 	.f_detach = filt_drm_detach,
392 	.f_event = filt_drm_event,
393 };
394 
395 static int
396 drm_kqfilter(struct file *fp, struct knote *kn)
397 {
398 	struct drm_file *const file = fp->f_data;
399 	struct drm_device *const dev = file->minor->dev;
400 	unsigned long irqflags;
401 
402 	switch (kn->kn_filter) {
403 	case EVFILT_READ:
404 		kn->kn_fop = &drm_filtops;
405 		kn->kn_hook = file;
406 		spin_lock_irqsave(&dev->event_lock, irqflags);
407 		selrecord_knote(&file->event_selq, kn);
408 		spin_unlock_irqrestore(&dev->event_lock, irqflags);
409 		return 0;
410 	case EVFILT_WRITE:
411 	default:
412 		return EINVAL;
413 	}
414 }
415 
416 static void
417 filt_drm_detach(struct knote *kn)
418 {
419 	struct drm_file *const file = kn->kn_hook;
420 	struct drm_device *const dev = file->minor->dev;
421 	unsigned long irqflags;
422 
423 	spin_lock_irqsave(&dev->event_lock, irqflags);
424 	selremove_knote(&file->event_selq, kn);
425 	spin_unlock_irqrestore(&dev->event_lock, irqflags);
426 }
427 
428 static int
429 filt_drm_event(struct knote *kn, long hint)
430 {
431 	struct drm_file *const file = kn->kn_hook;
432 	struct drm_device *const dev = file->minor->dev;
433 	unsigned long irqflags;
434 	int ret;
435 
436 	if (hint == NOTE_SUBMIT)
437 		KASSERT(spin_is_locked(&dev->event_lock));
438 	else
439 		spin_lock_irqsave(&dev->event_lock, irqflags);
440 	if (list_empty(&file->event_list)) {
441 		ret = 0;
442 	} else {
443 		struct drm_pending_event *const event =
444 		    list_first_entry(&file->event_list,
445 			struct drm_pending_event, link);
446 		kn->kn_data = event->event->length;
447 		ret = 1;
448 	}
449 	if (hint == NOTE_SUBMIT)
450 		KASSERT(spin_is_locked(&dev->event_lock));
451 	else
452 		spin_unlock_irqrestore(&dev->event_lock, irqflags);
453 
454 	return ret;
455 }
456 
457 static int
458 drm_stat(struct file *fp, struct stat *st)
459 {
460 	struct drm_file *const file = fp->f_data;
461 	struct drm_minor *const dminor = file->minor;
462 	const dev_t devno = makedev(cdevsw_lookup_major(&drm_cdevsw),
463 	    64*dminor->type + dminor->index);
464 
465 	(void)memset(st, 0, sizeof(*st));
466 
467 	st->st_dev = devno;
468 	st->st_ino = 0;		/* XXX (dev,ino) uniqueness bleh */
469 	st->st_uid = kauth_cred_geteuid(fp->f_cred);
470 	st->st_gid = kauth_cred_getegid(fp->f_cred);
471 	st->st_mode = S_IFCHR;	/* XXX what? */
472 	st->st_rdev = devno;
473 	/* XXX what else? */
474 
475 	return 0;
476 }
477 
478 static int
479 drm_fop_mmap(struct file *fp, off_t *offp, size_t len, int prot, int *flagsp,
480 	     int *advicep, struct uvm_object **uobjp, int *maxprotp)
481 {
482 	struct drm_file *const file = fp->f_data;
483 	struct drm_device *const dev = file->minor->dev;
484 	int error;
485 
486 	KASSERT(fp == file->filp);
487 	/* XXX errno Linux->NetBSD */
488 	error = -(*dev->driver->mmap_object)(dev, *offp, len, prot, uobjp,
489 	    offp, file->filp);
490 	*maxprotp = prot;
491 	*advicep = UVM_ADV_RANDOM;
492 	if (error == ERESTARTSYS)
493 		error = ERESTART;
494 	return error;
495 }
496 
497 static paddr_t
498 drm_legacy_mmap(dev_t d, off_t offset, int prot)
499 {
500 	struct drm_minor *dminor;
501 	paddr_t paddr;
502 
503 	dminor = drm_minor_acquire(minor(d));
504 	if (IS_ERR(dminor))
505 		return (paddr_t)-1;
506 
507 	paddr = drm_legacy_mmap_paddr(dminor->dev, offset, prot);
508 
509 	drm_minor_release(dminor);
510 	return paddr;
511 }
512