drm_cdevsw.c revision 1.10 1 /* $NetBSD: drm_cdevsw.c,v 1.10 2018/08/27 14:11:04 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.10 2018/08/27 14:11:04 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_legacy_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_legacy_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_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 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_dma_takedown(dev);
258 mutex_unlock(&dev->struct_mutex);
259
260 /* XXX Synchronize with drm_legacy_dev_reinit. */
261 if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
262 dev->sigdata.lock = NULL;
263 dev->context_flag = 0;
264 dev->last_context = 0;
265 dev->if_version = 0;
266 }
267
268 return 0;
269 }
270
271 static int
272 drm_read(struct file *fp, off_t *off, struct uio *uio, kauth_cred_t cred,
273 int flags)
274 {
275 struct drm_file *const file = fp->f_data;
276 struct drm_pending_event *event;
277 bool first;
278 int error = 0;
279
280 for (first = true; ; first = false) {
281 int f = 0;
282
283 if (!first || ISSET(fp->f_flag, FNONBLOCK))
284 f |= FNONBLOCK;
285
286 /* XXX errno Linux->NetBSD */
287 error = -drm_dequeue_event(file, uio->uio_resid, &event, f);
288 if (error) {
289 if ((error == EWOULDBLOCK) && !first)
290 error = 0;
291 break;
292 }
293 if (event == NULL)
294 break;
295 error = uiomove(event->event, event->event->length, uio);
296 if (error) /* XXX Requeue the event? */
297 break;
298 (*event->destroy)(event);
299 }
300
301 /* Success! */
302 return error;
303 }
304
305 static int
306 drm_dequeue_event(struct drm_file *file, size_t max_length,
307 struct drm_pending_event **eventp, int flags)
308 {
309 struct drm_device *const dev = file->minor->dev;
310 struct drm_pending_event *event = NULL;
311 unsigned long irqflags;
312 int ret = 0;
313
314 spin_lock_irqsave(&dev->event_lock, irqflags);
315
316 if (ISSET(flags, FNONBLOCK)) {
317 if (list_empty(&file->event_list))
318 ret = -EWOULDBLOCK;
319 } else {
320 DRM_SPIN_WAIT_UNTIL(ret, &file->event_wait, &dev->event_lock,
321 !list_empty(&file->event_list));
322 }
323 if (ret)
324 goto out;
325
326 event = list_first_entry(&file->event_list, struct drm_pending_event,
327 link);
328 if (event->event->length > max_length) {
329 /* Event is too large, can't return it. */
330 event = NULL;
331 ret = 0;
332 goto out;
333 }
334
335 file->event_space += event->event->length;
336 list_del(&event->link);
337
338 out: spin_unlock_irqrestore(&dev->event_lock, irqflags);
339 *eventp = event;
340 return ret;
341 }
342
343 static int
344 drm_poll(struct file *fp __unused, int events __unused)
345 {
346 struct drm_file *const file = fp->f_data;
347 struct drm_device *const dev = file->minor->dev;
348 int revents = 0;
349 unsigned long irqflags;
350
351 if (!ISSET(events, (POLLIN | POLLRDNORM)))
352 return 0;
353
354 spin_lock_irqsave(&dev->event_lock, irqflags);
355 if (list_empty(&file->event_list))
356 selrecord(curlwp, &file->event_selq);
357 else
358 revents |= (events & (POLLIN | POLLRDNORM));
359 spin_unlock_irqrestore(&dev->event_lock, irqflags);
360
361 return revents;
362 }
363
364 static void filt_drm_detach(struct knote *);
365 static int filt_drm_event(struct knote *, long);
366
367 static const struct filterops drm_filtops = {
368 .f_isfd = 1,
369 .f_attach = NULL,
370 .f_detach = filt_drm_detach,
371 .f_event = filt_drm_event,
372 };
373
374 static int
375 drm_kqfilter(struct file *fp, struct knote *kn)
376 {
377 struct drm_file *const file = fp->f_data;
378 struct drm_device *const dev = file->minor->dev;
379 unsigned long irqflags;
380
381 switch (kn->kn_filter) {
382 case EVFILT_READ:
383 kn->kn_fop = &drm_filtops;
384 kn->kn_hook = file;
385 spin_lock_irqsave(&dev->event_lock, irqflags);
386 SLIST_INSERT_HEAD(&file->event_selq.sel_klist, kn, kn_selnext);
387 spin_unlock_irqrestore(&dev->event_lock, irqflags);
388 return 0;
389 case EVFILT_WRITE:
390 default:
391 return EINVAL;
392 }
393 }
394
395 static void
396 filt_drm_detach(struct knote *kn)
397 {
398 struct drm_file *const file = kn->kn_hook;
399 struct drm_device *const dev = file->minor->dev;
400 unsigned long irqflags;
401
402 spin_lock_irqsave(&dev->event_lock, irqflags);
403 SLIST_REMOVE(&file->event_selq.sel_klist, kn, knote, kn_selnext);
404 spin_unlock_irqrestore(&dev->event_lock, irqflags);
405 }
406
407 static int
408 filt_drm_event(struct knote *kn, long hint)
409 {
410 struct drm_file *const file = kn->kn_hook;
411 struct drm_device *const dev = file->minor->dev;
412 unsigned long irqflags;
413 int ret;
414
415 if (hint == NOTE_SUBMIT)
416 KASSERT(spin_is_locked(&dev->event_lock));
417 else
418 spin_lock_irqsave(&dev->event_lock, irqflags);
419 if (list_empty(&file->event_list)) {
420 ret = 0;
421 } else {
422 struct drm_pending_event *const event =
423 list_first_entry(&file->event_list,
424 struct drm_pending_event, link);
425 kn->kn_data = event->event->length;
426 ret = 1;
427 }
428 if (hint == NOTE_SUBMIT)
429 KASSERT(spin_is_locked(&dev->event_lock));
430 else
431 spin_unlock_irqrestore(&dev->event_lock, irqflags);
432
433 return ret;
434 }
435
436 static int
437 drm_stat(struct file *fp, struct stat *st)
438 {
439 struct drm_file *const file = fp->f_data;
440 struct drm_minor *const dminor = file->minor;
441 const dev_t devno = makedev(cdevsw_lookup_major(&drm_cdevsw),
442 64*dminor->type + dminor->index);
443
444 (void)memset(st, 0, sizeof(*st));
445
446 st->st_dev = devno;
447 st->st_ino = 0; /* XXX (dev,ino) uniqueness bleh */
448 st->st_uid = kauth_cred_geteuid(fp->f_cred);
449 st->st_gid = kauth_cred_getegid(fp->f_cred);
450 st->st_mode = S_IFCHR; /* XXX what? */
451 st->st_rdev = devno;
452 /* XXX what else? */
453
454 return 0;
455 }
456
457 static int
458 drm_fop_mmap(struct file *fp, off_t *offp, size_t len, int prot, int *flagsp,
459 int *advicep, struct uvm_object **uobjp, int *maxprotp)
460 {
461 struct drm_file *const file = fp->f_data;
462 struct drm_device *const dev = file->minor->dev;
463 int error;
464
465 KASSERT(fp == file->filp);
466 error = (*dev->driver->mmap_object)(dev, *offp, len, prot, uobjp,
467 offp, file->filp);
468 *maxprotp = prot;
469 *advicep = UVM_ADV_RANDOM;
470 return -error;
471 }
472
473 static paddr_t
474 drm_legacy_mmap(dev_t d, off_t offset, int prot)
475 {
476 struct drm_minor *dminor;
477 paddr_t paddr;
478
479 dminor = drm_minor_acquire(minor(d));
480 if (IS_ERR(dminor))
481 return (paddr_t)-1;
482
483 paddr = drm_legacy_mmap_paddr(dminor->dev, offset, prot);
484
485 drm_minor_release(dminor);
486 return paddr;
487 }
488