2005-06-28 03:28:47 +04:00
/*
* pcmcia_ioctl . c - - ioctl interface for cardmgr and cardctl
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* The initial developer of the original code is David A . Hinds
* < dahinds @ users . sourceforge . net > . Portions created by David A . Hinds
* are Copyright ( C ) 1999 David A . Hinds . All Rights Reserved .
*
* ( C ) 1999 David A . Hinds
* ( C ) 2003 - 2004 Dominik Brodowski
*/
/*
* This file will go away soon .
*/
2005-06-28 03:28:51 +04:00
# include <linux/kernel.h>
2005-06-28 03:28:47 +04:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/major.h>
# include <linux/errno.h>
# include <linux/ioctl.h>
# include <linux/proc_fs.h>
# include <linux/poll.h>
# include <linux/pci.h>
# include <linux/workqueue.h>
# define IN_CARD_SERVICES
# include <pcmcia/cs_types.h>
# include <pcmcia/cs.h>
# include <pcmcia/cistpl.h>
# include <pcmcia/ds.h>
# include <pcmcia/ss.h>
# include "cs_internal.h"
# include "ds_internal.h"
static int major_dev = - 1 ;
/* Device user information */
# define MAX_EVENTS 32
# define USER_MAGIC 0x7ea4
# define CHECK_USER(u) \
( ( ( u ) = = NULL ) | | ( ( u ) - > user_magic ! = USER_MAGIC ) )
typedef struct user_info_t {
u_int user_magic ;
int event_head , event_tail ;
event_t event [ MAX_EVENTS ] ;
struct user_info_t * next ;
2005-06-28 03:28:50 +04:00
struct pcmcia_socket * socket ;
2005-06-28 03:28:47 +04:00
} user_info_t ;
# ifdef DEBUG
extern int ds_pc_debug ;
# define cs_socket_name(skt) ((skt)->dev.class_id)
# define ds_dbg(lvl, fmt, arg...) do { \
if ( ds_pc_debug > = lvl ) \
printk ( KERN_DEBUG " ds: " fmt , # # arg ) ; \
} while ( 0 )
# else
# define ds_dbg(lvl, fmt, arg...) do { } while (0)
# endif
2006-01-10 22:48:59 +03:00
static struct pcmcia_device * get_pcmcia_device ( struct pcmcia_socket * s ,
unsigned int function )
{
struct pcmcia_device * p_dev = NULL ;
unsigned long flags ;
spin_lock_irqsave ( & pcmcia_dev_list_lock , flags ) ;
list_for_each_entry ( p_dev , & s - > devices_list , socket_device_list ) {
if ( p_dev - > func = = function ) {
spin_unlock_irqrestore ( & pcmcia_dev_list_lock , flags ) ;
return pcmcia_get_dev ( p_dev ) ;
}
}
spin_unlock_irqrestore ( & pcmcia_dev_list_lock , flags ) ;
return NULL ;
}
2005-06-28 03:28:47 +04:00
/* backwards-compatible accessing of driver --- by name! */
2006-01-10 22:48:59 +03:00
static struct pcmcia_driver * get_pcmcia_driver ( dev_info_t * dev_info )
2005-06-28 03:28:47 +04:00
{
struct device_driver * drv ;
struct pcmcia_driver * p_drv ;
drv = driver_find ( ( char * ) dev_info , & pcmcia_bus_type ) ;
if ( ! drv )
return NULL ;
p_drv = container_of ( drv , struct pcmcia_driver , drv ) ;
return ( p_drv ) ;
}
# ifdef CONFIG_PROC_FS
static struct proc_dir_entry * proc_pccard = NULL ;
static int proc_read_drivers_callback ( struct device_driver * driver , void * d )
{
char * * p = d ;
struct pcmcia_driver * p_drv = container_of ( driver ,
struct pcmcia_driver , drv ) ;
* p + = sprintf ( * p , " %-24.24s 1 %d \n " , p_drv - > drv . name ,
# ifdef CONFIG_MODULE_UNLOAD
( p_drv - > owner ) ? module_refcount ( p_drv - > owner ) : 1
# else
1
# endif
) ;
d = ( void * ) p ;
return 0 ;
}
static int proc_read_drivers ( char * buf , char * * start , off_t pos ,
int count , int * eof , void * data )
{
char * p = buf ;
bus_for_each_drv ( & pcmcia_bus_type , NULL ,
( void * ) & p , proc_read_drivers_callback ) ;
return ( p - buf ) ;
}
# endif
/*======================================================================
These manage a ring buffer of events pending for one user process
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int queue_empty ( user_info_t * user )
{
return ( user - > event_head = = user - > event_tail ) ;
}
static event_t get_queued_event ( user_info_t * user )
{
user - > event_tail = ( user - > event_tail + 1 ) % MAX_EVENTS ;
return user - > event [ user - > event_tail ] ;
}
static void queue_event ( user_info_t * user , event_t event )
{
user - > event_head = ( user - > event_head + 1 ) % MAX_EVENTS ;
if ( user - > event_head = = user - > event_tail )
user - > event_tail = ( user - > event_tail + 1 ) % MAX_EVENTS ;
user - > event [ user - > event_head ] = event ;
}
2005-06-28 03:28:50 +04:00
void handle_event ( struct pcmcia_socket * s , event_t event )
2005-06-28 03:28:47 +04:00
{
user_info_t * user ;
for ( user = s - > user ; user ; user = user - > next )
queue_event ( user , event ) ;
wake_up_interruptible ( & s - > queue ) ;
}
/*======================================================================
bind_request ( ) and bind_device ( ) are merged by now . Register_client ( )
is called right at the end of bind_request ( ) , during the driver ' s
- > attach ( ) call . Individual descriptions :
bind_request ( ) connects a socket to a particular client driver .
It looks up the specified device ID in the list of registered
drivers , binds it to the socket , and tries to create an instance
of the device . unbind_request ( ) deletes a driver instance .
Bind_device ( ) associates a device driver with a particular socket .
It is normally called by Driver Services after it has identified
a newly inserted card . An instance of that driver will then be
eligible to register as a client of this socket .
Register_client ( ) uses the dev_info_t handle to match the
caller with a socket . The driver must have already been bound
to a socket with bind_device ( ) - - in fact , bind_device ( )
allocates the client structure that will be used .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
2005-06-28 03:28:50 +04:00
static int bind_request ( struct pcmcia_socket * s , bind_info_t * bind_info )
2005-06-28 03:28:47 +04:00
{
struct pcmcia_driver * p_drv ;
struct pcmcia_device * p_dev ;
int ret = 0 ;
unsigned long flags ;
2005-06-28 03:28:50 +04:00
s = pcmcia_get_socket ( s ) ;
2005-06-28 03:28:47 +04:00
if ( ! s )
return - EINVAL ;
2005-06-28 03:28:50 +04:00
ds_dbg ( 2 , " bind_request(%d, '%s') \n " , s - > sock ,
2005-06-28 03:28:47 +04:00
( char * ) bind_info - > dev_info ) ;
p_drv = get_pcmcia_driver ( & bind_info - > dev_info ) ;
if ( ! p_drv ) {
ret = - EINVAL ;
goto err_put ;
}
if ( ! try_module_get ( p_drv - > owner ) ) {
ret = - EINVAL ;
goto err_put_driver ;
}
spin_lock_irqsave ( & pcmcia_dev_list_lock , flags ) ;
list_for_each_entry ( p_dev , & s - > devices_list , socket_device_list ) {
if ( p_dev - > func = = bind_info - > function ) {
if ( ( p_dev - > dev . driver = = & p_drv - > drv ) ) {
if ( p_dev - > cardmgr ) {
/* if there's already a device
* registered , and it was registered
* by userspace before , we need to
* return the " instance " . */
spin_unlock_irqrestore ( & pcmcia_dev_list_lock , flags ) ;
2006-03-05 12:45:09 +03:00
bind_info - > instance = p_dev ;
2005-06-28 03:28:47 +04:00
ret = - EBUSY ;
goto err_put_module ;
} else {
/* the correct driver managed to bind
* itself magically to the correct
* device . */
spin_unlock_irqrestore ( & pcmcia_dev_list_lock , flags ) ;
p_dev - > cardmgr = p_drv ;
ret = 0 ;
goto err_put_module ;
}
} else if ( ! p_dev - > dev . driver ) {
/* there's already a device available where
* no device has been bound to yet . So we don ' t
* need to register a device ! */
spin_unlock_irqrestore ( & pcmcia_dev_list_lock , flags ) ;
goto rescan ;
}
}
}
spin_unlock_irqrestore ( & pcmcia_dev_list_lock , flags ) ;
p_dev = pcmcia_device_add ( s , bind_info - > function ) ;
if ( ! p_dev ) {
ret = - EIO ;
goto err_put_module ;
}
rescan :
p_dev - > cardmgr = p_drv ;
/* if a driver is already running, we can abort */
if ( p_dev - > dev . driver )
goto err_put_module ;
/*
* Prevent this racing with a card insertion .
*/
2006-01-10 23:20:36 +03:00
mutex_lock ( & s - > skt_mutex ) ;
2005-06-28 03:28:47 +04:00
bus_rescan_devices ( & pcmcia_bus_type ) ;
2006-01-10 23:20:36 +03:00
mutex_unlock ( & s - > skt_mutex ) ;
2005-06-28 03:28:47 +04:00
/* check whether the driver indeed matched. I don't care if this
* is racy or not , because it can only happen on cardmgr access
* paths . . .
*/
if ( ! ( p_dev - > dev . driver = = & p_drv - > drv ) )
p_dev - > cardmgr = NULL ;
err_put_module :
module_put ( p_drv - > owner ) ;
err_put_driver :
put_driver ( & p_drv - > drv ) ;
err_put :
2005-06-28 03:28:50 +04:00
pcmcia_put_socket ( s ) ;
2005-06-28 03:28:47 +04:00
return ( ret ) ;
} /* bind_request */
2005-06-28 03:28:53 +04:00
# ifdef CONFIG_CARDBUS
static struct pci_bus * pcmcia_lookup_bus ( struct pcmcia_socket * s )
{
if ( ! s | | ! ( s - > state & SOCKET_CARDBUS ) )
return NULL ;
2005-06-28 03:28:47 +04:00
2005-06-28 03:28:53 +04:00
return s - > cb_dev - > subordinate ;
}
# endif
2005-06-28 03:28:47 +04:00
2005-06-28 03:28:50 +04:00
static int get_device_info ( struct pcmcia_socket * s , bind_info_t * bind_info , int first )
2005-06-28 03:28:47 +04:00
{
dev_node_t * node ;
struct pcmcia_device * p_dev ;
2006-03-02 02:09:29 +03:00
struct pcmcia_driver * p_drv ;
2005-06-28 03:28:47 +04:00
unsigned long flags ;
int ret = 0 ;
# ifdef CONFIG_CARDBUS
/*
* Some unbelievably ugly code to associate the PCI cardbus
* device and its driver with the PCMCIA " bind " information .
*/
{
struct pci_bus * bus ;
2005-06-28 03:28:50 +04:00
bus = pcmcia_lookup_bus ( s ) ;
2005-06-28 03:28:47 +04:00
if ( bus ) {
struct list_head * list ;
struct pci_dev * dev = NULL ;
list = bus - > devices . next ;
while ( list ! = & bus - > devices ) {
struct pci_dev * pdev = pci_dev_b ( list ) ;
list = list - > next ;
if ( first ) {
dev = pdev ;
break ;
}
/* Try to handle "next" here some way? */
}
if ( dev & & dev - > driver ) {
strlcpy ( bind_info - > name , dev - > driver - > name , DEV_NAME_LEN ) ;
bind_info - > major = 0 ;
bind_info - > minor = 0 ;
bind_info - > next = NULL ;
return 0 ;
}
}
}
# endif
spin_lock_irqsave ( & pcmcia_dev_list_lock , flags ) ;
list_for_each_entry ( p_dev , & s - > devices_list , socket_device_list ) {
if ( p_dev - > func = = bind_info - > function ) {
p_dev = pcmcia_get_dev ( p_dev ) ;
if ( ! p_dev )
continue ;
goto found ;
}
}
spin_unlock_irqrestore ( & pcmcia_dev_list_lock , flags ) ;
return - ENODEV ;
found :
spin_unlock_irqrestore ( & pcmcia_dev_list_lock , flags ) ;
2006-03-02 02:09:29 +03:00
p_drv = to_pcmcia_drv ( p_dev - > dev . driver ) ;
if ( p_drv & & ! p_dev - > _locked ) {
2005-06-28 03:28:47 +04:00
ret = - EAGAIN ;
goto err_put ;
}
if ( first )
2006-03-05 12:45:09 +03:00
node = p_dev - > dev_node ;
2005-06-28 03:28:47 +04:00
else
2006-03-05 12:45:09 +03:00
for ( node = p_dev - > dev_node ; node ; node = node - > next )
2005-06-28 03:28:47 +04:00
if ( node = = bind_info - > next )
break ;
if ( ! node ) {
ret = - ENODEV ;
goto err_put ;
}
strlcpy ( bind_info - > name , node - > dev_name , DEV_NAME_LEN ) ;
bind_info - > major = node - > major ;
bind_info - > minor = node - > minor ;
bind_info - > next = node - > next ;
err_put :
pcmcia_put_dev ( p_dev ) ;
return ( ret ) ;
} /* get_device_info */
static int ds_open ( struct inode * inode , struct file * file )
{
socket_t i = iminor ( inode ) ;
2005-06-28 03:28:50 +04:00
struct pcmcia_socket * s ;
2005-06-28 03:28:47 +04:00
user_info_t * user ;
2005-09-13 12:25:03 +04:00
static int warning_printed = 0 ;
2005-06-28 03:28:47 +04:00
ds_dbg ( 0 , " ds_open(socket %d) \n " , i ) ;
2005-06-28 03:28:50 +04:00
s = pcmcia_get_socket_by_nr ( i ) ;
2005-06-28 03:28:47 +04:00
if ( ! s )
return - ENODEV ;
2005-06-28 03:28:50 +04:00
s = pcmcia_get_socket ( s ) ;
2005-06-28 03:28:47 +04:00
if ( ! s )
return - ENODEV ;
if ( ( file - > f_flags & O_ACCMODE ) ! = O_RDONLY ) {
2005-06-28 03:28:50 +04:00
if ( s - > pcmcia_state . busy ) {
2005-06-28 03:28:50 +04:00
pcmcia_put_socket ( s ) ;
2005-06-28 03:28:47 +04:00
return - EBUSY ;
}
else
2005-06-28 03:28:50 +04:00
s - > pcmcia_state . busy = 1 ;
2005-06-28 03:28:47 +04:00
}
user = kmalloc ( sizeof ( user_info_t ) , GFP_KERNEL ) ;
if ( ! user ) {
2005-06-28 03:28:50 +04:00
pcmcia_put_socket ( s ) ;
2005-06-28 03:28:47 +04:00
return - ENOMEM ;
}
user - > event_tail = user - > event_head = 0 ;
user - > next = s - > user ;
user - > user_magic = USER_MAGIC ;
user - > socket = s ;
s - > user = user ;
file - > private_data = user ;
2005-09-13 12:25:03 +04:00
if ( ! warning_printed ) {
printk ( KERN_INFO " pcmcia: Detected deprecated PCMCIA ioctl "
" usage. \n " ) ;
printk ( KERN_INFO " pcmcia: This interface will soon be removed from "
" the kernel; please expect breakage unless you upgrade "
" to new tools. \n " ) ;
printk ( KERN_INFO " pcmcia: see http://www.kernel.org/pub/linux/ "
" utils/kernel/pcmcia/pcmcia.html for details. \n " ) ;
warning_printed = 1 ;
}
2005-06-28 03:28:50 +04:00
if ( s - > pcmcia_state . present )
2005-06-28 03:28:47 +04:00
queue_event ( user , CS_EVENT_CARD_INSERTION ) ;
return 0 ;
} /* ds_open */
/*====================================================================*/
static int ds_release ( struct inode * inode , struct file * file )
{
2005-06-28 03:28:50 +04:00
struct pcmcia_socket * s ;
2005-06-28 03:28:47 +04:00
user_info_t * user , * * link ;
ds_dbg ( 0 , " ds_release(socket %d) \n " , iminor ( inode ) ) ;
user = file - > private_data ;
if ( CHECK_USER ( user ) )
goto out ;
s = user - > socket ;
/* Unlink user data structure */
if ( ( file - > f_flags & O_ACCMODE ) ! = O_RDONLY ) {
2005-06-28 03:28:50 +04:00
s - > pcmcia_state . busy = 0 ;
2005-06-28 03:28:47 +04:00
}
file - > private_data = NULL ;
for ( link = & s - > user ; * link ; link = & ( * link ) - > next )
if ( * link = = user ) break ;
if ( link = = NULL )
goto out ;
* link = user - > next ;
user - > user_magic = 0 ;
kfree ( user ) ;
2005-06-28 03:28:50 +04:00
pcmcia_put_socket ( s ) ;
2005-06-28 03:28:47 +04:00
out :
return 0 ;
} /* ds_release */
/*====================================================================*/
static ssize_t ds_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
{
2005-06-28 03:28:50 +04:00
struct pcmcia_socket * s ;
2005-06-28 03:28:47 +04:00
user_info_t * user ;
int ret ;
ds_dbg ( 2 , " ds_read(socket %d) \n " , iminor ( file - > f_dentry - > d_inode ) ) ;
if ( count < 4 )
return - EINVAL ;
user = file - > private_data ;
if ( CHECK_USER ( user ) )
return - EIO ;
s = user - > socket ;
2005-06-28 03:28:50 +04:00
if ( s - > pcmcia_state . dead )
2005-06-28 03:28:47 +04:00
return - EIO ;
ret = wait_event_interruptible ( s - > queue , ! queue_empty ( user ) ) ;
if ( ret = = 0 )
ret = put_user ( get_queued_event ( user ) , ( int __user * ) buf ) ? - EFAULT : 4 ;
return ret ;
} /* ds_read */
/*====================================================================*/
static ssize_t ds_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
{
ds_dbg ( 2 , " ds_write(socket %d) \n " , iminor ( file - > f_dentry - > d_inode ) ) ;
if ( count ! = 4 )
return - EINVAL ;
if ( ( file - > f_flags & O_ACCMODE ) = = O_RDONLY )
return - EBADF ;
return - EIO ;
} /* ds_write */
/*====================================================================*/
/* No kernel lock - fine */
static u_int ds_poll ( struct file * file , poll_table * wait )
{
2005-06-28 03:28:50 +04:00
struct pcmcia_socket * s ;
2005-06-28 03:28:47 +04:00
user_info_t * user ;
ds_dbg ( 2 , " ds_poll(socket %d) \n " , iminor ( file - > f_dentry - > d_inode ) ) ;
user = file - > private_data ;
if ( CHECK_USER ( user ) )
return POLLERR ;
s = user - > socket ;
/*
* We don ' t check for a dead socket here since that
* will send cardmgr into an endless spin .
*/
poll_wait ( file , & s - > queue , wait ) ;
if ( ! queue_empty ( user ) )
return POLLIN | POLLRDNORM ;
return 0 ;
} /* ds_poll */
/*====================================================================*/
extern int pcmcia_adjust_resource_info ( adjust_t * adj ) ;
static int ds_ioctl ( struct inode * inode , struct file * file ,
u_int cmd , u_long arg )
{
2005-06-28 03:28:50 +04:00
struct pcmcia_socket * s ;
2005-06-28 03:28:47 +04:00
void __user * uarg = ( char __user * ) arg ;
u_int size ;
int ret , err ;
ds_ioctl_arg_t * buf ;
user_info_t * user ;
ds_dbg ( 2 , " ds_ioctl(socket %d, %#x, %#lx) \n " , iminor ( inode ) , cmd , arg ) ;
user = file - > private_data ;
if ( CHECK_USER ( user ) )
return - EIO ;
s = user - > socket ;
2005-06-28 03:28:50 +04:00
if ( s - > pcmcia_state . dead )
2005-06-28 03:28:47 +04:00
return - EIO ;
size = ( cmd & IOCSIZE_MASK ) > > IOCSIZE_SHIFT ;
if ( size > sizeof ( ds_ioctl_arg_t ) ) return - EINVAL ;
/* Permission check */
if ( ! ( cmd & IOC_OUT ) & & ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( cmd & IOC_IN ) {
if ( ! access_ok ( VERIFY_READ , uarg , size ) ) {
ds_dbg ( 3 , " ds_ioctl(): verify_read = %d \n " , - EFAULT ) ;
return - EFAULT ;
}
}
if ( cmd & IOC_OUT ) {
if ( ! access_ok ( VERIFY_WRITE , uarg , size ) ) {
ds_dbg ( 3 , " ds_ioctl(): verify_write = %d \n " , - EFAULT ) ;
return - EFAULT ;
}
}
buf = kmalloc ( sizeof ( ds_ioctl_arg_t ) , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
err = ret = 0 ;
if ( cmd & IOC_IN ) __copy_from_user ( ( char * ) buf , uarg , size ) ;
switch ( cmd ) {
case DS_ADJUST_RESOURCE_INFO :
ret = pcmcia_adjust_resource_info ( & buf - > adjust ) ;
break ;
case DS_GET_CONFIGURATION_INFO :
if ( buf - > config . Function & &
2005-06-28 03:28:50 +04:00
( buf - > config . Function > = s - > functions ) )
2005-06-28 03:28:47 +04:00
ret = CS_BAD_ARGS ;
2006-01-10 22:48:59 +03:00
else {
struct pcmcia_device * p_dev = get_pcmcia_device ( s , buf - > config . Function ) ;
ret = pccard_get_configuration_info ( s , p_dev , & buf - > config ) ;
pcmcia_put_dev ( p_dev ) ;
}
2005-06-28 03:28:47 +04:00
break ;
case DS_GET_FIRST_TUPLE :
2006-01-10 23:20:36 +03:00
mutex_lock ( & s - > skt_mutex ) ;
2005-06-28 03:28:50 +04:00
pcmcia_validate_mem ( s ) ;
2006-01-10 23:20:36 +03:00
mutex_unlock ( & s - > skt_mutex ) ;
2005-06-28 03:28:50 +04:00
ret = pccard_get_first_tuple ( s , BIND_FN_ALL , & buf - > tuple ) ;
2005-06-28 03:28:47 +04:00
break ;
case DS_GET_NEXT_TUPLE :
2005-06-28 03:28:50 +04:00
ret = pccard_get_next_tuple ( s , BIND_FN_ALL , & buf - > tuple ) ;
2005-06-28 03:28:47 +04:00
break ;
case DS_GET_TUPLE_DATA :
buf - > tuple . TupleData = buf - > tuple_parse . data ;
buf - > tuple . TupleDataMax = sizeof ( buf - > tuple_parse . data ) ;
2005-06-28 03:28:50 +04:00
ret = pccard_get_tuple_data ( s , & buf - > tuple ) ;
2005-06-28 03:28:47 +04:00
break ;
case DS_PARSE_TUPLE :
buf - > tuple . TupleData = buf - > tuple_parse . data ;
ret = pccard_parse_tuple ( & buf - > tuple , & buf - > tuple_parse . parse ) ;
break ;
case DS_RESET_CARD :
2005-06-28 03:28:50 +04:00
ret = pccard_reset_card ( s ) ;
2005-06-28 03:28:47 +04:00
break ;
case DS_GET_STATUS :
2006-01-10 22:48:59 +03:00
if ( buf - > status . Function & &
( buf - > status . Function > = s - > functions ) )
ret = CS_BAD_ARGS ;
else {
struct pcmcia_device * p_dev = get_pcmcia_device ( s , buf - > status . Function ) ;
ret = pccard_get_status ( s , p_dev , & buf - > status ) ;
pcmcia_put_dev ( p_dev ) ;
}
break ;
2005-06-28 03:28:47 +04:00
case DS_VALIDATE_CIS :
2006-01-10 23:20:36 +03:00
mutex_lock ( & s - > skt_mutex ) ;
2005-06-28 03:28:50 +04:00
pcmcia_validate_mem ( s ) ;
2006-01-10 23:20:36 +03:00
mutex_unlock ( & s - > skt_mutex ) ;
2005-06-28 03:28:50 +04:00
ret = pccard_validate_cis ( s , BIND_FN_ALL , & buf - > cisinfo ) ;
2005-06-28 03:28:47 +04:00
break ;
case DS_SUSPEND_CARD :
2005-06-28 03:28:50 +04:00
ret = pcmcia_suspend_card ( s ) ;
2005-06-28 03:28:47 +04:00
break ;
case DS_RESUME_CARD :
2005-06-28 03:28:50 +04:00
ret = pcmcia_resume_card ( s ) ;
2005-06-28 03:28:47 +04:00
break ;
case DS_EJECT_CARD :
2005-06-28 03:28:50 +04:00
err = pcmcia_eject_card ( s ) ;
2005-06-28 03:28:47 +04:00
break ;
case DS_INSERT_CARD :
2005-06-28 03:28:50 +04:00
err = pcmcia_insert_card ( s ) ;
2005-06-28 03:28:47 +04:00
break ;
case DS_ACCESS_CONFIGURATION_REGISTER :
if ( ( buf - > conf_reg . Action = = CS_WRITE ) & & ! capable ( CAP_SYS_ADMIN ) ) {
err = - EPERM ;
goto free_out ;
}
2006-01-10 22:48:59 +03:00
ret = CS_BAD_ARGS ;
if ( ! ( buf - > conf_reg . Function & &
( buf - > conf_reg . Function > = s - > functions ) ) ) {
struct pcmcia_device * p_dev = get_pcmcia_device ( s , buf - > conf_reg . Function ) ;
if ( p_dev )
ret = pcmcia_access_configuration_register ( p_dev , & buf - > conf_reg ) ;
pcmcia_put_dev ( p_dev ) ;
}
2005-06-28 03:28:47 +04:00
break ;
case DS_GET_FIRST_REGION :
case DS_GET_NEXT_REGION :
case DS_BIND_MTD :
if ( ! capable ( CAP_SYS_ADMIN ) ) {
err = - EPERM ;
goto free_out ;
} else {
static int printed = 0 ;
if ( ! printed ) {
printk ( KERN_WARNING " 2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special \n " ) ;
printk ( KERN_WARNING " MTD handling any more. \n " ) ;
printed + + ;
}
}
err = - EINVAL ;
goto free_out ;
break ;
case DS_GET_FIRST_WINDOW :
2005-06-28 03:28:50 +04:00
ret = pcmcia_get_window ( s , & buf - > win_info . handle , 0 ,
2005-06-28 03:28:47 +04:00
& buf - > win_info . window ) ;
break ;
case DS_GET_NEXT_WINDOW :
2005-06-28 03:28:50 +04:00
ret = pcmcia_get_window ( s , & buf - > win_info . handle ,
2005-06-28 03:28:47 +04:00
buf - > win_info . handle - > index + 1 , & buf - > win_info . window ) ;
break ;
case DS_GET_MEM_PAGE :
ret = pcmcia_get_mem_page ( buf - > win_info . handle ,
& buf - > win_info . map ) ;
break ;
case DS_REPLACE_CIS :
2005-06-28 03:28:50 +04:00
ret = pcmcia_replace_cis ( s , & buf - > cisdump ) ;
2005-06-28 03:28:47 +04:00
break ;
case DS_BIND_REQUEST :
if ( ! capable ( CAP_SYS_ADMIN ) ) {
err = - EPERM ;
goto free_out ;
}
err = bind_request ( s , & buf - > bind_info ) ;
break ;
case DS_GET_DEVICE_INFO :
err = get_device_info ( s , & buf - > bind_info , 1 ) ;
break ;
case DS_GET_NEXT_DEVICE :
err = get_device_info ( s , & buf - > bind_info , 0 ) ;
break ;
case DS_UNBIND_REQUEST :
err = 0 ;
break ;
default :
err = - EINVAL ;
}
if ( ( err = = 0 ) & & ( ret ! = CS_SUCCESS ) ) {
ds_dbg ( 2 , " ds_ioctl: ret = %d \n " , ret ) ;
switch ( ret ) {
case CS_BAD_SOCKET : case CS_NO_CARD :
err = - ENODEV ; break ;
case CS_BAD_ARGS : case CS_BAD_ATTRIBUTE : case CS_BAD_IRQ :
case CS_BAD_TUPLE :
err = - EINVAL ; break ;
case CS_IN_USE :
err = - EBUSY ; break ;
case CS_OUT_OF_RESOURCE :
err = - ENOSPC ; break ;
case CS_NO_MORE_ITEMS :
err = - ENODATA ; break ;
case CS_UNSUPPORTED_FUNCTION :
err = - ENOSYS ; break ;
default :
err = - EIO ; break ;
}
}
if ( cmd & IOC_OUT ) {
if ( __copy_to_user ( uarg , ( char * ) buf , size ) )
err = - EFAULT ;
}
free_out :
kfree ( buf ) ;
return err ;
} /* ds_ioctl */
/*====================================================================*/
static struct file_operations ds_fops = {
. owner = THIS_MODULE ,
. open = ds_open ,
. release = ds_release ,
. ioctl = ds_ioctl ,
. read = ds_read ,
. write = ds_write ,
. poll = ds_poll ,
} ;
void __init pcmcia_setup_ioctl ( void ) {
int i ;
/* Set up character device for user mode clients */
i = register_chrdev ( 0 , " pcmcia " , & ds_fops ) ;
2005-06-28 03:29:00 +04:00
if ( i < 0 )
2005-06-28 03:28:47 +04:00
printk ( KERN_NOTICE " unable to find a free device # for "
2005-06-28 03:29:00 +04:00
" Driver Services (error=%d) \n " , i ) ;
2005-06-28 03:28:47 +04:00
else
major_dev = i ;
# ifdef CONFIG_PROC_FS
proc_pccard = proc_mkdir ( " pccard " , proc_bus ) ;
if ( proc_pccard )
create_proc_read_entry ( " drivers " , 0 , proc_pccard , proc_read_drivers , NULL ) ;
# endif
}
void __exit pcmcia_cleanup_ioctl ( void ) {
# ifdef CONFIG_PROC_FS
if ( proc_pccard ) {
remove_proc_entry ( " drivers " , proc_pccard ) ;
remove_proc_entry ( " pccard " , proc_bus ) ;
}
# endif
if ( major_dev ! = - 1 )
unregister_chrdev ( major_dev , " pcmcia " ) ;
}