2005-04-17 02:20:36 +04:00
/**
2005-09-25 08:28:13 +04:00
* \ file drm_fops . c
2005-04-17 02:20:36 +04:00
* File operations for DRM
2005-09-25 08:28:13 +04:00
*
2005-04-17 02:20:36 +04:00
* \ author Rickard E . ( Rik ) Faith < faith @ valinux . com >
* \ author Daryll Strauss < daryll @ valinux . com >
* \ author Gareth Hughes < gareth @ valinux . com >
*/
/*
* Created : Mon Jan 4 08 : 58 : 31 1999 by faith @ valinux . com
*
* Copyright 1999 Precision Insight , Inc . , Cedar Park , Texas .
* Copyright 2000 VA Linux Systems , Inc . , Sunnyvale , California .
* All Rights Reserved .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice ( including the next
* paragraph ) shall be included in all copies or substantial portions of the
* Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* VA LINUX SYSTEMS AND / OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM , DAMAGES OR
* OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE ,
* ARISING FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE .
*/
# include "drmP.h"
2006-01-02 13:32:48 +03:00
# include "drm_sarea.h"
2005-04-17 02:20:36 +04:00
# include <linux/poll.h>
2005-09-25 08:28:13 +04:00
static int drm_open_helper ( struct inode * inode , struct file * filp ,
2007-07-11 09:53:27 +04:00
struct drm_device * dev ) ;
2005-07-07 15:03:38 +04:00
2007-07-11 09:53:27 +04:00
static int drm_setup ( struct drm_device * dev )
2005-04-17 02:20:36 +04:00
{
2006-01-02 13:32:48 +03:00
drm_local_map_t * map ;
2005-04-17 02:20:36 +04:00
int i ;
int ret ;
2007-02-18 09:13:39 +03:00
u32 sareapage ;
2005-04-17 02:20:36 +04:00
2005-11-10 14:16:34 +03:00
if ( dev - > driver - > firstopen ) {
ret = dev - > driver - > firstopen ( dev ) ;
2005-09-25 08:28:13 +04:00
if ( ret ! = 0 )
2005-04-17 02:20:36 +04:00
return ret ;
}
2006-08-18 10:37:10 +04:00
dev - > magicfree . next = NULL ;
2006-01-02 13:32:48 +03:00
/* prebuild the SAREA */
2007-03-19 01:08:21 +03:00
sareapage = max_t ( unsigned , SAREA_MAX , PAGE_SIZE ) ;
2007-02-18 09:13:39 +03:00
i = drm_addmap ( dev , 0 , sareapage , _DRM_SHM , _DRM_CONTAINS_LOCK , & map ) ;
2006-01-02 13:32:48 +03:00
if ( i ! = 0 )
return i ;
2005-09-25 08:28:13 +04:00
atomic_set ( & dev - > ioctl_count , 0 ) ;
atomic_set ( & dev - > vma_count , 0 ) ;
2005-04-17 02:20:36 +04:00
dev - > buf_use = 0 ;
2005-09-25 08:28:13 +04:00
atomic_set ( & dev - > buf_alloc , 0 ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
if ( drm_core_check_feature ( dev , DRIVER_HAVE_DMA ) ) {
i = drm_dma_setup ( dev ) ;
if ( i < 0 )
2005-04-17 02:20:36 +04:00
return i ;
}
2006-08-07 14:07:43 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( dev - > counts ) ; i + + )
2005-09-25 08:28:13 +04:00
atomic_set ( & dev - > counts [ i ] , 0 ) ;
2005-04-17 02:20:36 +04:00
2006-08-07 16:22:10 +04:00
drm_ht_create ( & dev - > magiclist , DRM_MAGIC_HASH_ORDER ) ;
INIT_LIST_HEAD ( & dev - > magicfree ) ;
2005-04-17 02:20:36 +04:00
2007-02-18 09:13:39 +03:00
dev - > sigdata . lock = NULL ;
2005-09-25 08:28:13 +04:00
init_waitqueue_head ( & dev - > lock . lock_queue ) ;
2005-04-17 02:20:36 +04:00
dev - > queue_count = 0 ;
dev - > queue_reserved = 0 ;
dev - > queue_slots = 0 ;
dev - > queuelist = NULL ;
dev - > irq_enabled = 0 ;
dev - > context_flag = 0 ;
dev - > interrupt_flag = 0 ;
dev - > dma_flag = 0 ;
dev - > last_context = 0 ;
dev - > last_switch = 0 ;
dev - > last_checked = 0 ;
2005-09-25 08:28:13 +04:00
init_waitqueue_head ( & dev - > context_wait ) ;
2005-04-17 02:20:36 +04:00
dev - > if_version = 0 ;
dev - > ctx_start = 0 ;
dev - > lck_start = 0 ;
dev - > buf_async = NULL ;
2005-09-25 08:28:13 +04:00
init_waitqueue_head ( & dev - > buf_readers ) ;
init_waitqueue_head ( & dev - > buf_writers ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
DRM_DEBUG ( " \n " ) ;
2005-04-17 02:20:36 +04:00
/*
* The kernel ' s context could be created here , but is now created
2005-09-25 08:28:13 +04:00
* in drm_dma_enqueue . This is more resource - efficient for
2005-04-17 02:20:36 +04:00
* 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 .
*/
return 0 ;
}
/**
* Open file .
2005-09-25 08:28:13 +04:00
*
2005-04-17 02:20:36 +04:00
* \ param inode device inode
* \ param filp file pointer .
* \ return zero on success or a negative number on failure .
*
* Searches the DRM device with the same minor number , calls open_helper ( ) , and
* 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 ( ) .
*/
2005-09-25 08:28:13 +04:00
int drm_open ( struct inode * inode , struct file * filp )
2005-04-17 02:20:36 +04:00
{
2007-07-11 09:53:27 +04:00
struct drm_device * dev = NULL ;
2005-04-17 02:20:36 +04:00
int minor = iminor ( inode ) ;
int retcode = 0 ;
if ( ! ( ( minor > = 0 ) & & ( minor < drm_cards_limit ) ) )
return - ENODEV ;
2005-09-25 08:28:13 +04:00
2005-04-17 02:20:36 +04:00
if ( ! drm_heads [ minor ] )
return - ENODEV ;
if ( ! ( dev = drm_heads [ minor ] - > dev ) )
return - ENODEV ;
2005-09-25 08:28:13 +04:00
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 ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-25 08:28:13 +04:00
spin_unlock ( & dev - > count_lock ) ;
2005-04-17 02:20:36 +04:00
}
return retcode ;
}
EXPORT_SYMBOL ( drm_open ) ;
2006-01-02 13:32:48 +03:00
/**
* 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 )
{
2007-07-11 09:53:27 +04:00
struct drm_device * dev = NULL ;
2006-01-02 13:32:48 +03:00
int minor = iminor ( inode ) ;
int err = - ENODEV ;
2006-03-28 13:56:41 +04:00
const struct file_operations * old_fops ;
2006-01-02 13:32:48 +03:00
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 ,
2007-07-11 09:53:27 +04:00
struct drm_device * dev )
2006-01-02 13:32:48 +03:00
{
int minor = iminor ( inode ) ;
2007-07-11 09:53:27 +04:00
struct drm_file * priv ;
2006-01-02 13:32:48 +03:00
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 ;
2007-05-25 23:01:51 +04:00
INIT_LIST_HEAD ( & priv - > lhead ) ;
2006-01-02 13:32:48 +03:00
if ( dev - > driver - > open ) {
ret = dev - > driver - > open ( dev , priv ) ;
if ( ret < 0 )
goto out_free ;
}
2006-02-02 11:37:46 +03:00
mutex_lock ( & dev - > struct_mutex ) ;
2007-05-25 23:01:51 +04:00
if ( list_empty ( & dev - > filelist ) )
2006-01-02 13:32:48 +03:00
priv - > master = 1 ;
2007-05-25 23:01:51 +04:00
list_add ( & priv - > lhead , & dev - > filelist ) ;
2006-02-02 11:37:46 +03:00
mutex_unlock ( & dev - > struct_mutex ) ;
2006-01-02 13:32:48 +03:00
# 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 )
{
2007-07-11 09:53:27 +04:00
struct drm_file * priv = filp - > private_data ;
struct drm_device * dev = priv - > head - > dev ;
2006-01-02 13:32:48 +03:00
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 ) ;
2005-04-17 02:20:36 +04:00
/**
* Release file .
*
* \ param inode device inode
* \ param filp file pointer .
* \ return zero on success or a negative number on failure .
*
* 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
2005-11-10 14:16:34 +03:00
* zero calls drm_lastclose ( ) .
2005-04-17 02:20:36 +04:00
*/
2005-09-25 08:28:13 +04:00
int drm_release ( struct inode * inode , struct file * filp )
2005-04-17 02:20:36 +04:00
{
2007-07-11 09:53:27 +04:00
struct drm_file * priv = filp - > private_data ;
struct drm_device * dev ;
2005-04-17 02:20:36 +04:00
int retcode = 0 ;
lock_kernel ( ) ;
dev = priv - > head - > dev ;
2005-09-25 08:28:13 +04:00
DRM_DEBUG ( " open_count = %d \n " , dev - > open_count ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 14:16:34 +03:00
if ( dev - > driver - > preclose )
dev - > driver - > preclose ( dev , filp ) ;
2005-04-17 02:20:36 +04:00
/* ========================================================
* Begin inline drm_release
*/
2005-09-25 08:28:13 +04:00
DRM_DEBUG ( " pid = %d, device = 0x%lx, open_count = %d \n " ,
current - > pid , ( long ) old_encode_dev ( priv - > head - > device ) ,
dev - > open_count ) ;
2007-03-23 05:28:33 +03:00
if ( dev - > driver - > reclaim_buffers_locked & & dev - > lock . hw_lock ) {
if ( drm_i_have_hw_lock ( filp ) ) {
2005-11-10 14:16:34 +03:00
dev - > driver - > reclaim_buffers_locked ( dev , filp ) ;
2007-03-23 05:28:33 +03:00
} else {
unsigned long _end = jiffies + 3 * DRM_HZ ;
int locked = 0 ;
drm_idlelock_take ( & dev - > lock ) ;
/*
* Wait for a while .
*/
do {
spin_lock ( & dev - > lock . spinlock ) ;
locked = dev - > lock . idle_has_lock ;
spin_unlock ( & dev - > lock . spinlock ) ;
if ( locked )
break ;
schedule ( ) ;
} while ( ! time_after_eq ( jiffies , _end ) ) ;
if ( ! locked ) {
DRM_ERROR ( " reclaim_buffers_locked() deadlock. Please rework this \n "
" \t driver to use reclaim_buffers_idlelocked() instead. \n "
" \t I will go on reclaiming the buffers anyway. \n " ) ;
2005-04-17 02:20:36 +04:00
}
2007-03-23 05:28:33 +03:00
2005-11-10 14:16:34 +03:00
dev - > driver - > reclaim_buffers_locked ( dev , filp ) ;
2007-03-23 05:28:33 +03:00
drm_idlelock_release ( & dev - > lock ) ;
2005-04-17 02:20:36 +04:00
}
}
2005-09-25 08:28:13 +04:00
2007-03-23 05:28:33 +03:00
if ( dev - > driver - > reclaim_buffers_idlelocked & & dev - > lock . hw_lock ) {
drm_idlelock_take ( & dev - > lock ) ;
dev - > driver - > reclaim_buffers_idlelocked ( dev , filp ) ;
drm_idlelock_release ( & dev - > lock ) ;
}
if ( drm_i_have_hw_lock ( filp ) ) {
DRM_DEBUG ( " File %p released, freeing lock for context %d \n " ,
filp , _DRM_LOCKING_CONTEXT ( dev - > lock . hw_lock - > lock ) ) ;
drm_lock_free ( & dev - > lock ,
_DRM_LOCKING_CONTEXT ( dev - > lock . hw_lock - > lock ) ) ;
}
2005-11-10 14:16:34 +03:00
if ( drm_core_check_feature ( dev , DRIVER_HAVE_DMA ) & &
! dev - > driver - > reclaim_buffers_locked ) {
2005-04-17 02:20:36 +04:00
dev - > driver - > reclaim_buffers ( dev , filp ) ;
}
2005-09-25 08:28:13 +04:00
drm_fasync ( - 1 , filp , 0 ) ;
2005-04-17 02:20:36 +04:00
2006-02-02 11:37:46 +03:00
mutex_lock ( & dev - > ctxlist_mutex ) ;
2007-05-25 23:01:51 +04:00
if ( ! list_empty ( & dev - > ctxlist ) ) {
2007-07-11 10:53:40 +04:00
struct drm_ctx_list * pos , * n ;
2005-04-17 02:20:36 +04:00
2007-05-25 23:01:51 +04:00
list_for_each_entry_safe ( pos , n , & dev - > ctxlist , head ) {
2005-09-25 08:28:13 +04:00
if ( pos - > tag = = priv & &
pos - > handle ! = DRM_KERNEL_CONTEXT ) {
2005-04-17 02:20:36 +04:00
if ( dev - > driver - > context_dtor )
2005-09-25 08:28:13 +04:00
dev - > driver - > context_dtor ( dev ,
pos - > handle ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
drm_ctxbitmap_free ( dev , pos - > handle ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
list_del ( & pos - > head ) ;
drm_free ( pos , sizeof ( * pos ) , DRM_MEM_CTXLIST ) ;
2005-04-17 02:20:36 +04:00
- - dev - > ctx_count ;
}
}
}
2006-02-02 11:37:46 +03:00
mutex_unlock ( & dev - > ctxlist_mutex ) ;
2005-04-17 02:20:36 +04:00
2006-02-02 11:37:46 +03:00
mutex_lock ( & dev - > struct_mutex ) ;
2005-09-25 08:28:13 +04:00
if ( priv - > remove_auth_on_close = = 1 ) {
2007-07-11 09:53:27 +04:00
struct drm_file * temp ;
2007-05-25 23:01:51 +04:00
list_for_each_entry ( temp , & dev - > filelist , lhead )
2005-04-17 02:20:36 +04:00
temp - > authenticated = 0 ;
}
2007-05-25 23:01:51 +04:00
list_del ( & priv - > lhead ) ;
2006-02-02 11:37:46 +03:00
mutex_unlock ( & dev - > struct_mutex ) ;
2005-09-25 08:28:13 +04:00
2005-11-10 14:16:34 +03:00
if ( dev - > driver - > postclose )
dev - > driver - > postclose ( dev , priv ) ;
2005-09-25 08:28:13 +04:00
drm_free ( priv , sizeof ( * priv ) , DRM_MEM_FILES ) ;
2005-04-17 02:20:36 +04:00
/* ========================================================
* End inline drm_release
*/
2005-09-25 08:28:13 +04:00
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 ) ;
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return - EBUSY ;
}
2005-09-25 08:28:13 +04:00
spin_unlock ( & dev - > count_lock ) ;
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
2005-11-10 14:16:34 +03:00
return drm_lastclose ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-25 08:28:13 +04:00
spin_unlock ( & dev - > count_lock ) ;
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
return retcode ;
}
EXPORT_SYMBOL ( drm_release ) ;
/** No-op. */
unsigned int drm_poll ( struct file * filp , struct poll_table_struct * wait )
{
return 0 ;
}
2005-09-25 08:28:13 +04:00
EXPORT_SYMBOL ( drm_poll ) ;