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