drm_cdevsw.c revision 1.7 1 /* $NetBSD: drm_cdevsw.c,v 1.7 2018/08/27 06:52:34 riastradh 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.7 2018/08/27 06:52:34 riastradh 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_poll(struct file *, int);
74 static int drm_kqfilter(struct file *, struct knote *);
75 static int drm_stat(struct file *, struct stat *);
76 static int drm_fop_mmap(struct file *, off_t *, size_t, int, int *, int *,
77 struct uvm_object **, int *);
78 static paddr_t drm_mmap(dev_t, off_t, int);
79
80 const struct cdevsw drm_cdevsw = {
81 .d_open = drm_open,
82 .d_close = noclose,
83 .d_read = noread,
84 .d_write = nowrite,
85 .d_ioctl = noioctl,
86 .d_stop = nostop,
87 .d_tty = notty,
88 .d_poll = nopoll,
89 .d_mmap = drm_mmap,
90 .d_kqfilter = nokqfilter,
91 .d_discard = nodiscard,
92 /* XXX was D_TTY | D_NEGOFFSAFE */
93 /* XXX Add D_MPSAFE some day... */
94 .d_flag = D_NEGOFFSAFE,
95 };
96
97 static const struct fileops drm_fileops = {
98 .fo_name = "drm",
99 .fo_read = drm_read,
100 .fo_write = fbadop_write,
101 .fo_ioctl = drm_ioctl,
102 .fo_fcntl = fnullop_fcntl,
103 .fo_poll = drm_poll,
104 .fo_stat = drm_stat,
105 .fo_close = drm_close,
106 .fo_kqfilter = drm_kqfilter,
107 .fo_restart = fnullop_restart,
108 .fo_mmap = drm_fop_mmap,
109 };
110
111 static int
112 drm_open(dev_t d, int flags, int fmt, struct lwp *l)
113 {
114 struct drm_minor *dminor;
115 struct drm_device *dev;
116 bool firstopen, lastclose;
117 int fd;
118 struct file *fp;
119 int error;
120 extern int drm_guarantee_initialized(void);
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_lock(&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 return error;
188 }
189
190 static int
191 drm_close(struct file *fp)
192 {
193 struct drm_file *const file = fp->f_data;
194 struct drm_minor *const dminor = file->minor;
195 struct drm_device *const dev = dminor->dev;
196 bool lastclose;
197
198 drm_close_file(file);
199 kmem_free(file, sizeof(*file));
200
201 mutex_lock(&drm_global_mutex);
202 KASSERT(0 < dev->open_count);
203 --dev->open_count;
204 lastclose = (dev->open_count == 0);
205 mutex_unlock(&drm_global_mutex);
206
207 if (lastclose)
208 (void)drm_lastclose(dev);
209
210 drm_minor_release(dminor);
211
212 return 0;
213 }
214
215 static int
216 drm_firstopen(struct drm_device *dev)
217 {
218 int ret;
219
220 if (drm_core_check_feature(dev, DRIVER_MODESET))
221 return 0;
222
223 if (dev->driver->firstopen) {
224 ret = (*dev->driver->firstopen)(dev);
225 if (ret)
226 goto fail0;
227 }
228
229 ret = drm_legacy_dma_setup(dev);
230 if (ret)
231 goto fail1;
232
233 return 0;
234
235 fail2: __unused
236 drm_legacy_dma_takedown(dev);
237 fail1: if (dev->driver->lastclose)
238 (*dev->driver->lastclose)(dev);
239 fail0: KASSERT(ret);
240 return ret;
241 }
242
243 int
244 drm_lastclose(struct drm_device *dev)
245 {
246
247 /* XXX Order is sketchy here... */
248 if (dev->driver->lastclose)
249 (*dev->driver->lastclose)(dev);
250 if (dev->irq_enabled && !drm_core_check_feature(dev, DRIVER_MODESET))
251 drm_irq_uninstall(dev);
252
253 mutex_lock(&dev->struct_mutex);
254 if (dev->agp)
255 drm_agp_clear_hook(dev);
256 drm_legacy_sg_cleanup(dev);
257 drm_legacy_vma_flush(dev);
258 drm_legacy_dma_takedown(dev);
259 mutex_unlock(&dev->struct_mutex);
260
261 /* XXX Synchronize with drm_legacy_dev_reinit. */
262 if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
263 dev->sigdata.lock = NULL;
264 dev->context_flag = 0;
265 dev->last_context = 0;
266 dev->if_version = 0;
267 }
268
269 return 0;
270 }
271
272 static int
273 drm_read(struct file *fp, off_t *off, struct uio *uio, kauth_cred_t cred,
274 int flags)
275 {
276 struct drm_file *const file = fp->f_data;
277 struct drm_pending_event *event;
278 bool first;
279 int error = 0;
280
281 for (first = true; ; first = false) {
282 int f = 0;
283
284 if (!first || ISSET(fp->f_flag, FNONBLOCK))
285 f |= FNONBLOCK;
286
287 /* XXX errno Linux->NetBSD */
288 error = -drm_dequeue_event(file, uio->uio_resid, &event, f);
289 if (error) {
290 if ((error == EWOULDBLOCK) && !first)
291 error = 0;
292 break;
293 }
294 if (event == NULL)
295 break;
296 error = uiomove(event->event, event->event->length, uio);
297 if (error) /* XXX Requeue the event? */
298 break;
299 (*event->destroy)(event);
300 }
301
302 /* Success! */
303 return error;
304 }
305
306 static int
307 drm_dequeue_event(struct drm_file *file, size_t max_length,
308 struct drm_pending_event **eventp, int flags)
309 {
310 struct drm_device *const dev = file->minor->dev;
311 struct drm_pending_event *event = NULL;
312 unsigned long irqflags;
313 int ret = 0;
314
315 spin_lock_irqsave(&dev->event_lock, irqflags);
316
317 if (ISSET(flags, FNONBLOCK)) {
318 if (list_empty(&file->event_list))
319 ret = -EWOULDBLOCK;
320 } else {
321 DRM_SPIN_WAIT_UNTIL(ret, &file->event_wait, &dev->event_lock,
322 !list_empty(&file->event_list));
323 }
324 if (ret)
325 goto out;
326
327 event = list_first_entry(&file->event_list, struct drm_pending_event,
328 link);
329 if (event->event->length > max_length) {
330 /* Event is too large, can't return it. */
331 event = NULL;
332 ret = 0;
333 goto out;
334 }
335
336 file->event_space += event->event->length;
337 list_del(&event->link);
338
339 out: spin_unlock_irqrestore(&dev->event_lock, irqflags);
340 *eventp = event;
341 return ret;
342 }
343
344 static int
345 drm_poll(struct file *fp __unused, int events __unused)
346 {
347 struct drm_file *const file = fp->f_data;
348 struct drm_device *const dev = file->minor->dev;
349 int revents = 0;
350 unsigned long irqflags;
351
352 if (!ISSET(events, (POLLIN | POLLRDNORM)))
353 return 0;
354
355 spin_lock_irqsave(&dev->event_lock, irqflags);
356 if (list_empty(&file->event_list))
357 selrecord(curlwp, &file->event_selq);
358 else
359 revents |= (events & (POLLIN | POLLRDNORM));
360 spin_unlock_irqrestore(&dev->event_lock, irqflags);
361
362 return revents;
363 }
364
365 static void filt_drm_detach(struct knote *);
366 static int filt_drm_event(struct knote *, long);
367
368 static const struct filterops drm_filtops = {
369 .f_isfd = 1,
370 .f_attach = NULL,
371 .f_detach = filt_drm_detach,
372 .f_event = filt_drm_event,
373 };
374
375 static int
376 drm_kqfilter(struct file *fp, struct knote *kn)
377 {
378 struct drm_file *const file = fp->f_data;
379 struct drm_device *const dev = file->minor->dev;
380 unsigned long irqflags;
381
382 switch (kn->kn_filter) {
383 case EVFILT_READ:
384 kn->kn_fop = &drm_filtops;
385 kn->kn_hook = file;
386 spin_lock_irqsave(&dev->event_lock, irqflags);
387 SLIST_INSERT_HEAD(&file->event_selq.sel_klist, kn, kn_selnext);
388 spin_unlock_irqrestore(&dev->event_lock, irqflags);
389 return 0;
390 case EVFILT_WRITE:
391 default:
392 return EINVAL;
393 }
394 }
395
396 static void
397 filt_drm_detach(struct knote *kn)
398 {
399 struct drm_file *const file = kn->kn_hook;
400 struct drm_device *const dev = file->minor->dev;
401 unsigned long irqflags;
402
403 spin_lock_irqsave(&dev->event_lock, irqflags);
404 SLIST_REMOVE(&file->event_selq.sel_klist, kn, knote, kn_selnext);
405 spin_unlock_irqrestore(&dev->event_lock, irqflags);
406 }
407
408 static int
409 filt_drm_event(struct knote *kn, long hint)
410 {
411 struct drm_file *const file = kn->kn_hook;
412 struct drm_device *const dev = file->minor->dev;
413 unsigned long irqflags;
414 int ret;
415
416 if (hint == NOTE_SUBMIT)
417 KASSERT(spin_is_locked(&dev->event_lock));
418 else
419 spin_lock_irqsave(&dev->event_lock, irqflags);
420 if (list_empty(&file->event_list)) {
421 ret = 0;
422 } else {
423 struct drm_pending_event *const event =
424 list_first_entry(&file->event_list,
425 struct drm_pending_event, link);
426 kn->kn_data = event->event->length;
427 ret = 1;
428 }
429 if (hint == NOTE_SUBMIT)
430 KASSERT(spin_is_locked(&dev->event_lock));
431 else
432 spin_unlock_irqrestore(&dev->event_lock, irqflags);
433
434 return ret;
435 }
436
437 static int
438 drm_stat(struct file *fp, struct stat *st)
439 {
440 struct drm_file *const file = fp->f_data;
441 struct drm_minor *const dminor = file->minor;
442 const dev_t devno = makedev(cdevsw_lookup_major(&drm_cdevsw),
443 64*dminor->type + dminor->index);
444
445 (void)memset(st, 0, sizeof(*st));
446
447 st->st_dev = devno;
448 st->st_ino = 0; /* XXX (dev,ino) uniqueness bleh */
449 st->st_uid = kauth_cred_geteuid(fp->f_cred);
450 st->st_gid = kauth_cred_getegid(fp->f_cred);
451 st->st_mode = S_IFCHR; /* XXX what? */
452 st->st_rdev = devno;
453 /* XXX what else? */
454
455 return 0;
456 }
457
458 static int
459 drm_fop_mmap(struct file *fp, off_t *offp, size_t len, int prot, int *flagsp,
460 int *advicep, struct uvm_object **uobjp, int *maxprotp)
461 {
462 struct drm_file *const file = fp->f_data;
463 struct drm_device *const dev = file->minor->dev;
464 int error;
465
466 KASSERT(fp == file->filp);
467 error = (*dev->driver->mmap_object)(dev, *offp, len, prot, uobjp,
468 offp, file->filp);
469 *maxprotp = prot;
470 *advicep = UVM_ADV_RANDOM;
471 return -error;
472 }
473
474 static paddr_t
475 drm_mmap(dev_t d, off_t offset, int prot)
476 {
477 struct drm_minor *dminor;
478 paddr_t paddr;
479
480 dminor = drm_minor_acquire(minor(d));
481 if (IS_ERR(dminor))
482 return (paddr_t)-1;
483
484 paddr = drm_mmap_paddr(dminor->dev, offset, prot);
485
486 drm_minor_release(dminor);
487 return paddr;
488 }
489