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 ,
drm_device_t * dev ) ;
2005-07-07 15:03:38 +04:00
2005-09-25 08:28:13 +04:00
static int drm_setup ( drm_device_t * 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 ;
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-01-02 13:32:48 +03:00
/* prebuild the SAREA */
i = drm_addmap ( dev , 0 , SAREA_MAX , _DRM_SHM , _DRM_CONTAINS_LOCK , & map ) ;
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
2005-09-25 08:28:13 +04:00
dev - > ctxlist = drm_alloc ( sizeof ( * dev - > ctxlist ) , DRM_MEM_CTXLIST ) ;
if ( dev - > ctxlist = = NULL )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
memset ( dev - > ctxlist , 0 , sizeof ( * dev - > ctxlist ) ) ;
INIT_LIST_HEAD ( & dev - > ctxlist - > head ) ;
dev - > vmalist = NULL ;
dev - > sigdata . lock = dev - > lock . hw_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
{
drm_device_t * dev = NULL ;
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 )
{
drm_device_t * dev = NULL ;
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 ,
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 ;
}
2006-02-02 11:37:46 +03:00
mutex_lock ( & dev - > struct_mutex ) ;
2006-01-02 13:32:48 +03:00
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 ;
}
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 )
{
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 ) ;
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
{
drm_file_t * priv = filp - > private_data ;
drm_device_t * dev ;
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 ) ;
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 ) ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 14:16:34 +03:00
if ( dev - > driver - > reclaim_buffers_locked )
dev - > driver - > reclaim_buffers_locked ( dev , filp ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
drm_lock_free ( dev , & dev - > lock . hw_lock - > lock ,
_DRM_LOCKING_CONTEXT ( dev - > lock . hw_lock - > lock ) ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
/* FIXME: may require heavy-handed reset of
hardware at this point , possibly
processed via a callback to the X
server . */
2005-11-10 14:16:34 +03:00
} else if ( dev - > driver - > reclaim_buffers_locked & & priv - > lock_count
2005-09-25 08:28:13 +04:00
& & dev - > lock . hw_lock ) {
2005-04-17 02:20:36 +04:00
/* The lock is required to reclaim buffers */
2005-09-25 08:28:13 +04:00
DECLARE_WAITQUEUE ( entry , current ) ;
2005-04-17 02:20:36 +04:00
2005-09-25 08:28:13 +04:00
add_wait_queue ( & dev - > lock . lock_queue , & entry ) ;
2005-04-17 02:20:36 +04:00
for ( ; ; ) {
__set_current_state ( TASK_INTERRUPTIBLE ) ;
2005-09-25 08:28:13 +04:00
if ( ! dev - > lock . hw_lock ) {
2005-04-17 02:20:36 +04:00
/* Device has been unregistered */
retcode = - EINTR ;
break ;
}
2005-09-25 08:28:13 +04:00
if ( drm_lock_take ( & dev - > lock . hw_lock - > lock ,
DRM_KERNEL_CONTEXT ) ) {
dev - > lock . filp = filp ;
2005-04-17 02:20:36 +04:00
dev - > lock . lock_time = jiffies ;
2005-09-25 08:28:13 +04:00
atomic_inc ( & dev - > counts [ _DRM_STAT_LOCKS ] ) ;
2005-04-17 02:20:36 +04:00
break ; /* Got lock */
}
2005-09-25 08:28:13 +04:00
/* Contention */
2005-04-17 02:20:36 +04:00
schedule ( ) ;
2005-09-25 08:28:13 +04:00
if ( signal_pending ( current ) ) {
2005-04-17 02:20:36 +04:00
retcode = - ERESTARTSYS ;
break ;
}
}
__set_current_state ( TASK_RUNNING ) ;
2005-09-25 08:28:13 +04:00
remove_wait_queue ( & dev - > lock . lock_queue , & entry ) ;
if ( ! retcode ) {
2005-11-10 14:16:34 +03:00
dev - > driver - > reclaim_buffers_locked ( dev , filp ) ;
2005-09-25 08:28:13 +04:00
drm_lock_free ( dev , & dev - > lock . hw_lock - > lock ,
DRM_KERNEL_CONTEXT ) ;
2005-04-17 02:20:36 +04:00
}
}
2005-09-25 08:28:13 +04:00
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 ) ;
2005-09-25 08:28:13 +04:00
if ( dev - > ctxlist & & ( ! list_empty ( & dev - > ctxlist - > head ) ) ) {
2005-04-17 02:20:36 +04:00
drm_ctx_list_t * pos , * n ;
2005-09-25 08:28:13 +04:00
list_for_each_entry_safe ( pos , n , & dev - > ctxlist - > head , head ) {
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 ) {
2005-04-17 02:20:36 +04:00
drm_file_t * temp = dev - > file_first ;
2005-09-25 08:28:13 +04:00
while ( temp ) {
2005-04-17 02:20:36 +04:00
temp - > authenticated = 0 ;
temp = temp - > next ;
}
}
2005-09-25 08:28:13 +04:00
if ( priv - > prev ) {
2005-04-17 02:20:36 +04:00
priv - > prev - > next = priv - > next ;
} else {
2005-09-25 08:28:13 +04:00
dev - > file_first = priv - > next ;
2005-04-17 02:20:36 +04:00
}
2005-09-25 08:28:13 +04:00
if ( priv - > next ) {
2005-04-17 02:20:36 +04:00
priv - > next - > prev = priv - > prev ;
} else {
2005-09-25 08:28:13 +04:00
dev - > file_last = priv - > prev ;
2005-04-17 02:20:36 +04:00
}
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 ) ;