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