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 outisde 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 } 604