2005-04-17 02:20:36 +04:00
/*
* ds . c - - 16 - bit PCMCIA core support
*
* 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
2006-03-02 02:02:33 +03:00
* ( C ) 2003 - 2006 Dominik Brodowski
2005-04-17 02:20:36 +04:00
*/
2005-06-28 03:28:51 +04:00
# include <linux/kernel.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/list.h>
# include <linux/delay.h>
# include <linux/workqueue.h>
2005-06-28 03:28:04 +04:00
# include <linux/crc32.h>
2005-06-28 03:28:14 +04:00
# include <linux/firmware.h>
2006-01-10 22:50:39 +03:00
# include <linux/kref.h>
2005-04-17 02:20:36 +04:00
# 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"
2005-06-28 03:28:47 +04:00
# include "ds_internal.h"
2005-04-17 02:20:36 +04:00
/*====================================================================*/
/* Module parameters */
MODULE_AUTHOR ( " David Hinds <dahinds@users.sourceforge.net> " ) ;
MODULE_DESCRIPTION ( " PCMCIA Driver Services " ) ;
MODULE_LICENSE ( " GPL " ) ;
# ifdef DEBUG
2005-06-28 03:28:47 +04:00
int ds_pc_debug ;
2005-04-17 02:20:36 +04:00
module_param_named ( pc_debug , ds_pc_debug , int , 0644 ) ;
# 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
2005-06-28 03:28:47 +04:00
spinlock_t pcmcia_dev_list_lock ;
2005-04-17 02:20:36 +04:00
/*====================================================================*/
/* code which was in cs.c before */
/* String tables for error messages */
typedef struct lookup_t {
int key ;
char * msg ;
} lookup_t ;
static const lookup_t error_table [ ] = {
{ CS_SUCCESS , " Operation succeeded " } ,
{ CS_BAD_ADAPTER , " Bad adapter " } ,
{ CS_BAD_ATTRIBUTE , " Bad attribute " , } ,
{ CS_BAD_BASE , " Bad base address " } ,
{ CS_BAD_EDC , " Bad EDC " } ,
{ CS_BAD_IRQ , " Bad IRQ " } ,
{ CS_BAD_OFFSET , " Bad offset " } ,
{ CS_BAD_PAGE , " Bad page number " } ,
{ CS_READ_FAILURE , " Read failure " } ,
{ CS_BAD_SIZE , " Bad size " } ,
{ CS_BAD_SOCKET , " Bad socket " } ,
{ CS_BAD_TYPE , " Bad type " } ,
{ CS_BAD_VCC , " Bad Vcc " } ,
{ CS_BAD_VPP , " Bad Vpp " } ,
{ CS_BAD_WINDOW , " Bad window " } ,
{ CS_WRITE_FAILURE , " Write failure " } ,
{ CS_NO_CARD , " No card present " } ,
{ CS_UNSUPPORTED_FUNCTION , " Usupported function " } ,
{ CS_UNSUPPORTED_MODE , " Unsupported mode " } ,
{ CS_BAD_SPEED , " Bad speed " } ,
{ CS_BUSY , " Resource busy " } ,
{ CS_GENERAL_FAILURE , " General failure " } ,
{ CS_WRITE_PROTECTED , " Write protected " } ,
{ CS_BAD_ARG_LENGTH , " Bad argument length " } ,
{ CS_BAD_ARGS , " Bad arguments " } ,
{ CS_CONFIGURATION_LOCKED , " Configuration locked " } ,
{ CS_IN_USE , " Resource in use " } ,
{ CS_NO_MORE_ITEMS , " No more items " } ,
{ CS_OUT_OF_RESOURCE , " Out of resource " } ,
{ CS_BAD_HANDLE , " Bad handle " } ,
{ CS_BAD_TUPLE , " Bad CIS tuple " }
} ;
static const lookup_t service_table [ ] = {
{ AccessConfigurationRegister , " AccessConfigurationRegister " } ,
{ AddSocketServices , " AddSocketServices " } ,
{ AdjustResourceInfo , " AdjustResourceInfo " } ,
{ CheckEraseQueue , " CheckEraseQueue " } ,
{ CloseMemory , " CloseMemory " } ,
{ DeregisterClient , " DeregisterClient " } ,
{ DeregisterEraseQueue , " DeregisterEraseQueue " } ,
{ GetCardServicesInfo , " GetCardServicesInfo " } ,
{ GetClientInfo , " GetClientInfo " } ,
{ GetConfigurationInfo , " GetConfigurationInfo " } ,
{ GetEventMask , " GetEventMask " } ,
{ GetFirstClient , " GetFirstClient " } ,
{ GetFirstRegion , " GetFirstRegion " } ,
{ GetFirstTuple , " GetFirstTuple " } ,
{ GetNextClient , " GetNextClient " } ,
{ GetNextRegion , " GetNextRegion " } ,
{ GetNextTuple , " GetNextTuple " } ,
{ GetStatus , " GetStatus " } ,
{ GetTupleData , " GetTupleData " } ,
{ MapMemPage , " MapMemPage " } ,
{ ModifyConfiguration , " ModifyConfiguration " } ,
{ ModifyWindow , " ModifyWindow " } ,
{ OpenMemory , " OpenMemory " } ,
{ ParseTuple , " ParseTuple " } ,
{ ReadMemory , " ReadMemory " } ,
{ RegisterClient , " RegisterClient " } ,
{ RegisterEraseQueue , " RegisterEraseQueue " } ,
{ RegisterMTD , " RegisterMTD " } ,
{ ReleaseConfiguration , " ReleaseConfiguration " } ,
{ ReleaseIO , " ReleaseIO " } ,
{ ReleaseIRQ , " ReleaseIRQ " } ,
{ ReleaseWindow , " ReleaseWindow " } ,
{ RequestConfiguration , " RequestConfiguration " } ,
{ RequestIO , " RequestIO " } ,
{ RequestIRQ , " RequestIRQ " } ,
{ RequestSocketMask , " RequestSocketMask " } ,
{ RequestWindow , " RequestWindow " } ,
{ ResetCard , " ResetCard " } ,
{ SetEventMask , " SetEventMask " } ,
{ ValidateCIS , " ValidateCIS " } ,
{ WriteMemory , " WriteMemory " } ,
{ BindDevice , " BindDevice " } ,
{ BindMTD , " BindMTD " } ,
{ ReportError , " ReportError " } ,
{ SuspendCard , " SuspendCard " } ,
{ ResumeCard , " ResumeCard " } ,
{ EjectCard , " EjectCard " } ,
{ InsertCard , " InsertCard " } ,
{ ReplaceCIS , " ReplaceCIS " }
} ;
2005-07-08 04:59:02 +04:00
static int pcmcia_report_error ( struct pcmcia_device * p_dev , error_info_t * err )
2005-04-17 02:20:36 +04:00
{
int i ;
char * serv ;
2005-07-08 04:59:02 +04:00
if ( ! p_dev )
2005-04-17 02:20:36 +04:00
printk ( KERN_NOTICE ) ;
2005-07-08 04:59:02 +04:00
else
2005-04-17 02:20:36 +04:00
printk ( KERN_NOTICE " %s: " , p_dev - > dev . bus_id ) ;
for ( i = 0 ; i < ARRAY_SIZE ( service_table ) ; i + + )
if ( service_table [ i ] . key = = err - > func )
break ;
if ( i < ARRAY_SIZE ( service_table ) )
serv = service_table [ i ] . msg ;
else
serv = " Unknown service number " ;
for ( i = 0 ; i < ARRAY_SIZE ( error_table ) ; i + + )
if ( error_table [ i ] . key = = err - > retcode )
break ;
if ( i < ARRAY_SIZE ( error_table ) )
printk ( " %s: %s \n " , serv , error_table [ i ] . msg ) ;
else
printk ( " %s: Unknown error code %#x \n " , serv , err - > retcode ) ;
return CS_SUCCESS ;
} /* report_error */
/* end of code which was in cs.c before */
/*======================================================================*/
2005-07-08 04:59:02 +04:00
void cs_error ( struct pcmcia_device * p_dev , int func , int ret )
2005-04-17 02:20:36 +04:00
{
error_info_t err = { func , ret } ;
2005-07-08 04:59:02 +04:00
pcmcia_report_error ( p_dev , & err ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( cs_error ) ;
2005-06-28 03:28:07 +04:00
static void pcmcia_check_driver ( struct pcmcia_driver * p_drv )
{
struct pcmcia_device_id * did = p_drv - > id_table ;
unsigned int i ;
u32 hash ;
2005-11-14 23:25:51 +03:00
if ( ! p_drv - > probe | | ! p_drv - > remove )
2005-07-28 12:07:20 +04:00
printk ( KERN_DEBUG " pcmcia: %s lacks a requisite callback "
" function \n " , p_drv - > drv . name ) ;
2005-07-08 04:59:00 +04:00
2005-06-28 03:28:07 +04:00
while ( did & & did - > match_flags ) {
for ( i = 0 ; i < 4 ; i + + ) {
if ( ! did - > prod_id [ i ] )
continue ;
hash = crc32 ( 0 , did - > prod_id [ i ] , strlen ( did - > prod_id [ i ] ) ) ;
if ( hash = = did - > prod_id_hash [ i ] )
continue ;
printk ( KERN_DEBUG " pcmcia: %s: invalid hash for "
" product string \" %s \" : is 0x%x, should "
" be 0x%x \n " , p_drv - > drv . name , did - > prod_id [ i ] ,
did - > prod_id_hash [ i ] , hash ) ;
2005-06-28 03:28:45 +04:00
printk ( KERN_DEBUG " pcmcia: see "
" Documentation/pcmcia/devicetable.txt for "
" details \n " ) ;
2005-06-28 03:28:07 +04:00
}
did + + ;
}
return ;
}
2005-06-28 03:28:14 +04:00
# ifdef CONFIG_PCMCIA_LOAD_CIS
/**
* pcmcia_load_firmware - load CIS from userspace if device - provided is broken
* @ dev - the pcmcia device which needs a CIS override
2006-03-12 05:32:07 +03:00
* @ filename - requested filename in / lib / firmware /
2005-06-28 03:28:14 +04:00
*
* This uses the in - kernel firmware loading mechanism to use a " fake CIS " if
* the one provided by the card is broken . The firmware files reside in
2006-03-12 05:32:07 +03:00
* / lib / firmware / in userspace .
2005-06-28 03:28:14 +04:00
*/
static int pcmcia_load_firmware ( struct pcmcia_device * dev , char * filename )
{
struct pcmcia_socket * s = dev - > socket ;
const struct firmware * fw ;
char path [ 20 ] ;
int ret = - ENOMEM ;
cisdump_t * cis ;
if ( ! filename )
return - EINVAL ;
ds_dbg ( 1 , " trying to load firmware %s \n " , filename ) ;
if ( strlen ( filename ) > 14 )
return - EINVAL ;
snprintf ( path , 20 , " %s " , filename ) ;
if ( request_firmware ( & fw , path , & dev - > dev ) = = 0 ) {
if ( fw - > size > = CISTPL_MAX_CIS_SIZE )
goto release ;
2005-12-11 23:18:26 +03:00
cis = kzalloc ( sizeof ( cisdump_t ) , GFP_KERNEL ) ;
2005-06-28 03:28:14 +04:00
if ( ! cis )
goto release ;
cis - > Length = fw - > size + 1 ;
memcpy ( cis - > Data , fw - > data , fw - > size ) ;
if ( ! pcmcia_replace_cis ( s , cis ) )
ret = 0 ;
}
release :
release_firmware ( fw ) ;
return ( ret ) ;
}
# else /* !CONFIG_PCMCIA_LOAD_CIS */
static inline int pcmcia_load_firmware ( struct pcmcia_device * dev , char * filename )
{
return - ENODEV ;
}
# endif
2005-04-17 02:20:36 +04:00
/*======================================================================*/
/**
* pcmcia_register_driver - register a PCMCIA driver with the bus core
*
* Registers a PCMCIA driver with the PCMCIA bus core .
*/
int pcmcia_register_driver ( struct pcmcia_driver * driver )
{
if ( ! driver )
return - EINVAL ;
2005-06-28 03:28:07 +04:00
pcmcia_check_driver ( driver ) ;
2005-04-17 02:20:36 +04:00
/* initialize common fields */
driver - > drv . bus = & pcmcia_bus_type ;
driver - > drv . owner = driver - > owner ;
return driver_register ( & driver - > drv ) ;
}
EXPORT_SYMBOL ( pcmcia_register_driver ) ;
/**
* pcmcia_unregister_driver - unregister a PCMCIA driver with the bus core
*/
void pcmcia_unregister_driver ( struct pcmcia_driver * driver )
{
driver_unregister ( & driver - > drv ) ;
}
EXPORT_SYMBOL ( pcmcia_unregister_driver ) ;
/* pcmcia_device handling */
2005-06-28 03:28:47 +04:00
struct pcmcia_device * pcmcia_get_dev ( struct pcmcia_device * p_dev )
2005-04-17 02:20:36 +04:00
{
struct device * tmp_dev ;
tmp_dev = get_device ( & p_dev - > dev ) ;
if ( ! tmp_dev )
return NULL ;
return to_pcmcia_dev ( tmp_dev ) ;
}
2005-06-28 03:28:47 +04:00
void pcmcia_put_dev ( struct pcmcia_device * p_dev )
2005-04-17 02:20:36 +04:00
{
if ( p_dev )
put_device ( & p_dev - > dev ) ;
}
2006-01-10 22:50:39 +03:00
static void pcmcia_release_function ( struct kref * ref )
{
struct config_t * c = container_of ( ref , struct config_t , ref ) ;
kfree ( c ) ;
}
2005-04-17 02:20:36 +04:00
static void pcmcia_release_dev ( struct device * dev )
{
struct pcmcia_device * p_dev = to_pcmcia_dev ( dev ) ;
ds_dbg ( 1 , " releasing dev %p \n " , p_dev ) ;
2005-06-28 03:28:50 +04:00
pcmcia_put_socket ( p_dev - > socket ) ;
2005-09-10 00:03:29 +04:00
kfree ( p_dev - > devname ) ;
2006-01-10 22:50:39 +03:00
kref_put ( & p_dev - > function_config - > ref , pcmcia_release_function ) ;
2005-04-17 02:20:36 +04:00
kfree ( p_dev ) ;
}
2006-01-27 21:15:02 +03:00
static void pcmcia_add_pseudo_device ( struct pcmcia_socket * s )
{
if ( ! s - > pcmcia_state . device_add_pending ) {
s - > pcmcia_state . device_add_pending = 1 ;
schedule_work ( & s - > device_add ) ;
}
return ;
}
2005-04-17 02:20:36 +04:00
static int pcmcia_device_probe ( struct device * dev )
{
struct pcmcia_device * p_dev ;
struct pcmcia_driver * p_drv ;
2006-01-27 21:15:02 +03:00
struct pcmcia_device_id * did ;
2005-11-14 23:25:51 +03:00
struct pcmcia_socket * s ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
dev = get_device ( dev ) ;
if ( ! dev )
return - ENODEV ;
p_dev = to_pcmcia_dev ( dev ) ;
p_drv = to_pcmcia_drv ( dev - > driver ) ;
2005-11-14 23:25:51 +03:00
s = p_dev - > socket ;
2005-04-17 02:20:36 +04:00
2006-01-10 22:50:39 +03:00
if ( ( ! p_drv - > probe ) | | ( ! p_dev - > function_config ) | |
( ! try_module_get ( p_drv - > owner ) ) ) {
2005-04-17 02:20:36 +04:00
ret = - EINVAL ;
goto put_dev ;
}
2005-11-14 23:25:51 +03:00
ret = p_drv - > probe ( p_dev ) ;
2006-01-27 21:15:02 +03:00
if ( ret )
goto put_module ;
/* handle pseudo multifunction devices:
* there are at most two pseudo multifunction devices .
* if we ' re matching against the first , schedule a
* call which will then check whether there are two
* pseudo devices , and if not , add the second one .
*/
2006-04-11 10:24:57 +04:00
did = p_dev - > dev . driver_data ;
2006-03-11 00:13:44 +03:00
if ( did & & ( did - > match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO ) & &
2006-01-27 21:15:02 +03:00
( p_dev - > socket - > device_count = = 1 ) & & ( p_dev - > device_no = = 0 ) )
pcmcia_add_pseudo_device ( p_dev - > socket ) ;
2005-11-14 23:25:51 +03:00
put_module :
2005-04-17 02:20:36 +04:00
if ( ret )
module_put ( p_drv - > owner ) ;
put_dev :
2005-11-14 23:25:51 +03:00
if ( ret )
2005-04-17 02:20:36 +04:00
put_device ( dev ) ;
return ( ret ) ;
}
2006-02-05 11:51:34 +03:00
/*
* Removes a PCMCIA card from the device tree and socket list .
*/
static void pcmcia_card_remove ( struct pcmcia_socket * s , struct pcmcia_device * leftover )
{
struct pcmcia_device * p_dev ;
struct pcmcia_device * tmp ;
unsigned long flags ;
ds_dbg ( 2 , " unbind_request(%d) \n " , s - > sock ) ;
if ( ! leftover )
s - > device_count = 0 ;
else
s - > device_count = 1 ;
/* unregister all pcmcia_devices registered with this socket, except leftover */
list_for_each_entry_safe ( p_dev , tmp , & s - > devices_list , socket_device_list ) {
if ( p_dev = = leftover )
continue ;
spin_lock_irqsave ( & pcmcia_dev_list_lock , flags ) ;
list_del ( & p_dev - > socket_device_list ) ;
p_dev - > _removed = 1 ;
spin_unlock_irqrestore ( & pcmcia_dev_list_lock , flags ) ;
device_unregister ( & p_dev - > dev ) ;
}
return ;
}
2005-04-17 02:20:36 +04:00
static int pcmcia_device_remove ( struct device * dev )
{
struct pcmcia_device * p_dev ;
struct pcmcia_driver * p_drv ;
2006-02-05 11:51:34 +03:00
struct pcmcia_device_id * did ;
2005-11-14 23:23:14 +03:00
int i ;
2005-04-17 02:20:36 +04:00
p_dev = to_pcmcia_dev ( dev ) ;
p_drv = to_pcmcia_drv ( dev - > driver ) ;
2006-02-05 11:51:34 +03:00
/* If we're removing the primary module driving a
* pseudo multi - function card , we need to unbind
* all devices
*/
2006-04-11 10:24:57 +04:00
did = p_dev - > dev . driver_data ;
2006-03-11 00:15:19 +03:00
if ( did & & ( did - > match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO ) & &
2006-02-05 11:51:34 +03:00
( p_dev - > socket - > device_count ! = 0 ) & &
( p_dev - > device_no = = 0 ) )
pcmcia_card_remove ( p_dev - > socket , p_dev ) ;
/* detach the "instance" */
2005-11-14 23:25:23 +03:00
if ( ! p_drv )
return 0 ;
2005-04-17 02:20:36 +04:00
2005-11-14 23:25:23 +03:00
if ( p_drv - > remove )
2005-11-14 23:23:14 +03:00
p_drv - > remove ( p_dev ) ;
2006-04-04 13:09:26 +04:00
p_dev - > dev_node = NULL ;
2005-11-14 23:25:23 +03:00
/* check for proper unloading */
2006-03-02 02:09:29 +03:00
if ( p_dev - > _irq | | p_dev - > _io | | p_dev - > _locked )
2005-11-14 23:25:23 +03:00
printk ( KERN_INFO " pcmcia: driver %s did not release config properly \n " ,
p_drv - > drv . name ) ;
2005-11-14 23:23:14 +03:00
2005-11-14 23:25:23 +03:00
for ( i = 0 ; i < MAX_WIN ; i + + )
2006-03-02 02:09:29 +03:00
if ( p_dev - > _win & CLIENT_WIN_REQ ( i ) )
2005-11-14 23:25:23 +03:00
printk ( KERN_INFO " pcmcia: driver %s did not release windows properly \n " ,
p_drv - > drv . name ) ;
2005-11-14 23:23:14 +03:00
2005-11-14 23:25:23 +03:00
/* references from pcmcia_probe_device */
pcmcia_put_dev ( p_dev ) ;
module_put ( p_drv - > owner ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* pcmcia_device_query - - determine information about a pcmcia device
*/
static int pcmcia_device_query ( struct pcmcia_device * p_dev )
{
cistpl_manfid_t manf_id ;
cistpl_funcid_t func_id ;
2005-09-10 00:03:21 +04:00
cistpl_vers_1_t * vers1 ;
2005-04-17 02:20:36 +04:00
unsigned int i ;
2005-09-10 00:03:21 +04:00
vers1 = kmalloc ( sizeof ( * vers1 ) , GFP_KERNEL ) ;
if ( ! vers1 )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
if ( ! pccard_read_tuple ( p_dev - > socket , p_dev - > func ,
CISTPL_MANFID , & manf_id ) ) {
p_dev - > manf_id = manf_id . manf ;
p_dev - > card_id = manf_id . card ;
p_dev - > has_manf_id = 1 ;
p_dev - > has_card_id = 1 ;
}
if ( ! pccard_read_tuple ( p_dev - > socket , p_dev - > func ,
CISTPL_FUNCID , & func_id ) ) {
p_dev - > func_id = func_id . func ;
p_dev - > has_func_id = 1 ;
} else {
/* rule of thumb: cards with no FUNCID, but with
* common memory device geometry information , are
* probably memory cards ( from pcmcia - cs ) */
2005-09-10 00:03:21 +04:00
cistpl_device_geo_t * devgeo ;
devgeo = kmalloc ( sizeof ( * devgeo ) , GFP_KERNEL ) ;
if ( ! devgeo ) {
kfree ( vers1 ) ;
return - ENOMEM ;
}
2005-04-17 02:20:36 +04:00
if ( ! pccard_read_tuple ( p_dev - > socket , p_dev - > func ,
2005-09-10 00:03:21 +04:00
CISTPL_DEVICE_GEO , devgeo ) ) {
2005-04-17 02:20:36 +04:00
ds_dbg ( 0 , " mem device geometry probably means "
" FUNCID_MEMORY \n " ) ;
p_dev - > func_id = CISTPL_FUNCID_MEMORY ;
p_dev - > has_func_id = 1 ;
}
2005-09-10 00:03:21 +04:00
kfree ( devgeo ) ;
2005-04-17 02:20:36 +04:00
}
if ( ! pccard_read_tuple ( p_dev - > socket , p_dev - > func , CISTPL_VERS_1 ,
2005-09-10 00:03:21 +04:00
vers1 ) ) {
for ( i = 0 ; i < vers1 - > ns ; i + + ) {
2005-04-17 02:20:36 +04:00
char * tmp ;
unsigned int length ;
2005-09-10 00:03:21 +04:00
tmp = vers1 - > str + vers1 - > ofs [ i ] ;
2005-04-17 02:20:36 +04:00
length = strlen ( tmp ) + 1 ;
2006-03-31 03:04:57 +04:00
if ( ( length < 2 ) | | ( length > 255 ) )
2005-04-17 02:20:36 +04:00
continue ;
p_dev - > prod_id [ i ] = kmalloc ( sizeof ( char ) * length ,
GFP_KERNEL ) ;
if ( ! p_dev - > prod_id [ i ] )
continue ;
p_dev - > prod_id [ i ] = strncpy ( p_dev - > prod_id [ i ] ,
tmp , length ) ;
}
}
2005-09-10 00:03:21 +04:00
kfree ( vers1 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* device_add_lock is needed to avoid double registration by cardmgr and kernel.
* Serializes pcmcia_device_add ; will most likely be removed in future .
*
* While it has the caveat that adding new PCMCIA devices inside ( ! ) device_register ( )
* won ' t work , this doesn ' t matter much at the moment : the driver core doesn ' t
* support it either .
*/
2006-01-10 23:20:36 +03:00
static DEFINE_MUTEX ( device_add_lock ) ;
2005-04-17 02:20:36 +04:00
2005-06-28 03:28:50 +04:00
struct pcmcia_device * pcmcia_device_add ( struct pcmcia_socket * s , unsigned int function )
2005-04-17 02:20:36 +04:00
{
2006-01-10 22:50:39 +03:00
struct pcmcia_device * p_dev , * tmp_dev ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2005-09-10 00:03:29 +04:00
int bus_id_len ;
2005-04-17 02:20:36 +04:00
2005-06-28 03:28:50 +04:00
s = pcmcia_get_socket ( s ) ;
2005-04-17 02:20:36 +04:00
if ( ! s )
return NULL ;
2006-01-10 23:20:36 +03:00
mutex_lock ( & device_add_lock ) ;
2005-04-17 02:20:36 +04:00
2005-06-28 03:28:06 +04:00
/* max of 2 devices per card */
if ( s - > device_count = = 2 )
goto err_put ;
2005-12-11 23:18:26 +03:00
p_dev = kzalloc ( sizeof ( struct pcmcia_device ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! p_dev )
goto err_put ;
2005-06-28 03:28:50 +04:00
p_dev - > socket = s ;
2005-04-17 02:20:36 +04:00
p_dev - > device_no = ( s - > device_count + + ) ;
p_dev - > func = function ;
2006-03-01 16:04:52 +03:00
if ( s - > functions < = function )
s - > functions = function + 1 ;
2005-04-17 02:20:36 +04:00
p_dev - > dev . bus = & pcmcia_bus_type ;
2005-06-28 03:28:50 +04:00
p_dev - > dev . parent = s - > dev . dev ;
2005-04-17 02:20:36 +04:00
p_dev - > dev . release = pcmcia_release_dev ;
2005-09-10 00:03:29 +04:00
bus_id_len = sprintf ( p_dev - > dev . bus_id , " %d.%d " , p_dev - > socket - > sock , p_dev - > device_no ) ;
p_dev - > devname = kmalloc ( 6 + bus_id_len + 1 , GFP_KERNEL ) ;
if ( ! p_dev - > devname )
goto err_free ;
sprintf ( p_dev - > devname , " pcmcia%s " , p_dev - > dev . bus_id ) ;
2005-04-17 02:20:36 +04:00
/* compat */
spin_lock_irqsave ( & pcmcia_dev_list_lock , flags ) ;
2006-01-10 22:50:39 +03:00
/*
* p_dev - > function_config must be the same for all card functions .
* Note that this is serialized by the device_add_lock , so that
* only one such struct will be created .
*/
list_for_each_entry ( tmp_dev , & s - > devices_list , socket_device_list )
if ( p_dev - > func = = tmp_dev - > func ) {
p_dev - > function_config = tmp_dev - > function_config ;
kref_get ( & p_dev - > function_config - > ref ) ;
}
/* Add to the list in pcmcia_bus_socket */
2006-04-02 12:39:27 +04:00
list_add ( & p_dev - > socket_device_list , & s - > devices_list ) ;
2006-01-10 22:50:39 +03:00
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & pcmcia_dev_list_lock , flags ) ;
2006-01-10 22:50:39 +03:00
if ( ! p_dev - > function_config ) {
p_dev - > function_config = kzalloc ( sizeof ( struct config_t ) ,
GFP_KERNEL ) ;
if ( ! p_dev - > function_config )
goto err_unreg ;
kref_init ( & p_dev - > function_config - > ref ) ;
}
2005-11-13 01:34:06 +03:00
printk ( KERN_NOTICE " pcmcia: registering new device %s \n " ,
p_dev - > devname ) ;
2005-06-28 03:28:06 +04:00
pcmcia_device_query ( p_dev ) ;
2006-01-10 22:50:39 +03:00
if ( device_register ( & p_dev - > dev ) )
goto err_unreg ;
2005-04-17 02:20:36 +04:00
2006-01-10 23:20:36 +03:00
mutex_unlock ( & device_add_lock ) ;
2005-04-17 02:20:36 +04:00
return p_dev ;
2006-01-10 22:50:39 +03:00
err_unreg :
spin_lock_irqsave ( & pcmcia_dev_list_lock , flags ) ;
list_del ( & p_dev - > socket_device_list ) ;
spin_unlock_irqrestore ( & pcmcia_dev_list_lock , flags ) ;
2005-04-17 02:20:36 +04:00
err_free :
2005-09-10 00:03:29 +04:00
kfree ( p_dev - > devname ) ;
2005-04-17 02:20:36 +04:00
kfree ( p_dev ) ;
s - > device_count - - ;
err_put :
2006-01-10 23:20:36 +03:00
mutex_unlock ( & device_add_lock ) ;
2005-06-28 03:28:50 +04:00
pcmcia_put_socket ( s ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
static int pcmcia_card_add ( struct pcmcia_socket * s )
{
cisinfo_t cisinfo ;
cistpl_longlink_mfc_t mfc ;
unsigned int no_funcs , i ;
int ret = 0 ;
if ( ! ( s - > resource_setup_done ) )
return - EAGAIN ; /* try again, but later... */
2005-09-28 21:41:56 +04:00
if ( pcmcia_validate_mem ( s ) )
return - EAGAIN ; /* try again, but later... */
2005-04-17 02:20:36 +04:00
ret = pccard_validate_cis ( s , BIND_FN_ALL , & cisinfo ) ;
if ( ret | | ! cisinfo . Chains ) {
ds_dbg ( 0 , " invalid CIS or invalid resources \n " ) ;
return - ENODEV ;
}
if ( ! pccard_read_tuple ( s , BIND_FN_ALL , CISTPL_LONGLINK_MFC , & mfc ) )
no_funcs = mfc . nfn ;
else
no_funcs = 1 ;
for ( i = 0 ; i < no_funcs ; i + + )
2005-06-28 03:28:50 +04:00
pcmcia_device_add ( s , i ) ;
2005-04-17 02:20:36 +04:00
return ( ret ) ;
}
2005-06-28 03:28:06 +04:00
static void pcmcia_delayed_add_pseudo_device ( void * data )
{
2005-06-28 03:28:50 +04:00
struct pcmcia_socket * s = data ;
2005-06-28 03:28:06 +04:00
pcmcia_device_add ( s , 0 ) ;
2005-06-28 03:28:50 +04:00
s - > pcmcia_state . device_add_pending = 0 ;
2005-06-28 03:28:06 +04:00
}
2005-06-28 03:28:17 +04:00
static int pcmcia_requery ( struct device * dev , void * _data )
2005-06-28 03:28:09 +04:00
{
2005-06-28 03:28:17 +04:00
struct pcmcia_device * p_dev = to_pcmcia_dev ( dev ) ;
if ( ! p_dev - > dev . driver )
pcmcia_device_query ( p_dev ) ;
return 0 ;
}
static void pcmcia_bus_rescan ( struct pcmcia_socket * skt )
{
int no_devices = 0 ;
unsigned long flags ;
2006-01-10 23:20:36 +03:00
/* must be called with skt_mutex held */
2005-06-28 03:28:17 +04:00
spin_lock_irqsave ( & pcmcia_dev_list_lock , flags ) ;
2005-06-28 03:28:50 +04:00
if ( list_empty ( & skt - > devices_list ) )
2005-06-28 03:28:17 +04:00
no_devices = 1 ;
spin_unlock_irqrestore ( & pcmcia_dev_list_lock , flags ) ;
/* if no devices were added for this socket yet because of
* missing resource information or other trouble , we need to
* do this now . */
if ( no_devices ) {
int ret = pcmcia_card_add ( skt ) ;
if ( ret )
return ;
}
/* some device information might have changed because of a CIS
* update or because we can finally read it correctly . . . so
* determine it again , overwriting old values if necessary . */
bus_for_each_dev ( & pcmcia_bus_type , NULL , NULL , pcmcia_requery ) ;
/* we re-scan all devices, not just the ones connected to this
* socket . This does not matter , though . */
2005-06-28 03:28:10 +04:00
bus_rescan_devices ( & pcmcia_bus_type ) ;
2005-06-28 03:28:09 +04:00
}
2005-06-28 03:28:06 +04:00
static inline int pcmcia_devmatch ( struct pcmcia_device * dev ,
struct pcmcia_device_id * did )
{
if ( did - > match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID ) {
if ( ( ! dev - > has_manf_id ) | | ( dev - > manf_id ! = did - > manf_id ) )
return 0 ;
}
if ( did - > match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID ) {
if ( ( ! dev - > has_card_id ) | | ( dev - > card_id ! = did - > card_id ) )
return 0 ;
}
if ( did - > match_flags & PCMCIA_DEV_ID_MATCH_FUNCTION ) {
if ( dev - > func ! = did - > function )
return 0 ;
}
if ( did - > match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1 ) {
if ( ! dev - > prod_id [ 0 ] )
return 0 ;
if ( strcmp ( did - > prod_id [ 0 ] , dev - > prod_id [ 0 ] ) )
return 0 ;
}
if ( did - > match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2 ) {
if ( ! dev - > prod_id [ 1 ] )
return 0 ;
if ( strcmp ( did - > prod_id [ 1 ] , dev - > prod_id [ 1 ] ) )
return 0 ;
}
if ( did - > match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3 ) {
if ( ! dev - > prod_id [ 2 ] )
return 0 ;
if ( strcmp ( did - > prod_id [ 2 ] , dev - > prod_id [ 2 ] ) )
return 0 ;
}
if ( did - > match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4 ) {
if ( ! dev - > prod_id [ 3 ] )
return 0 ;
if ( strcmp ( did - > prod_id [ 3 ] , dev - > prod_id [ 3 ] ) )
return 0 ;
}
if ( did - > match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO ) {
if ( dev - > device_no ! = did - > device_no )
return 0 ;
}
if ( did - > match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID ) {
if ( ( ! dev - > has_func_id ) | | ( dev - > func_id ! = did - > func_id ) )
return 0 ;
/* if this is a pseudo-multi-function device,
* we need explicit matches */
if ( did - > match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO )
return 0 ;
if ( dev - > device_no )
return 0 ;
/* also, FUNC_ID matching needs to be activated by userspace
* after it has re - checked that there is no possible module
* with a prod_id / manf_id / card_id match .
*/
if ( ! dev - > allow_func_id_match )
return 0 ;
}
2005-06-28 03:28:07 +04:00
if ( did - > match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS ) {
2005-06-28 03:28:14 +04:00
if ( ! dev - > socket - > fake_cis )
pcmcia_load_firmware ( dev , did - > cisfile ) ;
if ( ! dev - > socket - > fake_cis )
2005-06-28 03:28:07 +04:00
return 0 ;
}
2005-06-28 03:28:09 +04:00
if ( did - > match_flags & PCMCIA_DEV_ID_MATCH_ANONYMOUS ) {
int i ;
for ( i = 0 ; i < 4 ; i + + )
if ( dev - > prod_id [ i ] )
return 0 ;
if ( dev - > has_manf_id | | dev - > has_card_id | | dev - > has_func_id )
return 0 ;
}
2005-06-28 03:28:06 +04:00
dev - > dev . driver_data = ( void * ) did ;
return 1 ;
}
2005-04-17 02:20:36 +04:00
static int pcmcia_bus_match ( struct device * dev , struct device_driver * drv ) {
struct pcmcia_device * p_dev = to_pcmcia_dev ( dev ) ;
struct pcmcia_driver * p_drv = to_pcmcia_drv ( drv ) ;
2005-06-28 03:28:06 +04:00
struct pcmcia_device_id * did = p_drv - > id_table ;
2005-04-17 02:20:36 +04:00
2006-01-15 03:14:31 +03:00
# ifdef CONFIG_PCMCIA_IOCTL
2005-04-17 02:20:36 +04:00
/* matching by cardmgr */
if ( p_dev - > cardmgr = = p_drv )
return 1 ;
2006-01-15 03:14:31 +03:00
# endif
2005-04-17 02:20:36 +04:00
2005-06-28 03:28:06 +04:00
while ( did & & did - > match_flags ) {
if ( pcmcia_devmatch ( p_dev , did ) )
return 1 ;
did + + ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-06-28 03:28:04 +04:00
# ifdef CONFIG_HOTPLUG
2005-11-16 11:00:00 +03:00
static int pcmcia_bus_uevent ( struct device * dev , char * * envp , int num_envp ,
char * buffer , int buffer_size )
2005-06-28 03:28:04 +04:00
{
struct pcmcia_device * p_dev ;
int i , length = 0 ;
u32 hash [ 4 ] = { 0 , 0 , 0 , 0 } ;
if ( ! dev )
return - ENODEV ;
p_dev = to_pcmcia_dev ( dev ) ;
/* calculate hashes */
for ( i = 0 ; i < 4 ; i + + ) {
if ( ! p_dev - > prod_id [ i ] )
continue ;
hash [ i ] = crc32 ( 0 , p_dev - > prod_id [ i ] , strlen ( p_dev - > prod_id [ i ] ) ) ;
}
i = 0 ;
2005-11-16 11:00:00 +03:00
if ( add_uevent_var ( envp , num_envp , & i ,
buffer , buffer_size , & length ,
" SOCKET_NO=%u " ,
p_dev - > socket - > sock ) )
2005-06-28 03:28:04 +04:00
return - ENOMEM ;
2005-11-16 11:00:00 +03:00
if ( add_uevent_var ( envp , num_envp , & i ,
buffer , buffer_size , & length ,
" DEVICE_NO=%02X " ,
p_dev - > device_no ) )
2005-06-28 03:28:04 +04:00
return - ENOMEM ;
2005-11-16 11:00:00 +03:00
if ( add_uevent_var ( envp , num_envp , & i ,
buffer , buffer_size , & length ,
" MODALIAS=pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X "
" pa%08Xpb%08Xpc%08Xpd%08X " ,
p_dev - > has_manf_id ? p_dev - > manf_id : 0 ,
p_dev - > has_card_id ? p_dev - > card_id : 0 ,
p_dev - > has_func_id ? p_dev - > func_id : 0 ,
p_dev - > func ,
p_dev - > device_no ,
hash [ 0 ] ,
hash [ 1 ] ,
hash [ 2 ] ,
hash [ 3 ] ) )
2005-06-28 03:28:04 +04:00
return - ENOMEM ;
envp [ i ] = NULL ;
return 0 ;
}
# else
2005-11-16 11:00:00 +03:00
static int pcmcia_bus_uevent ( struct device * dev , char * * envp , int num_envp ,
2005-06-28 03:28:04 +04:00
char * buffer , int buffer_size )
{
return - ENODEV ;
}
# endif
2005-04-17 02:20:36 +04:00
/************************ per-device sysfs output ***************************/
# define pcmcia_device_attr(field, test, format) \
2005-05-17 14:42:58 +04:00
static ssize_t field # # _show ( struct device * dev , struct device_attribute * attr , char * buf ) \
2005-04-17 02:20:36 +04:00
{ \
struct pcmcia_device * p_dev = to_pcmcia_dev ( dev ) ; \
return p_dev - > test ? sprintf ( buf , format , p_dev - > field ) : - ENODEV ; \
}
# define pcmcia_device_stringattr(name, field) \
2005-05-17 14:42:58 +04:00
static ssize_t name # # _show ( struct device * dev , struct device_attribute * attr , char * buf ) \
2005-04-17 02:20:36 +04:00
{ \
struct pcmcia_device * p_dev = to_pcmcia_dev ( dev ) ; \
return p_dev - > field ? sprintf ( buf , " %s \n " , p_dev - > field ) : - ENODEV ; \
}
pcmcia_device_attr ( func , socket , " 0x%02x \n " ) ;
pcmcia_device_attr ( func_id , has_func_id , " 0x%02x \n " ) ;
pcmcia_device_attr ( manf_id , has_manf_id , " 0x%04x \n " ) ;
pcmcia_device_attr ( card_id , has_card_id , " 0x%04x \n " ) ;
pcmcia_device_stringattr ( prod_id1 , prod_id [ 0 ] ) ;
pcmcia_device_stringattr ( prod_id2 , prod_id [ 1 ] ) ;
pcmcia_device_stringattr ( prod_id3 , prod_id [ 2 ] ) ;
pcmcia_device_stringattr ( prod_id4 , prod_id [ 3 ] ) ;
2006-01-10 21:19:37 +03:00
static ssize_t pcmcia_show_pm_state ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct pcmcia_device * p_dev = to_pcmcia_dev ( dev ) ;
2006-01-20 17:10:23 +03:00
if ( p_dev - > suspended )
2006-01-10 21:19:37 +03:00
return sprintf ( buf , " off \n " ) ;
else
return sprintf ( buf , " on \n " ) ;
}
static ssize_t pcmcia_store_pm_state ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct pcmcia_device * p_dev = to_pcmcia_dev ( dev ) ;
int ret = 0 ;
if ( ! count )
return - EINVAL ;
2006-01-20 17:10:23 +03:00
if ( ( ! p_dev - > suspended ) & & ! strncmp ( buf , " off " , 3 ) )
2006-01-10 21:19:37 +03:00
ret = dpm_runtime_suspend ( dev , PMSG_SUSPEND ) ;
2006-01-20 17:10:23 +03:00
else if ( p_dev - > suspended & & ! strncmp ( buf , " on " , 2 ) )
2006-01-10 21:19:37 +03:00
dpm_runtime_resume ( dev ) ;
return ret ? ret : count ;
}
2005-06-30 13:58:47 +04:00
static ssize_t modalias_show ( struct device * dev , struct device_attribute * attr , char * buf )
2005-06-28 03:29:01 +04:00
{
struct pcmcia_device * p_dev = to_pcmcia_dev ( dev ) ;
int i ;
u32 hash [ 4 ] = { 0 , 0 , 0 , 0 } ;
/* calculate hashes */
for ( i = 0 ; i < 4 ; i + + ) {
if ( ! p_dev - > prod_id [ i ] )
continue ;
hash [ i ] = crc32 ( 0 , p_dev - > prod_id [ i ] , strlen ( p_dev - > prod_id [ i ] ) ) ;
}
return sprintf ( buf , " pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X "
" pa%08Xpb%08Xpc%08Xpd%08X \n " ,
p_dev - > has_manf_id ? p_dev - > manf_id : 0 ,
p_dev - > has_card_id ? p_dev - > card_id : 0 ,
p_dev - > has_func_id ? p_dev - > func_id : 0 ,
p_dev - > func , p_dev - > device_no ,
hash [ 0 ] , hash [ 1 ] , hash [ 2 ] , hash [ 3 ] ) ;
}
2005-06-28 03:28:10 +04:00
2005-06-28 03:29:01 +04:00
static ssize_t pcmcia_store_allow_func_id_match ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t count )
2005-06-28 03:28:10 +04:00
{
struct pcmcia_device * p_dev = to_pcmcia_dev ( dev ) ;
2006-01-10 21:19:37 +03:00
if ( ! count )
return - EINVAL ;
2005-06-28 03:28:10 +04:00
2006-01-10 23:20:36 +03:00
mutex_lock ( & p_dev - > socket - > skt_mutex ) ;
2005-06-28 03:28:10 +04:00
p_dev - > allow_func_id_match = 1 ;
2006-01-10 23:20:36 +03:00
mutex_unlock ( & p_dev - > socket - > skt_mutex ) ;
2005-06-28 03:28:10 +04:00
bus_rescan_devices ( & pcmcia_bus_type ) ;
return count ;
}
2005-04-17 02:20:36 +04:00
static struct device_attribute pcmcia_dev_attrs [ ] = {
__ATTR ( function , 0444 , func_show , NULL ) ,
2006-01-10 21:19:37 +03:00
__ATTR ( pm_state , 0644 , pcmcia_show_pm_state , pcmcia_store_pm_state ) ,
2005-04-17 02:20:36 +04:00
__ATTR_RO ( func_id ) ,
__ATTR_RO ( manf_id ) ,
__ATTR_RO ( card_id ) ,
__ATTR_RO ( prod_id1 ) ,
__ATTR_RO ( prod_id2 ) ,
__ATTR_RO ( prod_id3 ) ,
__ATTR_RO ( prod_id4 ) ,
2005-06-28 03:29:01 +04:00
__ATTR_RO ( modalias ) ,
2005-06-28 03:28:10 +04:00
__ATTR ( allow_func_id_match , 0200 , NULL , pcmcia_store_allow_func_id_match ) ,
2005-04-17 02:20:36 +04:00
__ATTR_NULL ,
} ;
2006-01-06 02:02:03 +03:00
/* PM support, also needed for reset */
static int pcmcia_dev_suspend ( struct device * dev , pm_message_t state )
{
struct pcmcia_device * p_dev = to_pcmcia_dev ( dev ) ;
struct pcmcia_driver * p_drv = NULL ;
2006-01-20 17:10:23 +03:00
int ret = 0 ;
2006-01-06 02:02:03 +03:00
if ( dev - > driver )
p_drv = to_pcmcia_drv ( dev - > driver ) ;
2006-03-02 02:09:29 +03:00
if ( ! p_drv )
goto out ;
if ( p_drv - > suspend ) {
2006-03-02 02:02:33 +03:00
ret = p_drv - > suspend ( p_dev ) ;
if ( ret )
2006-01-20 17:10:23 +03:00
goto out ;
2006-03-02 02:02:33 +03:00
}
2006-01-06 02:02:03 +03:00
2006-03-02 02:09:29 +03:00
if ( p_dev - > device_no = = p_dev - > func )
pcmcia_release_configuration ( p_dev ) ;
2006-01-20 17:10:23 +03:00
out :
if ( ! ret )
p_dev - > suspended = 1 ;
return ret ;
2006-01-06 02:02:03 +03:00
}
static int pcmcia_dev_resume ( struct device * dev )
{
struct pcmcia_device * p_dev = to_pcmcia_dev ( dev ) ;
struct pcmcia_driver * p_drv = NULL ;
2006-01-20 17:10:23 +03:00
int ret = 0 ;
2006-01-06 02:02:03 +03:00
if ( dev - > driver )
p_drv = to_pcmcia_drv ( dev - > driver ) ;
2006-03-02 02:09:29 +03:00
if ( ! p_drv )
goto out ;
if ( p_dev - > device_no = = p_dev - > func ) {
ret = pcmcia_request_configuration ( p_dev , & p_dev - > conf ) ;
if ( ret )
goto out ;
2006-03-02 02:02:33 +03:00
}
2006-01-06 02:02:03 +03:00
2006-03-02 02:09:29 +03:00
if ( p_drv - > resume )
ret = p_drv - > resume ( p_dev ) ;
2006-01-20 17:10:23 +03:00
out :
if ( ! ret )
p_dev - > suspended = 0 ;
return ret ;
2006-01-06 02:02:03 +03:00
}
static int pcmcia_bus_suspend_callback ( struct device * dev , void * _data )
{
struct pcmcia_socket * skt = _data ;
struct pcmcia_device * p_dev = to_pcmcia_dev ( dev ) ;
if ( p_dev - > socket ! = skt )
return 0 ;
return dpm_runtime_suspend ( dev , PMSG_SUSPEND ) ;
}
static int pcmcia_bus_resume_callback ( struct device * dev , void * _data )
{
struct pcmcia_socket * skt = _data ;
struct pcmcia_device * p_dev = to_pcmcia_dev ( dev ) ;
if ( p_dev - > socket ! = skt )
return 0 ;
dpm_runtime_resume ( dev ) ;
return 0 ;
}
static int pcmcia_bus_resume ( struct pcmcia_socket * skt )
{
bus_for_each_dev ( & pcmcia_bus_type , NULL , skt , pcmcia_bus_resume_callback ) ;
return 0 ;
}
static int pcmcia_bus_suspend ( struct pcmcia_socket * skt )
{
if ( bus_for_each_dev ( & pcmcia_bus_type , NULL , skt ,
pcmcia_bus_suspend_callback ) ) {
pcmcia_bus_resume ( skt ) ;
return - EIO ;
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*======================================================================
The card status event handler .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* Normally, the event is passed to individual drivers after
* informing userspace . Only for CS_EVENT_CARD_REMOVAL this
* is inversed to maintain historic compatibility .
*/
static int ds_event ( struct pcmcia_socket * skt , event_t event , int priority )
{
2005-06-28 03:28:50 +04:00
struct pcmcia_socket * s = pcmcia_get_socket ( skt ) ;
2005-04-17 02:20:36 +04:00
2006-05-25 05:21:31 +04:00
if ( ! s ) {
printk ( KERN_ERR " PCMCIA obtaining reference to socket %p " \
" failed, event 0x%x lost! \n " , skt , event ) ;
return - ENODEV ;
}
2005-04-17 02:20:36 +04:00
ds_dbg ( 1 , " ds_event(0x%06x, %d, 0x%p) \n " ,
2005-06-28 03:28:50 +04:00
event , priority , skt ) ;
2005-04-17 02:20:36 +04:00
2005-11-14 23:25:51 +03:00
switch ( event ) {
2005-04-17 02:20:36 +04:00
case CS_EVENT_CARD_REMOVAL :
2005-06-28 03:28:50 +04:00
s - > pcmcia_state . present = 0 ;
2006-02-05 11:51:34 +03:00
pcmcia_card_remove ( skt , NULL ) ;
2005-06-28 03:28:50 +04:00
handle_event ( skt , event ) ;
2005-04-17 02:20:36 +04:00
break ;
2005-11-14 23:25:51 +03:00
2005-04-17 02:20:36 +04:00
case CS_EVENT_CARD_INSERTION :
2005-06-28 03:28:50 +04:00
s - > pcmcia_state . present = 1 ;
2005-04-17 02:20:36 +04:00
pcmcia_card_add ( skt ) ;
2005-06-28 03:28:50 +04:00
handle_event ( skt , event ) ;
2005-04-17 02:20:36 +04:00
break ;
case CS_EVENT_EJECTION_REQUEST :
break ;
2006-01-06 02:02:03 +03:00
case CS_EVENT_PM_SUSPEND :
case CS_EVENT_PM_RESUME :
case CS_EVENT_RESET_PHYSICAL :
case CS_EVENT_CARD_RESET :
2005-04-17 02:20:36 +04:00
default :
2005-06-28 03:28:50 +04:00
handle_event ( skt , event ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2005-06-28 03:28:50 +04:00
pcmcia_put_socket ( s ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
} /* ds_event */
2006-03-05 13:04:33 +03:00
struct pcmcia_device * pcmcia_dev_present ( struct pcmcia_device * _p_dev )
{
struct pcmcia_device * p_dev ;
struct pcmcia_device * ret = NULL ;
p_dev = pcmcia_get_dev ( _p_dev ) ;
if ( ! p_dev )
return NULL ;
if ( ! p_dev - > socket - > pcmcia_state . present )
goto out ;
if ( p_dev - > _removed )
goto out ;
if ( p_dev - > suspended )
goto out ;
ret = p_dev ;
out :
pcmcia_put_dev ( p_dev ) ;
return ret ;
}
EXPORT_SYMBOL ( pcmcia_dev_present ) ;
2005-06-28 03:28:49 +04:00
static struct pcmcia_callback pcmcia_bus_callback = {
. owner = THIS_MODULE ,
. event = ds_event ,
. requery = pcmcia_bus_rescan ,
2006-01-06 02:02:03 +03:00
. suspend = pcmcia_bus_suspend ,
. resume = pcmcia_bus_resume ,
2005-06-28 03:28:49 +04:00
} ;
2005-09-15 11:01:36 +04:00
static int __devinit pcmcia_bus_add_socket ( struct class_device * class_dev ,
struct class_interface * class_intf )
2005-04-17 02:20:36 +04:00
{
struct pcmcia_socket * socket = class_get_devdata ( class_dev ) ;
int ret ;
2005-06-28 03:28:50 +04:00
socket = pcmcia_get_socket ( socket ) ;
if ( ! socket ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " PCMCIA obtaining reference to socket %p failed \n " , socket ) ;
return - ENODEV ;
}
/*
* Ugly . But we want to wait for the socket threads to have started up .
* We really should let the drivers themselves drive some of this . .
*/
msleep ( 250 ) ;
2005-06-28 03:28:47 +04:00
# ifdef CONFIG_PCMCIA_IOCTL
2005-06-28 03:28:50 +04:00
init_waitqueue_head ( & socket - > queue ) ;
2005-06-28 03:28:47 +04:00
# endif
2005-06-28 03:28:50 +04:00
INIT_LIST_HEAD ( & socket - > devices_list ) ;
INIT_WORK ( & socket - > device_add , pcmcia_delayed_add_pseudo_device , socket ) ;
memset ( & socket - > pcmcia_state , 0 , sizeof ( u8 ) ) ;
socket - > device_count = 0 ;
2005-04-17 02:20:36 +04:00
2005-06-28 03:28:49 +04:00
ret = pccard_register_pcmcia ( socket , & pcmcia_bus_callback ) ;
2005-04-17 02:20:36 +04:00
if ( ret ) {
printk ( KERN_ERR " PCMCIA registration PCCard core failed for socket %p \n " , socket ) ;
2005-06-28 03:28:50 +04:00
pcmcia_put_socket ( socket ) ;
2005-04-17 02:20:36 +04:00
return ( ret ) ;
}
return 0 ;
}
2005-09-15 11:01:36 +04:00
static void pcmcia_bus_remove_socket ( struct class_device * class_dev ,
struct class_interface * class_intf )
2005-04-17 02:20:36 +04:00
{
struct pcmcia_socket * socket = class_get_devdata ( class_dev ) ;
2005-06-28 03:28:50 +04:00
if ( ! socket )
2005-04-17 02:20:36 +04:00
return ;
2005-06-28 03:28:50 +04:00
socket - > pcmcia_state . dead = 1 ;
2005-04-17 02:20:36 +04:00
pccard_register_pcmcia ( socket , NULL ) ;
2005-06-28 03:28:50 +04:00
pcmcia_put_socket ( socket ) ;
2005-04-17 02:20:36 +04:00
return ;
}
/* the pcmcia_bus_interface is used to handle pcmcia socket devices */
static struct class_interface pcmcia_bus_interface = {
. class = & pcmcia_socket_class ,
. add = & pcmcia_bus_add_socket ,
. remove = & pcmcia_bus_remove_socket ,
} ;
2005-06-28 03:28:47 +04:00
struct bus_type pcmcia_bus_type = {
2005-04-17 02:20:36 +04:00
. name = " pcmcia " ,
2005-11-16 11:00:00 +03:00
. uevent = pcmcia_bus_uevent ,
2005-04-17 02:20:36 +04:00
. match = pcmcia_bus_match ,
. dev_attrs = pcmcia_dev_attrs ,
2006-01-05 17:40:58 +03:00
. probe = pcmcia_device_probe ,
. remove = pcmcia_device_remove ,
2006-01-06 02:02:03 +03:00
. suspend = pcmcia_dev_suspend ,
. resume = pcmcia_dev_resume ,
2005-04-17 02:20:36 +04:00
} ;
static int __init init_pcmcia_bus ( void )
{
spin_lock_init ( & pcmcia_dev_list_lock ) ;
bus_register ( & pcmcia_bus_type ) ;
class_interface_register ( & pcmcia_bus_interface ) ;
2005-06-28 03:28:47 +04:00
pcmcia_setup_ioctl ( ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
fs_initcall ( init_pcmcia_bus ) ; /* one level after subsys_initcall so that
* pcmcia_socket_class is already registered */
static void __exit exit_pcmcia_bus ( void )
{
2005-06-28 03:28:47 +04:00
pcmcia_cleanup_ioctl ( ) ;
2005-04-17 02:20:36 +04:00
2005-06-28 03:28:47 +04:00
class_interface_unregister ( & pcmcia_bus_interface ) ;
2005-04-17 02:20:36 +04:00
bus_unregister ( & pcmcia_bus_type ) ;
}
module_exit ( exit_pcmcia_bus ) ;
MODULE_ALIAS ( " ds " ) ;