The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/drm2/drm_fops.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 }

Cache object: 8e7095a899e0673d586c7883e1de805b


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.