]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/drm2/drm_fops.c
ssh: Update to OpenSSH 9.6p1
[FreeBSD/FreeBSD.git] / sys / dev / drm2 / drm_fops.c
1 /**
2  * \file drm_fops.c
3  * File operations for DRM
4  *
5  * \author Rickard E. (Rik) Faith <faith@valinux.com>
6  * \author Daryll Strauss <daryll@valinux.com>
7  * \author Gareth Hughes <gareth@valinux.com>
8  */
9
10 /*
11  * Created: Mon Jan  4 08:58:31 1999 by faith@valinux.com
12  *
13  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
14  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
15  * All Rights Reserved.
16  *
17  * Permission is hereby granted, free of charge, to any person obtaining a
18  * copy of this software and associated documentation files (the "Software"),
19  * to deal in the Software without restriction, including without limitation
20  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
21  * and/or sell copies of the Software, and to permit persons to whom the
22  * Software is furnished to do so, subject to the following conditions:
23  *
24  * The above copyright notice and this permission notice (including the next
25  * paragraph) shall be included in all copies or substantial portions of the
26  * Software.
27  *
28  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
31  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
32  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
33  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
34  * OTHER DEALINGS IN THE SOFTWARE.
35  */
36
37 #include <sys/cdefs.h>
38 #include <dev/drm2/drmP.h>
39
40 static int drm_open_helper(struct cdev *kdev, int flags, int fmt,
41                            DRM_STRUCTPROC *p, struct drm_device *dev);
42
43 static int drm_setup(struct drm_device * dev)
44 {
45         int i;
46         int ret;
47
48         if (dev->driver->firstopen) {
49                 ret = dev->driver->firstopen(dev);
50                 if (ret != 0)
51                         return ret;
52         }
53
54         atomic_set(&dev->ioctl_count, 0);
55         atomic_set(&dev->vma_count, 0);
56
57         if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&
58             !drm_core_check_feature(dev, DRIVER_MODESET)) {
59                 dev->buf_use = 0;
60                 atomic_set(&dev->buf_alloc, 0);
61
62                 i = drm_dma_setup(dev);
63                 if (i < 0)
64                         return i;
65         }
66
67         /*
68          * FIXME Linux<->FreeBSD: counter incremented in drm_open() and
69          * reset to 0 here.
70          */
71 #if 0
72         for (i = 0; i < ARRAY_SIZE(dev->counts); i++)
73                 atomic_set(&dev->counts[i], 0);
74 #endif
75
76         dev->sigdata.lock = NULL;
77
78         dev->context_flag = 0;
79         dev->interrupt_flag = 0;
80         dev->dma_flag = 0;
81         dev->last_context = 0;
82         dev->last_switch = 0;
83         dev->last_checked = 0;
84         DRM_INIT_WAITQUEUE(&dev->context_wait);
85         dev->if_version = 0;
86
87 #ifdef FREEBSD_NOTYET
88         dev->ctx_start = 0;
89         dev->lck_start = 0;
90
91         dev->buf_async = NULL;
92         DRM_INIT_WAITQUEUE(&dev->buf_readers);
93         DRM_INIT_WAITQUEUE(&dev->buf_writers);
94 #endif /* FREEBSD_NOTYET */
95
96         DRM_DEBUG("\n");
97
98         /*
99          * The kernel's context could be created here, but is now created
100          * in drm_dma_enqueue.  This is more resource-efficient for
101          * hardware that does not do DMA, but may mean that
102          * drm_select_queue fails between the time the interrupt is
103          * initialized and the time the queues are initialized.
104          */
105
106         return 0;
107 }
108
109 /**
110  * Open file.
111  *
112  * \param inode device inode
113  * \param filp file pointer.
114  * \return zero on success or a negative number on failure.
115  *
116  * Searches the DRM device with the same minor number, calls open_helper(), and
117  * increments the device open count. If the open count was previous at zero,
118  * i.e., it's the first that the device is open, then calls setup().
119  */
120 int drm_open(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p)
121 {
122         struct drm_device *dev = NULL;
123         struct drm_minor *minor;
124         int retcode = 0;
125         int need_setup = 0;
126
127         minor = kdev->si_drv1;
128         if (!minor)
129                 return ENODEV;
130
131         if (!(dev = minor->dev))
132                 return ENODEV;
133
134         sx_xlock(&drm_global_mutex);
135
136         /*
137          * FIXME Linux<->FreeBSD: On Linux, counter updated outside
138          * global mutex.
139          */
140         if (!dev->open_count++)
141                 need_setup = 1;
142
143         retcode = drm_open_helper(kdev, flags, fmt, p, dev);
144         if (retcode) {
145                 sx_xunlock(&drm_global_mutex);
146                 return (-retcode);
147         }
148         atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
149         if (need_setup) {
150                 retcode = drm_setup(dev);
151                 if (retcode)
152                         goto err_undo;
153         }
154         sx_xunlock(&drm_global_mutex);
155         return 0;
156
157 err_undo:
158         device_unbusy(dev->dev);
159         dev->open_count--;
160         sx_xunlock(&drm_global_mutex);
161         return -retcode;
162 }
163 EXPORT_SYMBOL(drm_open);
164
165 /**
166  * Called whenever a process opens /dev/drm.
167  *
168  * \param inode device inode.
169  * \param filp file pointer.
170  * \param dev device.
171  * \return zero on success or a negative number on failure.
172  *
173  * Creates and initializes a drm_file structure for the file private data in \p
174  * filp and add it into the double linked list in \p dev.
175  */
176 static int drm_open_helper(struct cdev *kdev, int flags, int fmt,
177                            DRM_STRUCTPROC *p, struct drm_device *dev)
178 {
179         struct drm_file *priv;
180         int ret;
181
182         if (flags & O_EXCL)
183                 return -EBUSY;  /* No exclusive opens */
184         if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
185                 return -EINVAL;
186
187         DRM_DEBUG("pid = %d, device = %s\n", DRM_CURRENTPID, devtoname(kdev));
188
189         priv = malloc(sizeof(*priv), DRM_MEM_FILES, M_NOWAIT | M_ZERO);
190         if (!priv)
191                 return -ENOMEM;
192
193         priv->uid = p->td_ucred->cr_svuid;
194         priv->pid = p->td_proc->p_pid;
195         priv->minor = kdev->si_drv1;
196         priv->ioctl_count = 0;
197         /* for compatibility root is always authenticated */
198         priv->authenticated = DRM_SUSER(p);
199         priv->lock_count = 0;
200
201         INIT_LIST_HEAD(&priv->lhead);
202         INIT_LIST_HEAD(&priv->fbs);
203         INIT_LIST_HEAD(&priv->event_list);
204         priv->event_space = 4096; /* set aside 4k for event buffer */
205
206         if (dev->driver->driver_features & DRIVER_GEM)
207                 drm_gem_open(dev, priv);
208
209 #ifdef FREEBSD_NOTYET
210         if (drm_core_check_feature(dev, DRIVER_PRIME))
211                 drm_prime_init_file_private(&priv->prime);
212 #endif /* FREEBSD_NOTYET */
213
214         if (dev->driver->open) {
215                 ret = dev->driver->open(dev, priv);
216                 if (ret < 0)
217                         goto out_free;
218         }
219
220
221         /* if there is no current master make this fd it */
222         DRM_LOCK(dev);
223         if (!priv->minor->master) {
224                 /* create a new master */
225                 priv->minor->master = drm_master_create(priv->minor);
226                 if (!priv->minor->master) {
227                         DRM_UNLOCK(dev);
228                         ret = -ENOMEM;
229                         goto out_free;
230                 }
231
232                 priv->is_master = 1;
233                 /* take another reference for the copy in the local file priv */
234                 priv->master = drm_master_get(priv->minor->master);
235
236                 priv->authenticated = 1;
237
238                 DRM_UNLOCK(dev);
239                 if (dev->driver->master_create) {
240                         ret = dev->driver->master_create(dev, priv->master);
241                         if (ret) {
242                                 DRM_LOCK(dev);
243                                 /* drop both references if this fails */
244                                 drm_master_put(&priv->minor->master);
245                                 drm_master_put(&priv->master);
246                                 DRM_UNLOCK(dev);
247                                 goto out_free;
248                         }
249                 }
250                 DRM_LOCK(dev);
251                 if (dev->driver->master_set) {
252                         ret = dev->driver->master_set(dev, priv, true);
253                         if (ret) {
254                                 /* drop both references if this fails */
255                                 drm_master_put(&priv->minor->master);
256                                 drm_master_put(&priv->master);
257                                 DRM_UNLOCK(dev);
258                                 goto out_free;
259                         }
260                 }
261                 DRM_UNLOCK(dev);
262         } else {
263                 /* get a reference to the master */
264                 priv->master = drm_master_get(priv->minor->master);
265                 DRM_UNLOCK(dev);
266         }
267
268         DRM_LOCK(dev);
269         list_add(&priv->lhead, &dev->filelist);
270         DRM_UNLOCK(dev);
271
272         device_busy(dev->dev);
273
274         ret = devfs_set_cdevpriv(priv, drm_release);
275         if (ret != 0)
276                 drm_release(priv);
277
278         return ret;
279       out_free:
280         free(priv, DRM_MEM_FILES);
281         return ret;
282 }
283
284 static void drm_master_release(struct drm_device *dev, struct drm_file *file_priv)
285 {
286
287         if (drm_i_have_hw_lock(dev, file_priv)) {
288                 DRM_DEBUG("File %p released, freeing lock for context %d\n",
289                           file_priv, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
290                 drm_lock_free(&file_priv->master->lock,
291                               _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
292         }
293 }
294
295 static void drm_events_release(struct drm_file *file_priv)
296 {
297         struct drm_device *dev = file_priv->minor->dev;
298         struct drm_pending_event *e, *et;
299         struct drm_pending_vblank_event *v, *vt;
300         unsigned long flags;
301
302         DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags);
303
304         /* Remove pending flips */
305         list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link)
306                 if (v->base.file_priv == file_priv) {
307                         list_del(&v->base.link);
308                         drm_vblank_put(dev, v->pipe);
309                         v->base.destroy(&v->base);
310                 }
311
312         /* Remove unconsumed events */
313         list_for_each_entry_safe(e, et, &file_priv->event_list, link)
314                 e->destroy(e);
315
316         DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags);
317 }
318
319 /**
320  * Release file.
321  *
322  * \param inode device inode
323  * \param file_priv DRM file private.
324  * \return zero on success or a negative number on failure.
325  *
326  * If the hardware lock is held then free it, and take it again for the kernel
327  * context since it's necessary to reclaim buffers. Unlink the file private
328  * data from its list and free it. Decreases the open count and if it reaches
329  * zero calls drm_lastclose().
330  */
331 void drm_release(void *data)
332 {
333         struct drm_file *file_priv = data;
334         struct drm_device *dev = file_priv->minor->dev;
335
336         sx_xlock(&drm_global_mutex);
337
338         DRM_DEBUG("open_count = %d\n", dev->open_count);
339
340         if (dev->driver->preclose)
341                 dev->driver->preclose(dev, file_priv);
342
343         /* ========================================================
344          * Begin inline drm_release
345          */
346
347         DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
348                   DRM_CURRENTPID,
349                   (long)file_priv->minor->device,
350                   dev->open_count);
351
352         /* Release any auth tokens that might point to this file_priv,
353            (do that under the drm_global_mutex) */
354         if (file_priv->magic)
355                 (void) drm_remove_magic(file_priv->master, file_priv->magic);
356
357         /* if the master has gone away we can't do anything with the lock */
358         if (file_priv->minor->master)
359                 drm_master_release(dev, file_priv);
360
361         if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
362                 drm_core_reclaim_buffers(dev, file_priv);
363
364         drm_events_release(file_priv);
365
366         seldrain(&file_priv->event_poll);
367
368         if (dev->driver->driver_features & DRIVER_MODESET)
369                 drm_fb_release(file_priv);
370
371         if (dev->driver->driver_features & DRIVER_GEM)
372                 drm_gem_release(dev, file_priv);
373
374 #ifdef FREEBSD_NOTYET
375         mutex_lock(&dev->ctxlist_mutex);
376         if (!list_empty(&dev->ctxlist)) {
377                 struct drm_ctx_list *pos, *n;
378
379                 list_for_each_entry_safe(pos, n, &dev->ctxlist, head) {
380                         if (pos->tag == file_priv &&
381                             pos->handle != DRM_KERNEL_CONTEXT) {
382                                 if (dev->driver->context_dtor)
383                                         dev->driver->context_dtor(dev,
384                                                                   pos->handle);
385
386                                 drm_ctxbitmap_free(dev, pos->handle);
387
388                                 list_del(&pos->head);
389                                 kfree(pos);
390                                 --dev->ctx_count;
391                         }
392                 }
393         }
394         mutex_unlock(&dev->ctxlist_mutex);
395 #endif /* FREEBSD_NOTYET */
396
397         DRM_LOCK(dev);
398
399         if (file_priv->is_master) {
400                 struct drm_master *master = file_priv->master;
401                 struct drm_file *temp;
402                 list_for_each_entry(temp, &dev->filelist, lhead) {
403                         if ((temp->master == file_priv->master) &&
404                             (temp != file_priv))
405                                 temp->authenticated = 0;
406                 }
407
408                 /**
409                  * Since the master is disappearing, so is the
410                  * possibility to lock.
411                  */
412
413                 if (master->lock.hw_lock) {
414                         if (dev->sigdata.lock == master->lock.hw_lock)
415                                 dev->sigdata.lock = NULL;
416                         master->lock.hw_lock = NULL;
417                         master->lock.file_priv = NULL;
418                         DRM_WAKEUP_INT(&master->lock.lock_queue);
419                 }
420
421                 if (file_priv->minor->master == file_priv->master) {
422                         /* drop the reference held my the minor */
423                         if (dev->driver->master_drop)
424                                 dev->driver->master_drop(dev, file_priv, true);
425                         drm_master_put(&file_priv->minor->master);
426                 }
427         }
428
429         /* drop the reference held my the file priv */
430         drm_master_put(&file_priv->master);
431         file_priv->is_master = 0;
432         list_del(&file_priv->lhead);
433         DRM_UNLOCK(dev);
434
435         if (dev->driver->postclose)
436                 dev->driver->postclose(dev, file_priv);
437
438 #ifdef FREEBSD_NOTYET
439         if (drm_core_check_feature(dev, DRIVER_PRIME))
440                 drm_prime_destroy_file_private(&file_priv->prime);
441 #endif /* FREEBSD_NOTYET */
442
443         free(file_priv, DRM_MEM_FILES);
444
445         /* ========================================================
446          * End inline drm_release
447          */
448
449         atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
450         device_unbusy(dev->dev);
451         if (!--dev->open_count) {
452                 if (atomic_read(&dev->ioctl_count)) {
453                         DRM_ERROR("Device busy: %d\n",
454                                   atomic_read(&dev->ioctl_count));
455                 } else
456                         drm_lastclose(dev);
457         }
458         sx_xunlock(&drm_global_mutex);
459 }
460 EXPORT_SYMBOL(drm_release);
461
462 static bool
463 drm_dequeue_event(struct drm_file *file_priv, struct uio *uio,
464     struct drm_pending_event **out)
465 {
466         struct drm_pending_event *e;
467         bool ret = false;
468
469         /* Already locked in drm_read(). */
470         /* DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags); */
471
472         *out = NULL;
473         if (list_empty(&file_priv->event_list))
474                 goto out;
475         e = list_first_entry(&file_priv->event_list,
476                              struct drm_pending_event, link);
477         if (e->event->length > uio->uio_resid)
478                 goto out;
479
480         file_priv->event_space += e->event->length;
481         list_del(&e->link);
482         *out = e;
483         ret = true;
484
485 out:
486         /* DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags); */
487         return ret;
488 }
489
490 int
491 drm_read(struct cdev *kdev, struct uio *uio, int ioflag)
492 {
493         struct drm_file *file_priv;
494         struct drm_device *dev;
495         struct drm_pending_event *e;
496         ssize_t error;
497
498         error = devfs_get_cdevpriv((void **)&file_priv);
499         if (error != 0) {
500                 DRM_ERROR("can't find authenticator\n");
501                 return (EINVAL);
502         }
503
504         dev = drm_get_device_from_kdev(kdev);
505         mtx_lock(&dev->event_lock);
506         while (list_empty(&file_priv->event_list)) {
507                 if ((ioflag & O_NONBLOCK) != 0) {
508                         error = EAGAIN;
509                         goto out;
510                 }
511                 error = msleep(&file_priv->event_space, &dev->event_lock,
512                    PCATCH, "drmrea", 0);
513                if (error != 0)
514                        goto out;
515         }
516
517         while (drm_dequeue_event(file_priv, uio, &e)) {
518                 mtx_unlock(&dev->event_lock);
519                 error = uiomove(e->event, e->event->length, uio);
520                 CTR3(KTR_DRM, "drm_event_dequeued %d %d %d", curproc->p_pid,
521                     e->event->type, e->event->length);
522
523                 e->destroy(e);
524                 if (error != 0)
525                         return (error);
526                 mtx_lock(&dev->event_lock);
527         }
528
529 out:
530         mtx_unlock(&dev->event_lock);
531         return (error);
532 }
533 EXPORT_SYMBOL(drm_read);
534
535 void
536 drm_event_wakeup(struct drm_pending_event *e)
537 {
538         struct drm_file *file_priv;
539         struct drm_device *dev __diagused;
540
541         file_priv = e->file_priv;
542         dev = file_priv->minor->dev;
543         mtx_assert(&dev->event_lock, MA_OWNED);
544
545         wakeup(&file_priv->event_space);
546         selwakeup(&file_priv->event_poll);
547 }
548
549 int
550 drm_poll(struct cdev *kdev, int events, struct thread *td)
551 {
552         struct drm_file *file_priv;
553         struct drm_device *dev;
554         int error, revents;
555
556         error = devfs_get_cdevpriv((void **)&file_priv);
557         if (error != 0) {
558                 DRM_ERROR("can't find authenticator\n");
559                 return (EINVAL);
560         }
561
562         dev = drm_get_device_from_kdev(kdev);
563
564         revents = 0;
565         mtx_lock(&dev->event_lock);
566         if ((events & (POLLIN | POLLRDNORM)) != 0) {
567                 if (list_empty(&file_priv->event_list)) {
568                         CTR0(KTR_DRM, "drm_poll empty list");
569                         selrecord(td, &file_priv->event_poll);
570                 } else {
571                         revents |= events & (POLLIN | POLLRDNORM);
572                         CTR1(KTR_DRM, "drm_poll revents %x", revents);
573                 }
574         }
575         mtx_unlock(&dev->event_lock);
576         return (revents);
577 }
578 EXPORT_SYMBOL(drm_poll);
579
580 int
581 drm_mmap_single(struct cdev *kdev, vm_ooffset_t *offset, vm_size_t size,
582     struct vm_object **obj_res, int nprot)
583 {
584         struct drm_device *dev;
585
586         dev = drm_get_device_from_kdev(kdev);
587         if (dev->drm_ttm_bdev != NULL) {
588                 return (-ttm_bo_mmap_single(dev->drm_ttm_bdev, offset, size,
589                     obj_res, nprot));
590         } else if ((dev->driver->driver_features & DRIVER_GEM) != 0) {
591                 return (-drm_gem_mmap_single(dev, offset, size, obj_res, nprot));
592         } else {
593                 return (ENODEV);
594         }
595 }