1280183Sdumbbell/** 2280183Sdumbbell * \file drm_fops.c 3280183Sdumbbell * File operations for DRM 4280183Sdumbbell * 5280183Sdumbbell * \author Rickard E. (Rik) Faith <faith@valinux.com> 6280183Sdumbbell * \author Daryll Strauss <daryll@valinux.com> 7280183Sdumbbell * \author Gareth Hughes <gareth@valinux.com> 8280183Sdumbbell */ 9280183Sdumbbell 10280183Sdumbbell/* 11280183Sdumbbell * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.com 12280183Sdumbbell * 13235783Skib * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. 14235783Skib * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 15235783Skib * All Rights Reserved. 16235783Skib * 17235783Skib * Permission is hereby granted, free of charge, to any person obtaining a 18235783Skib * copy of this software and associated documentation files (the "Software"), 19235783Skib * to deal in the Software without restriction, including without limitation 20235783Skib * the rights to use, copy, modify, merge, publish, distribute, sublicense, 21235783Skib * and/or sell copies of the Software, and to permit persons to whom the 22235783Skib * Software is furnished to do so, subject to the following conditions: 23235783Skib * 24235783Skib * The above copyright notice and this permission notice (including the next 25235783Skib * paragraph) shall be included in all copies or substantial portions of the 26235783Skib * Software. 27235783Skib * 28235783Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29235783Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30235783Skib * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 31235783Skib * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 32235783Skib * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 33235783Skib * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 34235783Skib * OTHER DEALINGS IN THE SOFTWARE. 35235783Skib */ 36235783Skib 37235783Skib#include <sys/cdefs.h> 38235783Skib__FBSDID("$FreeBSD: releng/11.0/sys/dev/drm2/drm_fops.c 288653 2015-10-04 07:45:36Z adrian $"); 39235783Skib 40280183Sdumbbell#include <dev/drm2/drmP.h> 41280183Sdumbbell 42280183Sdumbbellstatic int drm_open_helper(struct cdev *kdev, int flags, int fmt, 43280183Sdumbbell DRM_STRUCTPROC *p, struct drm_device *dev); 44280183Sdumbbell 45280183Sdumbbellstatic int drm_setup(struct drm_device * dev) 46280183Sdumbbell{ 47280183Sdumbbell int i; 48280183Sdumbbell int ret; 49280183Sdumbbell 50280183Sdumbbell if (dev->driver->firstopen) { 51280183Sdumbbell ret = dev->driver->firstopen(dev); 52280183Sdumbbell if (ret != 0) 53280183Sdumbbell return ret; 54280183Sdumbbell } 55280183Sdumbbell 56280183Sdumbbell atomic_set(&dev->ioctl_count, 0); 57280183Sdumbbell atomic_set(&dev->vma_count, 0); 58280183Sdumbbell 59280183Sdumbbell if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && 60280183Sdumbbell !drm_core_check_feature(dev, DRIVER_MODESET)) { 61280183Sdumbbell dev->buf_use = 0; 62280183Sdumbbell atomic_set(&dev->buf_alloc, 0); 63280183Sdumbbell 64280183Sdumbbell i = drm_dma_setup(dev); 65280183Sdumbbell if (i < 0) 66280183Sdumbbell return i; 67280183Sdumbbell } 68280183Sdumbbell 69280183Sdumbbell /* 70280183Sdumbbell * FIXME Linux<->FreeBSD: counter incremented in drm_open() and 71280183Sdumbbell * reset to 0 here. 72280183Sdumbbell */ 73280183Sdumbbell#if 0 74280183Sdumbbell for (i = 0; i < ARRAY_SIZE(dev->counts); i++) 75280183Sdumbbell atomic_set(&dev->counts[i], 0); 76280183Sdumbbell#endif 77280183Sdumbbell 78280183Sdumbbell dev->sigdata.lock = NULL; 79280183Sdumbbell 80280183Sdumbbell dev->context_flag = 0; 81280183Sdumbbell dev->interrupt_flag = 0; 82280183Sdumbbell dev->dma_flag = 0; 83280183Sdumbbell dev->last_context = 0; 84280183Sdumbbell dev->last_switch = 0; 85280183Sdumbbell dev->last_checked = 0; 86280183Sdumbbell DRM_INIT_WAITQUEUE(&dev->context_wait); 87280183Sdumbbell dev->if_version = 0; 88280183Sdumbbell 89280183Sdumbbell#ifdef FREEBSD_NOTYET 90280183Sdumbbell dev->ctx_start = 0; 91280183Sdumbbell dev->lck_start = 0; 92280183Sdumbbell 93280183Sdumbbell dev->buf_async = NULL; 94280183Sdumbbell DRM_INIT_WAITQUEUE(&dev->buf_readers); 95280183Sdumbbell DRM_INIT_WAITQUEUE(&dev->buf_writers); 96280183Sdumbbell#endif /* FREEBSD_NOTYET */ 97280183Sdumbbell 98280183Sdumbbell DRM_DEBUG("\n"); 99280183Sdumbbell 100280183Sdumbbell /* 101280183Sdumbbell * The kernel's context could be created here, but is now created 102280183Sdumbbell * in drm_dma_enqueue. This is more resource-efficient for 103280183Sdumbbell * hardware that does not do DMA, but may mean that 104280183Sdumbbell * drm_select_queue fails between the time the interrupt is 105280183Sdumbbell * initialized and the time the queues are initialized. 106280183Sdumbbell */ 107280183Sdumbbell 108280183Sdumbbell return 0; 109280183Sdumbbell} 110280183Sdumbbell 111280183Sdumbbell/** 112280183Sdumbbell * Open file. 113280183Sdumbbell * 114280183Sdumbbell * \param inode device inode 115280183Sdumbbell * \param filp file pointer. 116280183Sdumbbell * \return zero on success or a negative number on failure. 117280183Sdumbbell * 118280183Sdumbbell * Searches the DRM device with the same minor number, calls open_helper(), and 119280183Sdumbbell * increments the device open count. If the open count was previous at zero, 120280183Sdumbbell * i.e., it's the first that the device is open, then calls setup(). 121235783Skib */ 122280183Sdumbbellint drm_open(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p) 123280183Sdumbbell{ 124280183Sdumbbell struct drm_device *dev = NULL; 125280183Sdumbbell struct drm_minor *minor; 126280183Sdumbbell int retcode = 0; 127280183Sdumbbell int need_setup = 0; 128235783Skib 129280183Sdumbbell minor = kdev->si_drv1; 130280183Sdumbbell if (!minor) 131280183Sdumbbell return ENODEV; 132235783Skib 133280183Sdumbbell if (!(dev = minor->dev)) 134280183Sdumbbell return ENODEV; 135280183Sdumbbell 136280183Sdumbbell sx_xlock(&drm_global_mutex); 137280183Sdumbbell 138280183Sdumbbell /* 139288653Sadrian * FIXME Linux<->FreeBSD: On Linux, counter updated outside 140280183Sdumbbell * global mutex. 141280183Sdumbbell */ 142280183Sdumbbell if (!dev->open_count++) 143280183Sdumbbell need_setup = 1; 144280183Sdumbbell 145280183Sdumbbell retcode = drm_open_helper(kdev, flags, fmt, p, dev); 146280183Sdumbbell if (retcode) { 147280183Sdumbbell sx_xunlock(&drm_global_mutex); 148280183Sdumbbell return (-retcode); 149280183Sdumbbell } 150280183Sdumbbell atomic_inc(&dev->counts[_DRM_STAT_OPENS]); 151280183Sdumbbell if (need_setup) { 152280183Sdumbbell retcode = drm_setup(dev); 153280183Sdumbbell if (retcode) 154280183Sdumbbell goto err_undo; 155280183Sdumbbell } 156280183Sdumbbell sx_xunlock(&drm_global_mutex); 157280183Sdumbbell return 0; 158280183Sdumbbell 159280183Sdumbbellerr_undo: 160280183Sdumbbell mtx_lock(&Giant); /* FIXME: Giant required? */ 161280183Sdumbbell device_unbusy(dev->dev); 162280183Sdumbbell mtx_unlock(&Giant); 163280183Sdumbbell dev->open_count--; 164280183Sdumbbell sx_xunlock(&drm_global_mutex); 165280183Sdumbbell return -retcode; 166280183Sdumbbell} 167280183SdumbbellEXPORT_SYMBOL(drm_open); 168280183Sdumbbell 169280183Sdumbbell/** 170280183Sdumbbell * Called whenever a process opens /dev/drm. 171280183Sdumbbell * 172280183Sdumbbell * \param inode device inode. 173280183Sdumbbell * \param filp file pointer. 174280183Sdumbbell * \param dev device. 175280183Sdumbbell * \return zero on success or a negative number on failure. 176280183Sdumbbell * 177280183Sdumbbell * Creates and initializes a drm_file structure for the file private data in \p 178280183Sdumbbell * filp and add it into the double linked list in \p dev. 179280183Sdumbbell */ 180280183Sdumbbellstatic int drm_open_helper(struct cdev *kdev, int flags, int fmt, 181280183Sdumbbell DRM_STRUCTPROC *p, struct drm_device *dev) 182235783Skib{ 183235783Skib struct drm_file *priv; 184280183Sdumbbell int ret; 185235783Skib 186235783Skib if (flags & O_EXCL) 187280183Sdumbbell return -EBUSY; /* No exclusive opens */ 188280183Sdumbbell if (dev->switch_power_state != DRM_SWITCH_POWER_ON) 189280183Sdumbbell return -EINVAL; 190235783Skib 191235783Skib DRM_DEBUG("pid = %d, device = %s\n", DRM_CURRENTPID, devtoname(kdev)); 192235783Skib 193235783Skib priv = malloc(sizeof(*priv), DRM_MEM_FILES, M_NOWAIT | M_ZERO); 194280183Sdumbbell if (!priv) 195280183Sdumbbell return -ENOMEM; 196235783Skib 197280183Sdumbbell priv->uid = p->td_ucred->cr_svuid; 198280183Sdumbbell priv->pid = p->td_proc->p_pid; 199280183Sdumbbell priv->minor = kdev->si_drv1; 200280183Sdumbbell priv->ioctl_count = 0; 201235783Skib /* for compatibility root is always authenticated */ 202280183Sdumbbell priv->authenticated = DRM_SUSER(p); 203280183Sdumbbell priv->lock_count = 0; 204235783Skib 205280183Sdumbbell INIT_LIST_HEAD(&priv->lhead); 206235783Skib INIT_LIST_HEAD(&priv->fbs); 207235783Skib INIT_LIST_HEAD(&priv->event_list); 208235783Skib priv->event_space = 4096; /* set aside 4k for event buffer */ 209235783Skib 210235783Skib if (dev->driver->driver_features & DRIVER_GEM) 211235783Skib drm_gem_open(dev, priv); 212235783Skib 213280183Sdumbbell#ifdef FREEBSD_NOTYET 214280183Sdumbbell if (drm_core_check_feature(dev, DRIVER_PRIME)) 215280183Sdumbbell drm_prime_init_file_private(&priv->prime); 216280183Sdumbbell#endif /* FREEBSD_NOTYET */ 217280183Sdumbbell 218235783Skib if (dev->driver->open) { 219280183Sdumbbell ret = dev->driver->open(dev, priv); 220280183Sdumbbell if (ret < 0) 221280183Sdumbbell goto out_free; 222280183Sdumbbell } 223280183Sdumbbell 224280183Sdumbbell 225280183Sdumbbell /* if there is no current master make this fd it */ 226280183Sdumbbell DRM_LOCK(dev); 227280183Sdumbbell if (!priv->minor->master) { 228280183Sdumbbell /* create a new master */ 229280183Sdumbbell priv->minor->master = drm_master_create(priv->minor); 230280183Sdumbbell if (!priv->minor->master) { 231235783Skib DRM_UNLOCK(dev); 232280183Sdumbbell ret = -ENOMEM; 233280183Sdumbbell goto out_free; 234235783Skib } 235280183Sdumbbell 236280183Sdumbbell priv->is_master = 1; 237280183Sdumbbell /* take another reference for the copy in the local file priv */ 238280183Sdumbbell priv->master = drm_master_get(priv->minor->master); 239280183Sdumbbell 240280183Sdumbbell priv->authenticated = 1; 241280183Sdumbbell 242280183Sdumbbell DRM_UNLOCK(dev); 243280183Sdumbbell if (dev->driver->master_create) { 244280183Sdumbbell ret = dev->driver->master_create(dev, priv->master); 245280183Sdumbbell if (ret) { 246280183Sdumbbell DRM_LOCK(dev); 247280183Sdumbbell /* drop both references if this fails */ 248280183Sdumbbell drm_master_put(&priv->minor->master); 249280183Sdumbbell drm_master_put(&priv->master); 250280183Sdumbbell DRM_UNLOCK(dev); 251280183Sdumbbell goto out_free; 252280183Sdumbbell } 253280183Sdumbbell } 254280183Sdumbbell DRM_LOCK(dev); 255280183Sdumbbell if (dev->driver->master_set) { 256280183Sdumbbell ret = dev->driver->master_set(dev, priv, true); 257280183Sdumbbell if (ret) { 258280183Sdumbbell /* drop both references if this fails */ 259280183Sdumbbell drm_master_put(&priv->minor->master); 260280183Sdumbbell drm_master_put(&priv->master); 261280183Sdumbbell DRM_UNLOCK(dev); 262280183Sdumbbell goto out_free; 263280183Sdumbbell } 264280183Sdumbbell } 265280183Sdumbbell DRM_UNLOCK(dev); 266280183Sdumbbell } else { 267280183Sdumbbell /* get a reference to the master */ 268280183Sdumbbell priv->master = drm_master_get(priv->minor->master); 269280183Sdumbbell DRM_UNLOCK(dev); 270235783Skib } 271235783Skib 272280183Sdumbbell DRM_LOCK(dev); 273280183Sdumbbell list_add(&priv->lhead, &dev->filelist); 274280183Sdumbbell DRM_UNLOCK(dev); 275235783Skib 276280183Sdumbbell mtx_lock(&Giant); /* FIXME: Giant required? */ 277280183Sdumbbell device_busy(dev->dev); 278280183Sdumbbell mtx_unlock(&Giant); 279280183Sdumbbell 280280183Sdumbbell ret = devfs_set_cdevpriv(priv, drm_release); 281280183Sdumbbell if (ret != 0) 282280183Sdumbbell drm_release(priv); 283280183Sdumbbell 284280183Sdumbbell return ret; 285280183Sdumbbell out_free: 286280183Sdumbbell free(priv, DRM_MEM_FILES); 287280183Sdumbbell return ret; 288280183Sdumbbell} 289280183Sdumbbell 290280183Sdumbbellstatic void drm_master_release(struct drm_device *dev, struct drm_file *file_priv) 291280183Sdumbbell{ 292280183Sdumbbell 293280183Sdumbbell if (drm_i_have_hw_lock(dev, file_priv)) { 294280183Sdumbbell DRM_DEBUG("File %p released, freeing lock for context %d\n", 295280183Sdumbbell file_priv, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); 296280183Sdumbbell drm_lock_free(&file_priv->master->lock, 297280183Sdumbbell _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); 298280183Sdumbbell } 299280183Sdumbbell} 300280183Sdumbbell 301280183Sdumbbellstatic void drm_events_release(struct drm_file *file_priv) 302280183Sdumbbell{ 303280183Sdumbbell struct drm_device *dev = file_priv->minor->dev; 304280183Sdumbbell struct drm_pending_event *e, *et; 305280183Sdumbbell struct drm_pending_vblank_event *v, *vt; 306280183Sdumbbell unsigned long flags; 307280183Sdumbbell 308280183Sdumbbell DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags); 309280183Sdumbbell 310280183Sdumbbell /* Remove pending flips */ 311280183Sdumbbell list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link) 312280183Sdumbbell if (v->base.file_priv == file_priv) { 313280183Sdumbbell list_del(&v->base.link); 314280183Sdumbbell drm_vblank_put(dev, v->pipe); 315280183Sdumbbell v->base.destroy(&v->base); 316280183Sdumbbell } 317280183Sdumbbell 318280183Sdumbbell /* Remove unconsumed events */ 319280183Sdumbbell list_for_each_entry_safe(e, et, &file_priv->event_list, link) 320280183Sdumbbell e->destroy(e); 321280183Sdumbbell 322280183Sdumbbell DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags); 323280183Sdumbbell} 324280183Sdumbbell 325280183Sdumbbell/** 326280183Sdumbbell * Release file. 327280183Sdumbbell * 328280183Sdumbbell * \param inode device inode 329280183Sdumbbell * \param file_priv DRM file private. 330280183Sdumbbell * \return zero on success or a negative number on failure. 331280183Sdumbbell * 332280183Sdumbbell * If the hardware lock is held then free it, and take it again for the kernel 333280183Sdumbbell * context since it's necessary to reclaim buffers. Unlink the file private 334280183Sdumbbell * data from its list and free it. Decreases the open count and if it reaches 335280183Sdumbbell * zero calls drm_lastclose(). 336280183Sdumbbell */ 337280183Sdumbbellvoid drm_release(void *data) 338280183Sdumbbell{ 339280183Sdumbbell struct drm_file *file_priv = data; 340280183Sdumbbell struct drm_device *dev = file_priv->minor->dev; 341280183Sdumbbell 342280183Sdumbbell sx_xlock(&drm_global_mutex); 343280183Sdumbbell 344280183Sdumbbell DRM_DEBUG("open_count = %d\n", dev->open_count); 345280183Sdumbbell 346280183Sdumbbell if (dev->driver->preclose) 347280183Sdumbbell dev->driver->preclose(dev, file_priv); 348280183Sdumbbell 349280183Sdumbbell /* ======================================================== 350280183Sdumbbell * Begin inline drm_release 351280183Sdumbbell */ 352280183Sdumbbell 353280183Sdumbbell DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", 354280183Sdumbbell DRM_CURRENTPID, 355280183Sdumbbell (long)file_priv->minor->device, 356280183Sdumbbell dev->open_count); 357280183Sdumbbell 358280183Sdumbbell /* Release any auth tokens that might point to this file_priv, 359280183Sdumbbell (do that under the drm_global_mutex) */ 360280183Sdumbbell if (file_priv->magic) 361280183Sdumbbell (void) drm_remove_magic(file_priv->master, file_priv->magic); 362280183Sdumbbell 363280183Sdumbbell /* if the master has gone away we can't do anything with the lock */ 364280183Sdumbbell if (file_priv->minor->master) 365280183Sdumbbell drm_master_release(dev, file_priv); 366280183Sdumbbell 367280183Sdumbbell if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) 368280183Sdumbbell drm_core_reclaim_buffers(dev, file_priv); 369280183Sdumbbell 370280183Sdumbbell drm_events_release(file_priv); 371280183Sdumbbell 372280183Sdumbbell seldrain(&file_priv->event_poll); 373280183Sdumbbell 374280183Sdumbbell if (dev->driver->driver_features & DRIVER_MODESET) 375280183Sdumbbell drm_fb_release(file_priv); 376280183Sdumbbell 377280183Sdumbbell if (dev->driver->driver_features & DRIVER_GEM) 378280183Sdumbbell drm_gem_release(dev, file_priv); 379280183Sdumbbell 380280183Sdumbbell#ifdef FREEBSD_NOTYET 381280183Sdumbbell mutex_lock(&dev->ctxlist_mutex); 382280183Sdumbbell if (!list_empty(&dev->ctxlist)) { 383280183Sdumbbell struct drm_ctx_list *pos, *n; 384280183Sdumbbell 385280183Sdumbbell list_for_each_entry_safe(pos, n, &dev->ctxlist, head) { 386280183Sdumbbell if (pos->tag == file_priv && 387280183Sdumbbell pos->handle != DRM_KERNEL_CONTEXT) { 388280183Sdumbbell if (dev->driver->context_dtor) 389280183Sdumbbell dev->driver->context_dtor(dev, 390280183Sdumbbell pos->handle); 391280183Sdumbbell 392280183Sdumbbell drm_ctxbitmap_free(dev, pos->handle); 393280183Sdumbbell 394280183Sdumbbell list_del(&pos->head); 395280183Sdumbbell kfree(pos); 396280183Sdumbbell --dev->ctx_count; 397280183Sdumbbell } 398280183Sdumbbell } 399280183Sdumbbell } 400280183Sdumbbell mutex_unlock(&dev->ctxlist_mutex); 401280183Sdumbbell#endif /* FREEBSD_NOTYET */ 402280183Sdumbbell 403280183Sdumbbell DRM_LOCK(dev); 404280183Sdumbbell 405280183Sdumbbell if (file_priv->is_master) { 406280183Sdumbbell struct drm_master *master = file_priv->master; 407280183Sdumbbell struct drm_file *temp; 408280183Sdumbbell list_for_each_entry(temp, &dev->filelist, lhead) { 409280183Sdumbbell if ((temp->master == file_priv->master) && 410280183Sdumbbell (temp != file_priv)) 411280183Sdumbbell temp->authenticated = 0; 412280183Sdumbbell } 413280183Sdumbbell 414280183Sdumbbell /** 415280183Sdumbbell * Since the master is disappearing, so is the 416280183Sdumbbell * possibility to lock. 417280183Sdumbbell */ 418280183Sdumbbell 419280183Sdumbbell if (master->lock.hw_lock) { 420280183Sdumbbell if (dev->sigdata.lock == master->lock.hw_lock) 421280183Sdumbbell dev->sigdata.lock = NULL; 422280183Sdumbbell master->lock.hw_lock = NULL; 423280183Sdumbbell master->lock.file_priv = NULL; 424280183Sdumbbell DRM_WAKEUP_INT(&master->lock.lock_queue); 425280183Sdumbbell } 426280183Sdumbbell 427280183Sdumbbell if (file_priv->minor->master == file_priv->master) { 428280183Sdumbbell /* drop the reference held my the minor */ 429280183Sdumbbell if (dev->driver->master_drop) 430280183Sdumbbell dev->driver->master_drop(dev, file_priv, true); 431280183Sdumbbell drm_master_put(&file_priv->minor->master); 432280183Sdumbbell } 433280183Sdumbbell } 434280183Sdumbbell 435280183Sdumbbell /* drop the reference held my the file priv */ 436280183Sdumbbell drm_master_put(&file_priv->master); 437280183Sdumbbell file_priv->is_master = 0; 438280183Sdumbbell list_del(&file_priv->lhead); 439235783Skib DRM_UNLOCK(dev); 440239303Shselasky 441280183Sdumbbell if (dev->driver->postclose) 442280183Sdumbbell dev->driver->postclose(dev, file_priv); 443239303Shselasky 444280183Sdumbbell#ifdef FREEBSD_NOTYET 445280183Sdumbbell if (drm_core_check_feature(dev, DRIVER_PRIME)) 446280183Sdumbbell drm_prime_destroy_file_private(&file_priv->prime); 447280183Sdumbbell#endif /* FREEBSD_NOTYET */ 448280183Sdumbbell 449280183Sdumbbell free(file_priv, DRM_MEM_FILES); 450280183Sdumbbell 451280183Sdumbbell /* ======================================================== 452280183Sdumbbell * End inline drm_release 453280183Sdumbbell */ 454280183Sdumbbell 455280183Sdumbbell atomic_inc(&dev->counts[_DRM_STAT_CLOSES]); 456280183Sdumbbell mtx_lock(&Giant); 457280183Sdumbbell device_unbusy(dev->dev); 458280183Sdumbbell mtx_unlock(&Giant); 459280183Sdumbbell if (!--dev->open_count) { 460280183Sdumbbell if (atomic_read(&dev->ioctl_count)) { 461280183Sdumbbell DRM_ERROR("Device busy: %d\n", 462280183Sdumbbell atomic_read(&dev->ioctl_count)); 463280183Sdumbbell } else 464280183Sdumbbell drm_lastclose(dev); 465280183Sdumbbell } 466280183Sdumbbell sx_xunlock(&drm_global_mutex); 467235783Skib} 468280183SdumbbellEXPORT_SYMBOL(drm_release); 469235783Skib 470235783Skibstatic bool 471280183Sdumbbelldrm_dequeue_event(struct drm_file *file_priv, struct uio *uio, 472280183Sdumbbell struct drm_pending_event **out) 473235783Skib{ 474235783Skib struct drm_pending_event *e; 475280183Sdumbbell bool ret = false; 476235783Skib 477280183Sdumbbell /* Already locked in drm_read(). */ 478280183Sdumbbell /* DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags); */ 479280183Sdumbbell 480280183Sdumbbell *out = NULL; 481235783Skib if (list_empty(&file_priv->event_list)) 482280183Sdumbbell goto out; 483235783Skib e = list_first_entry(&file_priv->event_list, 484280183Sdumbbell struct drm_pending_event, link); 485235783Skib if (e->event->length > uio->uio_resid) 486280183Sdumbbell goto out; 487235783Skib 488235783Skib file_priv->event_space += e->event->length; 489235783Skib list_del(&e->link); 490235783Skib *out = e; 491280183Sdumbbell ret = true; 492280183Sdumbbell 493280183Sdumbbellout: 494280183Sdumbbell /* DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags); */ 495280183Sdumbbell return ret; 496235783Skib} 497235783Skib 498235783Skibint 499235783Skibdrm_read(struct cdev *kdev, struct uio *uio, int ioflag) 500235783Skib{ 501235783Skib struct drm_file *file_priv; 502235783Skib struct drm_device *dev; 503235783Skib struct drm_pending_event *e; 504280183Sdumbbell ssize_t error; 505235783Skib 506235783Skib error = devfs_get_cdevpriv((void **)&file_priv); 507235783Skib if (error != 0) { 508235783Skib DRM_ERROR("can't find authenticator\n"); 509235783Skib return (EINVAL); 510235783Skib } 511280183Sdumbbell 512235783Skib dev = drm_get_device_from_kdev(kdev); 513235783Skib mtx_lock(&dev->event_lock); 514235783Skib while (list_empty(&file_priv->event_list)) { 515235783Skib if ((ioflag & O_NONBLOCK) != 0) { 516235783Skib error = EAGAIN; 517235783Skib goto out; 518235783Skib } 519235783Skib error = msleep(&file_priv->event_space, &dev->event_lock, 520235783Skib PCATCH, "drmrea", 0); 521235783Skib if (error != 0) 522235783Skib goto out; 523235783Skib } 524280183Sdumbbell 525280183Sdumbbell while (drm_dequeue_event(file_priv, uio, &e)) { 526235783Skib mtx_unlock(&dev->event_lock); 527235783Skib error = uiomove(e->event, e->event->length, uio); 528235783Skib CTR3(KTR_DRM, "drm_event_dequeued %d %d %d", curproc->p_pid, 529235783Skib e->event->type, e->event->length); 530280183Sdumbbell 531235783Skib e->destroy(e); 532235783Skib if (error != 0) 533235783Skib return (error); 534235783Skib mtx_lock(&dev->event_lock); 535235783Skib } 536280183Sdumbbell 537235783Skibout: 538235783Skib mtx_unlock(&dev->event_lock); 539235783Skib return (error); 540235783Skib} 541280183SdumbbellEXPORT_SYMBOL(drm_read); 542235783Skib 543235783Skibvoid 544235783Skibdrm_event_wakeup(struct drm_pending_event *e) 545235783Skib{ 546235783Skib struct drm_file *file_priv; 547235783Skib struct drm_device *dev; 548235783Skib 549235783Skib file_priv = e->file_priv; 550280183Sdumbbell dev = file_priv->minor->dev; 551235783Skib mtx_assert(&dev->event_lock, MA_OWNED); 552235783Skib 553235783Skib wakeup(&file_priv->event_space); 554235783Skib selwakeup(&file_priv->event_poll); 555235783Skib} 556235783Skib 557235783Skibint 558235783Skibdrm_poll(struct cdev *kdev, int events, struct thread *td) 559235783Skib{ 560235783Skib struct drm_file *file_priv; 561235783Skib struct drm_device *dev; 562235783Skib int error, revents; 563235783Skib 564235783Skib error = devfs_get_cdevpriv((void **)&file_priv); 565235783Skib if (error != 0) { 566235783Skib DRM_ERROR("can't find authenticator\n"); 567235783Skib return (EINVAL); 568235783Skib } 569280183Sdumbbell 570235783Skib dev = drm_get_device_from_kdev(kdev); 571235783Skib 572235783Skib revents = 0; 573235783Skib mtx_lock(&dev->event_lock); 574235783Skib if ((events & (POLLIN | POLLRDNORM)) != 0) { 575235783Skib if (list_empty(&file_priv->event_list)) { 576235783Skib CTR0(KTR_DRM, "drm_poll empty list"); 577235783Skib selrecord(td, &file_priv->event_poll); 578235783Skib } else { 579235783Skib revents |= events & (POLLIN | POLLRDNORM); 580235783Skib CTR1(KTR_DRM, "drm_poll revents %x", revents); 581235783Skib } 582235783Skib } 583235783Skib mtx_unlock(&dev->event_lock); 584235783Skib return (revents); 585235783Skib} 586280183SdumbbellEXPORT_SYMBOL(drm_poll); 587280183Sdumbbell 588280183Sdumbbellint 589280183Sdumbbelldrm_mmap_single(struct cdev *kdev, vm_ooffset_t *offset, vm_size_t size, 590280183Sdumbbell struct vm_object **obj_res, int nprot) 591280183Sdumbbell{ 592280183Sdumbbell struct drm_device *dev; 593280183Sdumbbell 594280183Sdumbbell dev = drm_get_device_from_kdev(kdev); 595280183Sdumbbell if (dev->drm_ttm_bdev != NULL) { 596280183Sdumbbell return (-ttm_bo_mmap_single(dev->drm_ttm_bdev, offset, size, 597280183Sdumbbell obj_res, nprot)); 598280183Sdumbbell } else if ((dev->driver->driver_features & DRIVER_GEM) != 0) { 599280183Sdumbbell return (-drm_gem_mmap_single(dev, offset, size, obj_res, nprot)); 600280183Sdumbbell } else { 601280183Sdumbbell return (ENODEV); 602280183Sdumbbell } 603280183Sdumbbell} 604