X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fchar%2Fdrm%2Fdrm_fops.c;fp=drivers%2Fchar%2Fdrm%2Fdrm_fops.c;h=b7f7951c458721d8b3517c49dd5b4531bfa6f3be;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=906794247aeb8cfe4343b99416df7ec3dcd375a3;hpb=cee37fe97739d85991964371c1f3a745c00dd236;p=linux-2.6.git diff --git a/drivers/char/drm/drm_fops.c b/drivers/char/drm/drm_fops.c index 906794247..b7f7951c4 100644 --- a/drivers/char/drm/drm_fops.c +++ b/drivers/char/drm/drm_fops.c @@ -1,7 +1,7 @@ /** - * \file drm_fops.h + * \file drm_fops.c * File operations for DRM - * + * * \author Rickard E. (Rik) Faith * \author Daryll Strauss * \author Gareth Hughes @@ -35,55 +35,57 @@ */ #include "drmP.h" +#include "drm_sarea.h" #include -static int drm_setup( drm_device_t *dev ) +static int drm_open_helper(struct inode *inode, struct file *filp, + drm_device_t * dev); + +static int drm_setup(drm_device_t * dev) { + drm_local_map_t *map; int i; int ret; - if (dev->driver->presetup) - { - ret=dev->driver->presetup(dev); - if (ret!=0) + if (dev->driver->firstopen) { + ret = dev->driver->firstopen(dev); + if (ret != 0) return ret; } - atomic_set( &dev->ioctl_count, 0 ); - atomic_set( &dev->vma_count, 0 ); + /* prebuild the SAREA */ + i = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM, _DRM_CONTAINS_LOCK, &map); + if (i != 0) + return i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); dev->buf_use = 0; - atomic_set( &dev->buf_alloc, 0 ); + atomic_set(&dev->buf_alloc, 0); - if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) - { - i = drm_dma_setup( dev ); - if ( i < 0 ) + if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) { + i = drm_dma_setup(dev); + if (i < 0) return i; } - for ( i = 0 ; i < DRM_ARRAY_SIZE(dev->counts) ; i++ ) - atomic_set( &dev->counts[i], 0 ); + for (i = 0; i < DRM_ARRAY_SIZE(dev->counts); i++) + atomic_set(&dev->counts[i], 0); - for ( i = 0 ; i < DRM_HASH_SIZE ; i++ ) { + for (i = 0; i < DRM_HASH_SIZE; i++) { dev->magiclist[i].head = NULL; dev->magiclist[i].tail = NULL; } - dev->maplist = drm_alloc(sizeof(*dev->maplist), - DRM_MEM_MAPS); - if(dev->maplist == NULL) return -ENOMEM; - memset(dev->maplist, 0, sizeof(*dev->maplist)); - INIT_LIST_HEAD(&dev->maplist->head); - - dev->ctxlist = drm_alloc(sizeof(*dev->ctxlist), - DRM_MEM_CTXLIST); - if(dev->ctxlist == NULL) return -ENOMEM; + dev->ctxlist = drm_alloc(sizeof(*dev->ctxlist), DRM_MEM_CTXLIST); + if (dev->ctxlist == NULL) + return -ENOMEM; memset(dev->ctxlist, 0, sizeof(*dev->ctxlist)); INIT_LIST_HEAD(&dev->ctxlist->head); dev->vmalist = NULL; dev->sigdata.lock = dev->lock.hw_lock = NULL; - init_waitqueue_head( &dev->lock.lock_queue ); + init_waitqueue_head(&dev->lock.lock_queue); dev->queue_count = 0; dev->queue_reserved = 0; dev->queue_slots = 0; @@ -95,37 +97,32 @@ static int drm_setup( drm_device_t *dev ) dev->last_context = 0; dev->last_switch = 0; dev->last_checked = 0; - init_waitqueue_head( &dev->context_wait ); + init_waitqueue_head(&dev->context_wait); dev->if_version = 0; dev->ctx_start = 0; dev->lck_start = 0; - dev->buf_rp = dev->buf; - dev->buf_wp = dev->buf; - dev->buf_end = dev->buf + DRM_BSZ; dev->buf_async = NULL; - init_waitqueue_head( &dev->buf_readers ); - init_waitqueue_head( &dev->buf_writers ); + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); - DRM_DEBUG( "\n" ); + DRM_DEBUG("\n"); /* * The kernel's context could be created here, but is now created - * in drm_dma_enqueue. This is more resource-efficient for + * in drm_dma_enqueue. This is more resource-efficient for * hardware that does not do DMA, but may mean that * drm_select_queue fails between the time the interrupt is * initialized and the time the queues are initialized. */ - if (dev->driver->postsetup) - dev->driver->postsetup(dev); return 0; } /** * Open file. - * + * * \param inode device inode * \param filp file pointer. * \return zero on success or a negative number on failure. @@ -134,7 +131,7 @@ static int drm_setup( drm_device_t *dev ) * increments the device open count. If the open count was previous at zero, * i.e., it's the first that the device is open, then calls setup(). */ -int drm_open( struct inode *inode, struct file *filp ) +int drm_open(struct inode *inode, struct file *filp) { drm_device_t *dev = NULL; int minor = iminor(inode); @@ -142,28 +139,187 @@ int drm_open( struct inode *inode, struct file *filp ) if (!((minor >= 0) && (minor < drm_cards_limit))) return -ENODEV; - + if (!drm_heads[minor]) return -ENODEV; if (!(dev = drm_heads[minor]->dev)) return -ENODEV; - - retcode = drm_open_helper( inode, filp, dev ); - if ( !retcode ) { - atomic_inc( &dev->counts[_DRM_STAT_OPENS] ); - spin_lock( &dev->count_lock ); - if ( !dev->open_count++ ) { - spin_unlock( &dev->count_lock ); - return drm_setup( dev ); + + retcode = drm_open_helper(inode, filp, dev); + if (!retcode) { + atomic_inc(&dev->counts[_DRM_STAT_OPENS]); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return drm_setup(dev); } - spin_unlock( &dev->count_lock ); + spin_unlock(&dev->count_lock); } return retcode; } EXPORT_SYMBOL(drm_open); +/** + * File \c open operation. + * + * \param inode device inode. + * \param filp file pointer. + * + * Puts the dev->fops corresponding to the device minor number into + * \p filp, call the \c open method, and restore the file operations. + */ +int drm_stub_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev = NULL; + int minor = iminor(inode); + int err = -ENODEV; + const struct file_operations *old_fops; + + DRM_DEBUG("\n"); + + if (!((minor >= 0) && (minor < drm_cards_limit))) + return -ENODEV; + + if (!drm_heads[minor]) + return -ENODEV; + + if (!(dev = drm_heads[minor]->dev)) + return -ENODEV; + + old_fops = filp->f_op; + filp->f_op = fops_get(&dev->driver->fops); + if (filp->f_op->open && (err = filp->f_op->open(inode, filp))) { + fops_put(filp->f_op); + filp->f_op = fops_get(old_fops); + } + fops_put(old_fops); + + return err; +} + +/** + * Check whether DRI will run on this CPU. + * + * \return non-zero if the DRI will run on this CPU, or zero otherwise. + */ +static int drm_cpu_valid(void) +{ +#if defined(__i386__) + if (boot_cpu_data.x86 == 3) + return 0; /* No cmpxchg on a 386 */ +#endif +#if defined(__sparc__) && !defined(__sparc_v9__) + return 0; /* No cmpxchg before v9 sparc. */ +#endif + return 1; +} + +/** + * Called whenever a process opens /dev/drm. + * + * \param inode device inode. + * \param filp file pointer. + * \param dev device. + * \return zero on success or a negative number on failure. + * + * Creates and initializes a drm_file structure for the file private data in \p + * filp and add it into the double linked list in \p dev. + */ +static int drm_open_helper(struct inode *inode, struct file *filp, + drm_device_t * dev) +{ + int minor = iminor(inode); + drm_file_t *priv; + int ret; + + if (filp->f_flags & O_EXCL) + return -EBUSY; /* No exclusive opens */ + if (!drm_cpu_valid()) + return -EINVAL; + + DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor); + + priv = drm_alloc(sizeof(*priv), DRM_MEM_FILES); + if (!priv) + return -ENOMEM; + + memset(priv, 0, sizeof(*priv)); + filp->private_data = priv; + priv->uid = current->euid; + priv->pid = current->pid; + priv->minor = minor; + priv->head = drm_heads[minor]; + priv->ioctl_count = 0; + /* for compatibility root is always authenticated */ + priv->authenticated = capable(CAP_SYS_ADMIN); + priv->lock_count = 0; + + if (dev->driver->open) { + ret = dev->driver->open(dev, priv); + if (ret < 0) + goto out_free; + } + + mutex_lock(&dev->struct_mutex); + if (!dev->file_last) { + priv->next = NULL; + priv->prev = NULL; + dev->file_first = priv; + dev->file_last = priv; + /* first opener automatically becomes master */ + priv->master = 1; + } else { + priv->next = NULL; + priv->prev = dev->file_last; + dev->file_last->next = priv; + dev->file_last = priv; + } + mutex_unlock(&dev->struct_mutex); + +#ifdef __alpha__ + /* + * Default the hose + */ + if (!dev->hose) { + struct pci_dev *pci_dev; + pci_dev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL); + if (pci_dev) { + dev->hose = pci_dev->sysdata; + pci_dev_put(pci_dev); + } + if (!dev->hose) { + struct pci_bus *b = pci_bus_b(pci_root_buses.next); + if (b) + dev->hose = b->sysdata; + } + } +#endif + + return 0; + out_free: + drm_free(priv, sizeof(*priv), DRM_MEM_FILES); + filp->private_data = NULL; + return ret; +} + +/** No-op. */ +int drm_fasync(int fd, struct file *filp, int on) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + int retcode; + + DRM_DEBUG("fd = %d, device = 0x%lx\n", fd, + (long)old_encode_dev(priv->head->device)); + retcode = fasync_helper(fd, filp, on, &dev->buf_async); + if (retcode < 0) + return retcode; + return 0; +} +EXPORT_SYMBOL(drm_fasync); + /** * Release file. * @@ -174,9 +330,9 @@ EXPORT_SYMBOL(drm_open); * If the hardware lock is held then free it, and take it again for the kernel * context since it's necessary to reclaim buffers. Unlink the file private * data from its list and free it. Decreases the open count and if it reaches - * zero calls takedown(). + * zero calls drm_lastclose(). */ -int drm_release( struct inode *inode, struct file *filp ) +int drm_release(struct inode *inode, struct file *filp) { drm_file_t *priv = filp->private_data; drm_device_t *dev; @@ -185,144 +341,142 @@ int drm_release( struct inode *inode, struct file *filp ) lock_kernel(); dev = priv->head->dev; - DRM_DEBUG( "open_count = %d\n", dev->open_count ); + DRM_DEBUG("open_count = %d\n", dev->open_count); - if (dev->driver->prerelease) - dev->driver->prerelease(dev, filp); + if (dev->driver->preclose) + dev->driver->preclose(dev, filp); /* ======================================================== * Begin inline drm_release */ - DRM_DEBUG( "pid = %d, device = 0x%lx, open_count = %d\n", - current->pid, (long)old_encode_dev(priv->head->device), dev->open_count ); - - if ( priv->lock_count && dev->lock.hw_lock && - _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) && - dev->lock.filp == filp ) { - DRM_DEBUG( "File %p released, freeing lock for context %d\n", - filp, - _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) ); - - if (dev->driver->release) - dev->driver->release(dev, filp); - - drm_lock_free( dev, &dev->lock.hw_lock->lock, - _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) ); - - /* FIXME: may require heavy-handed reset of - hardware at this point, possibly - processed via a callback to the X - server. */ - } - else if ( dev->driver->release && priv->lock_count && dev->lock.hw_lock ) { + DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", + current->pid, (long)old_encode_dev(priv->head->device), + dev->open_count); + + if (priv->lock_count && dev->lock.hw_lock && + _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) && + dev->lock.filp == filp) { + DRM_DEBUG("File %p released, freeing lock for context %d\n", + filp, _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + + if (dev->driver->reclaim_buffers_locked) + dev->driver->reclaim_buffers_locked(dev, filp); + + drm_lock_free(dev, &dev->lock.hw_lock->lock, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + + /* FIXME: may require heavy-handed reset of + hardware at this point, possibly + processed via a callback to the X + server. */ + } else if (dev->driver->reclaim_buffers_locked && priv->lock_count + && dev->lock.hw_lock) { /* The lock is required to reclaim buffers */ - DECLARE_WAITQUEUE( entry, current ); + DECLARE_WAITQUEUE(entry, current); - add_wait_queue( &dev->lock.lock_queue, &entry ); + add_wait_queue(&dev->lock.lock_queue, &entry); for (;;) { __set_current_state(TASK_INTERRUPTIBLE); - if ( !dev->lock.hw_lock ) { + if (!dev->lock.hw_lock) { /* Device has been unregistered */ retcode = -EINTR; break; } - if ( drm_lock_take( &dev->lock.hw_lock->lock, - DRM_KERNEL_CONTEXT ) ) { - dev->lock.filp = filp; + if (drm_lock_take(&dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + dev->lock.filp = filp; dev->lock.lock_time = jiffies; - atomic_inc( &dev->counts[_DRM_STAT_LOCKS] ); + atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); break; /* Got lock */ } - /* Contention */ + /* Contention */ schedule(); - if ( signal_pending( current ) ) { + if (signal_pending(current)) { retcode = -ERESTARTSYS; break; } } __set_current_state(TASK_RUNNING); - remove_wait_queue( &dev->lock.lock_queue, &entry ); - if( !retcode ) { - if (dev->driver->release) - dev->driver->release(dev, filp); - drm_lock_free( dev, &dev->lock.hw_lock->lock, - DRM_KERNEL_CONTEXT ); + remove_wait_queue(&dev->lock.lock_queue, &entry); + if (!retcode) { + dev->driver->reclaim_buffers_locked(dev, filp); + drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT); } } - - if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) - { + + if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && + !dev->driver->reclaim_buffers_locked) { dev->driver->reclaim_buffers(dev, filp); } - drm_fasync( -1, filp, 0 ); + drm_fasync(-1, filp, 0); - down( &dev->ctxlist_sem ); - if ( !list_empty( &dev->ctxlist->head ) ) { + mutex_lock(&dev->ctxlist_mutex); + if (dev->ctxlist && (!list_empty(&dev->ctxlist->head))) { drm_ctx_list_t *pos, *n; - list_for_each_entry_safe( pos, n, &dev->ctxlist->head, head ) { - if ( pos->tag == priv && - pos->handle != DRM_KERNEL_CONTEXT ) { + list_for_each_entry_safe(pos, n, &dev->ctxlist->head, head) { + if (pos->tag == priv && + pos->handle != DRM_KERNEL_CONTEXT) { if (dev->driver->context_dtor) - dev->driver->context_dtor(dev, pos->handle); + dev->driver->context_dtor(dev, + pos->handle); - drm_ctxbitmap_free( dev, pos->handle ); + drm_ctxbitmap_free(dev, pos->handle); - list_del( &pos->head ); - drm_free( pos, sizeof(*pos), DRM_MEM_CTXLIST ); + list_del(&pos->head); + drm_free(pos, sizeof(*pos), DRM_MEM_CTXLIST); --dev->ctx_count; } } } - up( &dev->ctxlist_sem ); + mutex_unlock(&dev->ctxlist_mutex); - down( &dev->struct_sem ); - if ( priv->remove_auth_on_close == 1 ) { + mutex_lock(&dev->struct_mutex); + if (priv->remove_auth_on_close == 1) { drm_file_t *temp = dev->file_first; - while ( temp ) { + while (temp) { temp->authenticated = 0; temp = temp->next; } } - if ( priv->prev ) { + if (priv->prev) { priv->prev->next = priv->next; } else { - dev->file_first = priv->next; + dev->file_first = priv->next; } - if ( priv->next ) { + if (priv->next) { priv->next->prev = priv->prev; } else { - dev->file_last = priv->prev; + dev->file_last = priv->prev; } - up( &dev->struct_sem ); - - if (dev->driver->free_filp_priv) - dev->driver->free_filp_priv(dev, priv); + mutex_unlock(&dev->struct_mutex); - drm_free( priv, sizeof(*priv), DRM_MEM_FILES ); + if (dev->driver->postclose) + dev->driver->postclose(dev, priv); + drm_free(priv, sizeof(*priv), DRM_MEM_FILES); /* ======================================================== * End inline drm_release */ - atomic_inc( &dev->counts[_DRM_STAT_CLOSES] ); - spin_lock( &dev->count_lock ); - if ( !--dev->open_count ) { - if ( atomic_read( &dev->ioctl_count ) || dev->blocked ) { - DRM_ERROR( "Device busy: %d %d\n", - atomic_read( &dev->ioctl_count ), - dev->blocked ); - spin_unlock( &dev->count_lock ); + atomic_inc(&dev->counts[_DRM_STAT_CLOSES]); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), dev->blocked); + spin_unlock(&dev->count_lock); unlock_kernel(); return -EBUSY; } - spin_unlock( &dev->count_lock ); + spin_unlock(&dev->count_lock); unlock_kernel(); - return drm_takedown( dev ); + return drm_lastclose(dev); } - spin_unlock( &dev->count_lock ); + spin_unlock(&dev->count_lock); unlock_kernel(); @@ -330,122 +484,9 @@ int drm_release( struct inode *inode, struct file *filp ) } EXPORT_SYMBOL(drm_release); -/** - * Called whenever a process opens /dev/drm. - * - * \param inode device inode. - * \param filp file pointer. - * \param dev device. - * \return zero on success or a negative number on failure. - * - * Creates and initializes a drm_file structure for the file private data in \p - * filp and add it into the double linked list in \p dev. - */ -int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t *dev) -{ - int minor = iminor(inode); - drm_file_t *priv; - int ret; - - if (filp->f_flags & O_EXCL) return -EBUSY; /* No exclusive opens */ - if (!drm_cpu_valid()) return -EINVAL; - - DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor); - - priv = drm_alloc(sizeof(*priv), DRM_MEM_FILES); - if(!priv) return -ENOMEM; - - memset(priv, 0, sizeof(*priv)); - filp->private_data = priv; - priv->uid = current->euid; - priv->pid = current->pid; - priv->minor = minor; - priv->head = drm_heads[minor]; - priv->ioctl_count = 0; - priv->authenticated = capable(CAP_SYS_ADMIN); - priv->lock_count = 0; - - if (dev->driver->open_helper) { - ret=dev->driver->open_helper(dev, priv); - if (ret < 0) - goto out_free; - } - - down(&dev->struct_sem); - if (!dev->file_last) { - priv->next = NULL; - priv->prev = NULL; - dev->file_first = priv; - dev->file_last = priv; - } else { - priv->next = NULL; - priv->prev = dev->file_last; - dev->file_last->next = priv; - dev->file_last = priv; - } - up(&dev->struct_sem); - -#ifdef __alpha__ - /* - * Default the hose - */ - if (!dev->hose) { - struct pci_dev *pci_dev; - pci_dev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL); - if (pci_dev) { - dev->hose = pci_dev->sysdata; - pci_dev_put(pci_dev); - } - if (!dev->hose) { - struct pci_bus *b = pci_bus_b(pci_root_buses.next); - if (b) dev->hose = b->sysdata; - } - } -#endif - - return 0; -out_free: - drm_free(priv, sizeof(*priv), DRM_MEM_FILES); - filp->private_data=NULL; - return ret; -} - -/** No-op. */ -int drm_flush(struct file *filp) -{ - drm_file_t *priv = filp->private_data; - drm_device_t *dev = priv->head->dev; - - DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", - current->pid, (long)old_encode_dev(priv->head->device), dev->open_count); - return 0; -} -EXPORT_SYMBOL(drm_flush); - -/** No-op. */ -int drm_fasync(int fd, struct file *filp, int on) -{ - drm_file_t *priv = filp->private_data; - drm_device_t *dev = priv->head->dev; - int retcode; - - DRM_DEBUG("fd = %d, device = 0x%lx\n", fd, (long)old_encode_dev(priv->head->device)); - retcode = fasync_helper(fd, filp, on, &dev->buf_async); - if (retcode < 0) return retcode; - return 0; -} -EXPORT_SYMBOL(drm_fasync); - /** No-op. */ unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) { return 0; } EXPORT_SYMBOL(drm_poll); - - -/** No-op. */ -ssize_t drm_read(struct file *filp, char __user *buf, size_t count, loff_t *off) -{ - return 0; -}