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         mtx_lock(&Giant); /* FIXME: Giant required? */
  161         device_unbusy(dev->dev);
  162         mtx_unlock(&Giant);
  163         dev->open_count--;
  164         sx_xunlock(&drm_global_mutex);
  165         return -retcode;
  166 }
  167 EXPORT_SYMBOL(drm_open);
  168 
  169 /**
  170  * Called whenever a process opens /dev/drm.
  171  *
  172  * \param inode device inode.
  173  * \param filp file pointer.
  174  * \param dev device.
  175  * \return zero on success or a negative number on failure.
  176  *
  177  * Creates and initializes a drm_file structure for the file private data in \p
  178  * filp and add it into the double linked list in \p dev.
  179  */
  180 static int drm_open_helper(struct cdev *kdev, int flags, int fmt,
  181                            DRM_STRUCTPROC *p, struct drm_device *dev)
  182 {
  183         struct drm_file *priv;
  184         int ret;
  185 
  186         if (flags & O_EXCL)
  187                 return -EBUSY;  /* No exclusive opens */
  188         if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
  189                 return -EINVAL;
  190 
  191         DRM_DEBUG("pid = %d, device = %s\n", DRM_CURRENTPID, devtoname(kdev));
  192 
  193         priv = malloc(sizeof(*priv), DRM_MEM_FILES, M_NOWAIT | M_ZERO);
  194         if (!priv)
  195                 return -ENOMEM;
  196 
  197         priv->uid = p->td_ucred->cr_svuid;
  198         priv->pid = p->td_proc->p_pid;
  199         priv->minor = kdev->si_drv1;
  200         priv->ioctl_count = 0;
  201         /* for compatibility root is always authenticated */
  202         priv->authenticated = DRM_SUSER(p);
  203         priv->lock_count = 0;
  204 
  205         INIT_LIST_HEAD(&priv->lhead);
  206         INIT_LIST_HEAD(&priv->fbs);
  207         INIT_LIST_HEAD(&priv->event_list);
  208         priv->event_space = 4096; /* set aside 4k for event buffer */
  209 
  210         if (dev->driver->driver_features & DRIVER_GEM)
  211                 drm_gem_open(dev, priv);
  212 
  213 #ifdef FREEBSD_NOTYET
  214         if (drm_core_check_feature(dev, DRIVER_PRIME))
  215                 drm_prime_init_file_private(&priv->prime);
  216 #endif /* FREEBSD_NOTYET */
  217 
  218         if (dev->driver->open) {
  219                 ret = dev->driver->open(dev, priv);
  220                 if (ret < 0)
  221                         goto out_free;
  222         }
  223 
  224 
  225         /* if there is no current master make this fd it */
  226         DRM_LOCK(dev);
  227         if (!priv->minor->master) {
  228                 /* create a new master */
  229                 priv->minor->master = drm_master_create(priv->minor);
  230                 if (!priv->minor->master) {
  231                         DRM_UNLOCK(dev);
  232                         ret = -ENOMEM;
  233                         goto out_free;
  234                 }
  235 
  236                 priv->is_master = 1;
  237                 /* take another reference for the copy in the local file priv */
  238                 priv->master = drm_master_get(priv->minor->master);
  239 
  240                 priv->authenticated = 1;
  241 
  242                 DRM_UNLOCK(dev);
  243                 if (dev->driver->master_create) {
  244                         ret = dev->driver->master_create(dev, priv->master);
  245                         if (ret) {
  246                                 DRM_LOCK(dev);
  247                                 /* drop both references if this fails */
  248                                 drm_master_put(&priv->minor->master);
  249                                 drm_master_put(&priv->master);
  250                                 DRM_UNLOCK(dev);
  251                                 goto out_free;
  252                         }
  253                 }
  254                 DRM_LOCK(dev);
  255                 if (dev->driver->master_set) {
  256                         ret = dev->driver->master_set(dev, priv, true);
  257                         if (ret) {
  258                                 /* drop both references if this fails */
  259                                 drm_master_put(&priv->minor->master);
  260                                 drm_master_put(&priv->master);
  261                                 DRM_UNLOCK(dev);
  262                                 goto out_free;
  263                         }
  264                 }
  265                 DRM_UNLOCK(dev);
  266         } else {
  267                 /* get a reference to the master */
  268                 priv->master = drm_master_get(priv->minor->master);
  269                 DRM_UNLOCK(dev);
  270         }
  271 
  272         DRM_LOCK(dev);
  273         list_add(&priv->lhead, &dev->filelist);
  274         DRM_UNLOCK(dev);
  275 
  276         mtx_lock(&Giant); /* FIXME: Giant required? */
  277         device_busy(dev->dev);
  278         mtx_unlock(&Giant);
  279 
  280         ret = devfs_set_cdevpriv(priv, drm_release);
  281         if (ret != 0)
  282                 drm_release(priv);
  283 
  284         return ret;
  285       out_free:
  286         free(priv, DRM_MEM_FILES);
  287         return ret;
  288 }
  289 
  290 static void drm_master_release(struct drm_device *dev, struct drm_file *file_priv)
  291 {
  292 
  293         if (drm_i_have_hw_lock(dev, file_priv)) {
  294                 DRM_DEBUG("File %p released, freeing lock for context %d\n",
  295                           file_priv, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
  296                 drm_lock_free(&file_priv->master->lock,
  297                               _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));
  298         }
  299 }
  300 
  301 static void drm_events_release(struct drm_file *file_priv)
  302 {
  303         struct drm_device *dev = file_priv->minor->dev;
  304         struct drm_pending_event *e, *et;
  305         struct drm_pending_vblank_event *v, *vt;
  306         unsigned long flags;
  307 
  308         DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags);
  309 
  310         /* Remove pending flips */
  311         list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link)
  312                 if (v->base.file_priv == file_priv) {
  313                         list_del(&v->base.link);
  314                         drm_vblank_put(dev, v->pipe);
  315                         v->base.destroy(&v->base);
  316                 }
  317 
  318         /* Remove unconsumed events */
  319         list_for_each_entry_safe(e, et, &file_priv->event_list, link)
  320                 e->destroy(e);
  321 
  322         DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags);
  323 }
  324 
  325 /**
  326  * Release file.
  327  *
  328  * \param inode device inode
  329  * \param file_priv DRM file private.
  330  * \return zero on success or a negative number on failure.
  331  *
  332  * If the hardware lock is held then free it, and take it again for the kernel
  333  * context since it's necessary to reclaim buffers. Unlink the file private
  334  * data from its list and free it. Decreases the open count and if it reaches
  335  * zero calls drm_lastclose().
  336  */
  337 void drm_release(void *data)
  338 {
  339         struct drm_file *file_priv = data;
  340         struct drm_device *dev = file_priv->minor->dev;
  341 
  342         sx_xlock(&drm_global_mutex);
  343 
  344         DRM_DEBUG("open_count = %d\n", dev->open_count);
  345 
  346         if (dev->driver->preclose)
  347                 dev->driver->preclose(dev, file_priv);
  348 
  349         /* ========================================================
  350          * Begin inline drm_release
  351          */
  352 
  353         DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
  354                   DRM_CURRENTPID,
  355                   (long)file_priv->minor->device,
  356                   dev->open_count);
  357 
  358         /* Release any auth tokens that might point to this file_priv,
  359            (do that under the drm_global_mutex) */
  360         if (file_priv->magic)
  361                 (void) drm_remove_magic(file_priv->master, file_priv->magic);
  362 
  363         /* if the master has gone away we can't do anything with the lock */
  364         if (file_priv->minor->master)
  365                 drm_master_release(dev, file_priv);
  366 
  367         if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
  368                 drm_core_reclaim_buffers(dev, file_priv);
  369 
  370         drm_events_release(file_priv);
  371 
  372         seldrain(&file_priv->event_poll);
  373 
  374         if (dev->driver->driver_features & DRIVER_MODESET)
  375                 drm_fb_release(file_priv);
  376 
  377         if (dev->driver->driver_features & DRIVER_GEM)
  378                 drm_gem_release(dev, file_priv);
  379 
  380 #ifdef FREEBSD_NOTYET
  381         mutex_lock(&dev->ctxlist_mutex);
  382         if (!list_empty(&dev->ctxlist)) {
  383                 struct drm_ctx_list *pos, *n;
  384 
  385                 list_for_each_entry_safe(pos, n, &dev->ctxlist, head) {
  386                         if (pos->tag == file_priv &&
  387                             pos->handle != DRM_KERNEL_CONTEXT) {
  388                                 if (dev->driver->context_dtor)
  389                                         dev->driver->context_dtor(dev,
  390                                                                   pos->handle);
  391 
  392                                 drm_ctxbitmap_free(dev, pos->handle);
  393 
  394                                 list_del(&pos->head);
  395                                 kfree(pos);
  396                                 --dev->ctx_count;
  397                         }
  398                 }
  399         }
  400         mutex_unlock(&dev->ctxlist_mutex);
  401 #endif /* FREEBSD_NOTYET */
  402 
  403         DRM_LOCK(dev);
  404 
  405         if (file_priv->is_master) {
  406                 struct drm_master *master = file_priv->master;
  407                 struct drm_file *temp;
  408                 list_for_each_entry(temp, &dev->filelist, lhead) {
  409                         if ((temp->master == file_priv->master) &&
  410                             (temp != file_priv))
  411                                 temp->authenticated = 0;
  412                 }
  413 
  414                 /**
  415                  * Since the master is disappearing, so is the
  416                  * possibility to lock.
  417                  */
  418 
  419                 if (master->lock.hw_lock) {
  420                         if (dev->sigdata.lock == master->lock.hw_lock)
  421                                 dev->sigdata.lock = NULL;
  422                         master->lock.hw_lock = NULL;
  423                         master->lock.file_priv = NULL;
  424                         DRM_WAKEUP_INT(&master->lock.lock_queue);
  425                 }
  426 
  427                 if (file_priv->minor->master == file_priv->master) {
  428                         /* drop the reference held my the minor */
  429                         if (dev->driver->master_drop)
  430                                 dev->driver->master_drop(dev, file_priv, true);
  431                         drm_master_put(&file_priv->minor->master);
  432                 }
  433         }
  434 
  435         /* drop the reference held my the file priv */
  436         drm_master_put(&file_priv->master);
  437         file_priv->is_master = 0;
  438         list_del(&file_priv->lhead);
  439         DRM_UNLOCK(dev);
  440 
  441         if (dev->driver->postclose)
  442                 dev->driver->postclose(dev, file_priv);
  443 
  444 #ifdef FREEBSD_NOTYET
  445         if (drm_core_check_feature(dev, DRIVER_PRIME))
  446                 drm_prime_destroy_file_private(&file_priv->prime);
  447 #endif /* FREEBSD_NOTYET */
  448 
  449         free(file_priv, DRM_MEM_FILES);
  450 
  451         /* ========================================================
  452          * End inline drm_release
  453          */
  454 
  455         atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
  456         mtx_lock(&Giant);
  457         device_unbusy(dev->dev);
  458         mtx_unlock(&Giant);
  459         if (!--dev->open_count) {
  460                 if (atomic_read(&dev->ioctl_count)) {
  461                         DRM_ERROR("Device busy: %d\n",
  462                                   atomic_read(&dev->ioctl_count));
  463                 } else
  464                         drm_lastclose(dev);
  465         }
  466         sx_xunlock(&drm_global_mutex);
  467 }
  468 EXPORT_SYMBOL(drm_release);
  469 
  470 static bool
  471 drm_dequeue_event(struct drm_file *file_priv, struct uio *uio,
  472     struct drm_pending_event **out)
  473 {
  474         struct drm_pending_event *e;
  475         bool ret = false;
  476 
  477         /* Already locked in drm_read(). */
  478         /* DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags); */
  479 
  480         *out = NULL;
  481         if (list_empty(&file_priv->event_list))
  482                 goto out;
  483         e = list_first_entry(&file_priv->event_list,
  484                              struct drm_pending_event, link);
  485         if (e->event->length > uio->uio_resid)
  486                 goto out;
  487 
  488         file_priv->event_space += e->event->length;
  489         list_del(&e->link);
  490         *out = e;
  491         ret = true;
  492 
  493 out:
  494         /* DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags); */
  495         return ret;
  496 }
  497 
  498 int
  499 drm_read(struct cdev *kdev, struct uio *uio, int ioflag)
  500 {
  501         struct drm_file *file_priv;
  502         struct drm_device *dev;
  503         struct drm_pending_event *e;
  504         ssize_t error;
  505 
  506         error = devfs_get_cdevpriv((void **)&file_priv);
  507         if (error != 0) {
  508                 DRM_ERROR("can't find authenticator\n");
  509                 return (EINVAL);
  510         }
  511 
  512         dev = drm_get_device_from_kdev(kdev);
  513         mtx_lock(&dev->event_lock);
  514         while (list_empty(&file_priv->event_list)) {
  515                 if ((ioflag & O_NONBLOCK) != 0) {
  516                         error = EAGAIN;
  517                         goto out;
  518                 }
  519                 error = msleep(&file_priv->event_space, &dev->event_lock,
  520                    PCATCH, "drmrea", 0);
  521                if (error != 0)
  522                        goto out;
  523         }
  524 
  525         while (drm_dequeue_event(file_priv, uio, &e)) {
  526                 mtx_unlock(&dev->event_lock);
  527                 error = uiomove(e->event, e->event->length, uio);
  528                 CTR3(KTR_DRM, "drm_event_dequeued %d %d %d", curproc->p_pid,
  529                     e->event->type, e->event->length);
  530 
  531                 e->destroy(e);
  532                 if (error != 0)
  533                         return (error);
  534                 mtx_lock(&dev->event_lock);
  535         }
  536 
  537 out:
  538         mtx_unlock(&dev->event_lock);
  539         return (error);
  540 }
  541 EXPORT_SYMBOL(drm_read);
  542 
  543 void
  544 drm_event_wakeup(struct drm_pending_event *e)
  545 {
  546         struct drm_file *file_priv;
  547         struct drm_device *dev;
  548 
  549         file_priv = e->file_priv;
  550         dev = file_priv->minor->dev;
  551         mtx_assert(&dev->event_lock, MA_OWNED);
  552 
  553         wakeup(&file_priv->event_space);
  554         selwakeup(&file_priv->event_poll);
  555 }
  556 
  557 int
  558 drm_poll(struct cdev *kdev, int events, struct thread *td)
  559 {
  560         struct drm_file *file_priv;
  561         struct drm_device *dev;
  562         int error, revents;
  563 
  564         error = devfs_get_cdevpriv((void **)&file_priv);
  565         if (error != 0) {
  566                 DRM_ERROR("can't find authenticator\n");
  567                 return (EINVAL);
  568         }
  569 
  570         dev = drm_get_device_from_kdev(kdev);
  571 
  572         revents = 0;
  573         mtx_lock(&dev->event_lock);
  574         if ((events & (POLLIN | POLLRDNORM)) != 0) {
  575                 if (list_empty(&file_priv->event_list)) {
  576                         CTR0(KTR_DRM, "drm_poll empty list");
  577                         selrecord(td, &file_priv->event_poll);
  578                 } else {
  579                         revents |= events & (POLLIN | POLLRDNORM);
  580                         CTR1(KTR_DRM, "drm_poll revents %x", revents);
  581                 }
  582         }
  583         mtx_unlock(&dev->event_lock);
  584         return (revents);
  585 }
  586 EXPORT_SYMBOL(drm_poll);
  587 
  588 int
  589 drm_mmap_single(struct cdev *kdev, vm_ooffset_t *offset, vm_size_t size,
  590     struct vm_object **obj_res, int nprot)
  591 {
  592         struct drm_device *dev;
  593 
  594         dev = drm_get_device_from_kdev(kdev);
  595         if (dev->drm_ttm_bdev != NULL) {
  596                 return (-ttm_bo_mmap_single(dev->drm_ttm_bdev, offset, size,
  597                     obj_res, nprot));
  598         } else if ((dev->driver->driver_features & DRIVER_GEM) != 0) {
  599                 return (-drm_gem_mmap_single(dev, offset, size, obj_res, nprot));
  600         } else {
  601                 return (ENODEV);
  602         }
  603 }

Cache object: 283ceab92caeda58f671c141fc46d564


[ 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.