2005-04-17 02:20:36 +04:00
/*
* driver . c - device id matching , driver model , etc .
*
* Copyright 2002 Adam Belay < ambx1 @ neo . rr . com >
*/
# include <linux/string.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/ctype.h>
# include <linux/slab.h>
# include <linux/pnp.h>
# include "base.h"
static int compare_func ( const char * ida , const char * idb )
{
int i ;
2007-07-26 21:41:21 +04:00
2005-04-17 02:20:36 +04:00
/* we only need to compare the last 4 chars */
2007-07-26 21:41:20 +04:00
for ( i = 3 ; i < 7 ; i + + ) {
2005-04-17 02:20:36 +04:00
if ( ida [ i ] ! = ' X ' & &
2007-07-26 21:41:20 +04:00
idb [ i ] ! = ' X ' & & toupper ( ida [ i ] ) ! = toupper ( idb [ i ] ) )
2005-04-17 02:20:36 +04:00
return 0 ;
}
return 1 ;
}
int compare_pnp_id ( struct pnp_id * pos , const char * id )
{
if ( ! pos | | ! id | | ( strlen ( id ) ! = 7 ) )
return 0 ;
2007-07-26 21:41:20 +04:00
if ( memcmp ( id , " ANYDEVS " , 7 ) = = 0 )
2005-04-17 02:20:36 +04:00
return 1 ;
2007-07-26 21:41:20 +04:00
while ( pos ) {
if ( memcmp ( pos - > id , id , 3 ) = = 0 )
if ( compare_func ( pos - > id , id ) = = 1 )
2005-04-17 02:20:36 +04:00
return 1 ;
pos = pos - > next ;
}
return 0 ;
}
2007-07-26 21:41:20 +04:00
static const struct pnp_device_id * match_device ( struct pnp_driver * drv ,
struct pnp_dev * dev )
2005-04-17 02:20:36 +04:00
{
const struct pnp_device_id * drv_id = drv - > id_table ;
2007-07-26 21:41:21 +04:00
2005-04-17 02:20:36 +04:00
if ( ! drv_id )
return NULL ;
while ( * drv_id - > id ) {
if ( compare_pnp_id ( dev - > id , drv_id - > id ) )
return drv_id ;
drv_id + + ;
}
return NULL ;
}
int pnp_device_attach ( struct pnp_dev * pnp_dev )
{
spin_lock ( & pnp_lock ) ;
2007-07-26 21:41:20 +04:00
if ( pnp_dev - > status ! = PNP_READY ) {
2005-04-17 02:20:36 +04:00
spin_unlock ( & pnp_lock ) ;
return - EBUSY ;
}
pnp_dev - > status = PNP_ATTACHED ;
spin_unlock ( & pnp_lock ) ;
return 0 ;
}
void pnp_device_detach ( struct pnp_dev * pnp_dev )
{
spin_lock ( & pnp_lock ) ;
if ( pnp_dev - > status = = PNP_ATTACHED )
pnp_dev - > status = PNP_READY ;
spin_unlock ( & pnp_lock ) ;
pnp_disable_dev ( pnp_dev ) ;
}
static int pnp_device_probe ( struct device * dev )
{
int error ;
struct pnp_driver * pnp_drv ;
struct pnp_dev * pnp_dev ;
const struct pnp_device_id * dev_id = NULL ;
pnp_dev = to_pnp_dev ( dev ) ;
pnp_drv = to_pnp_driver ( dev - > driver ) ;
error = pnp_device_attach ( pnp_dev ) ;
if ( error < 0 )
return error ;
if ( pnp_dev - > active = = 0 ) {
if ( ! ( pnp_drv - > flags & PNP_DRIVER_RES_DO_NOT_CHANGE ) ) {
error = pnp_activate_dev ( pnp_dev ) ;
if ( error < 0 )
return error ;
}
} else if ( ( pnp_drv - > flags & PNP_DRIVER_RES_DISABLE )
2007-07-26 21:41:20 +04:00
= = PNP_DRIVER_RES_DISABLE ) {
2005-04-17 02:20:36 +04:00
error = pnp_disable_dev ( pnp_dev ) ;
if ( error < 0 )
return error ;
}
error = 0 ;
if ( pnp_drv - > probe ) {
dev_id = match_device ( pnp_drv , pnp_dev ) ;
if ( dev_id ! = NULL )
error = pnp_drv - > probe ( pnp_dev , dev_id ) ;
}
2007-07-26 21:41:20 +04:00
if ( error > = 0 ) {
2005-04-17 02:20:36 +04:00
pnp_dev - > driver = pnp_drv ;
error = 0 ;
} else
goto fail ;
2007-10-17 10:31:10 +04:00
2005-04-17 02:20:36 +04:00
return error ;
2007-08-15 20:32:08 +04:00
fail :
2005-04-17 02:20:36 +04:00
pnp_device_detach ( pnp_dev ) ;
return error ;
}
static int pnp_device_remove ( struct device * dev )
{
2007-07-26 21:41:20 +04:00
struct pnp_dev * pnp_dev = to_pnp_dev ( dev ) ;
struct pnp_driver * drv = pnp_dev - > driver ;
2005-04-17 02:20:36 +04:00
if ( drv ) {
if ( drv - > remove )
drv - > remove ( pnp_dev ) ;
pnp_dev - > driver = NULL ;
}
pnp_device_detach ( pnp_dev ) ;
return 0 ;
}
2009-09-22 04:04:52 +04:00
static void pnp_device_shutdown ( struct device * dev )
{
struct pnp_dev * pnp_dev = to_pnp_dev ( dev ) ;
struct pnp_driver * drv = pnp_dev - > driver ;
if ( drv & & drv - > shutdown )
drv - > shutdown ( pnp_dev ) ;
}
2005-04-17 02:20:36 +04:00
static int pnp_bus_match ( struct device * dev , struct device_driver * drv )
{
2007-07-26 21:41:20 +04:00
struct pnp_dev * pnp_dev = to_pnp_dev ( dev ) ;
struct pnp_driver * pnp_drv = to_pnp_driver ( drv ) ;
2007-07-26 21:41:21 +04:00
2005-04-17 02:20:36 +04:00
if ( match_device ( pnp_drv , pnp_dev ) = = NULL )
return 0 ;
return 1 ;
}
2013-07-27 01:07:26 +04:00
static int __pnp_bus_suspend ( struct device * dev , pm_message_t state )
2005-11-29 11:09:32 +03:00
{
2007-07-26 21:41:20 +04:00
struct pnp_dev * pnp_dev = to_pnp_dev ( dev ) ;
struct pnp_driver * pnp_drv = pnp_dev - > driver ;
2005-11-29 11:09:32 +03:00
int error ;
if ( ! pnp_drv )
return 0 ;
2013-09-12 01:23:09 +04:00
if ( pnp_drv - > driver . pm & & pnp_drv - > driver . pm - > suspend ) {
error = pnp_drv - > driver . pm - > suspend ( dev ) ;
suspend_report_result ( pnp_drv - > driver . pm - > suspend , error ) ;
if ( error )
return error ;
}
2005-11-29 11:09:32 +03:00
if ( pnp_drv - > suspend ) {
error = pnp_drv - > suspend ( pnp_dev , state ) ;
if ( error )
return error ;
}
2008-02-06 12:40:05 +03:00
if ( pnp_can_disable ( pnp_dev ) ) {
2007-07-26 21:41:20 +04:00
error = pnp_stop_dev ( pnp_dev ) ;
if ( error )
return error ;
2005-11-29 11:09:32 +03:00
}
2005-11-29 11:09:32 +03:00
2015-01-22 19:50:24 +03:00
if ( pnp_can_suspend ( pnp_dev ) )
2007-07-20 06:03:20 +04:00
pnp_dev - > protocol - > suspend ( pnp_dev , state ) ;
2005-11-29 11:09:32 +03:00
return 0 ;
}
2013-07-27 01:07:26 +04:00
static int pnp_bus_suspend ( struct device * dev )
{
return __pnp_bus_suspend ( dev , PMSG_SUSPEND ) ;
}
static int pnp_bus_freeze ( struct device * dev )
{
return __pnp_bus_suspend ( dev , PMSG_FREEZE ) ;
}
2013-12-05 05:01:55 +04:00
static int pnp_bus_poweroff ( struct device * dev )
{
return __pnp_bus_suspend ( dev , PMSG_HIBERNATE ) ;
}
2005-11-29 11:09:32 +03:00
static int pnp_bus_resume ( struct device * dev )
2005-11-29 11:09:32 +03:00
{
2007-07-26 21:41:20 +04:00
struct pnp_dev * pnp_dev = to_pnp_dev ( dev ) ;
struct pnp_driver * pnp_drv = pnp_dev - > driver ;
2005-11-29 11:09:32 +03:00
int error ;
if ( ! pnp_drv )
return 0 ;
2011-01-10 23:23:16 +03:00
if ( pnp_dev - > protocol - > resume ) {
error = pnp_dev - > protocol - > resume ( pnp_dev ) ;
if ( error )
return error ;
}
2007-07-20 06:03:20 +04:00
2008-02-06 12:40:05 +03:00
if ( pnp_can_write ( pnp_dev ) ) {
2005-11-29 11:09:32 +03:00
error = pnp_start_dev ( pnp_dev ) ;
if ( error )
return error ;
}
2005-11-29 11:09:32 +03:00
2013-09-12 01:23:09 +04:00
if ( pnp_drv - > driver . pm & & pnp_drv - > driver . pm - > resume ) {
error = pnp_drv - > driver . pm - > resume ( dev ) ;
if ( error )
return error ;
}
2008-02-06 12:40:05 +03:00
if ( pnp_drv - > resume ) {
error = pnp_drv - > resume ( pnp_dev ) ;
if ( error )
return error ;
}
2005-11-29 11:09:32 +03:00
return 0 ;
2005-11-29 11:09:32 +03:00
}
2005-04-17 02:20:36 +04:00
2013-07-27 01:07:26 +04:00
static const struct dev_pm_ops pnp_bus_dev_pm_ops = {
2013-12-05 05:01:55 +04:00
/* Suspend callbacks */
2013-07-27 01:07:26 +04:00
. suspend = pnp_bus_suspend ,
. resume = pnp_bus_resume ,
2013-12-05 05:01:55 +04:00
/* Hibernate callbacks */
. freeze = pnp_bus_freeze ,
. thaw = pnp_bus_resume ,
. poweroff = pnp_bus_poweroff ,
. restore = pnp_bus_resume ,
2013-07-27 01:07:26 +04:00
} ;
2005-04-17 02:20:36 +04:00
struct bus_type pnp_bus_type = {
2007-07-26 21:41:21 +04:00
. name = " pnp " ,
. match = pnp_bus_match ,
. probe = pnp_device_probe ,
. remove = pnp_device_remove ,
2009-09-22 04:04:52 +04:00
. shutdown = pnp_device_shutdown ,
2013-07-27 01:07:26 +04:00
. pm = & pnp_bus_dev_pm_ops ,
2013-10-07 10:55:42 +04:00
. dev_groups = pnp_dev_groups ,
2005-04-17 02:20:36 +04:00
} ;
int pnp_register_driver ( struct pnp_driver * drv )
{
drv - > driver . name = drv - > name ;
drv - > driver . bus = & pnp_bus_type ;
2006-03-27 13:17:08 +04:00
return driver_register ( & drv - > driver ) ;
2005-04-17 02:20:36 +04:00
}
void pnp_unregister_driver ( struct pnp_driver * drv )
{
driver_unregister ( & drv - > driver ) ;
}
/**
* pnp_add_id - adds an EISA id to the specified device
* @ dev : pointer to the desired device
2008-04-29 02:33:52 +04:00
* @ id : pointer to an EISA id string
2005-04-17 02:20:36 +04:00
*/
2010-10-01 12:54:00 +04:00
struct pnp_id * pnp_add_id ( struct pnp_dev * dev , const char * id )
2005-04-17 02:20:36 +04:00
{
2008-04-29 02:33:52 +04:00
struct pnp_id * dev_id , * ptr ;
2007-07-26 21:41:21 +04:00
2008-04-29 02:33:52 +04:00
dev_id = kzalloc ( sizeof ( struct pnp_id ) , GFP_KERNEL ) ;
if ( ! dev_id )
return NULL ;
dev_id - > id [ 0 ] = id [ 0 ] ;
dev_id - > id [ 1 ] = id [ 1 ] ;
dev_id - > id [ 2 ] = id [ 2 ] ;
dev_id - > id [ 3 ] = tolower ( id [ 3 ] ) ;
dev_id - > id [ 4 ] = tolower ( id [ 4 ] ) ;
dev_id - > id [ 5 ] = tolower ( id [ 5 ] ) ;
dev_id - > id [ 6 ] = tolower ( id [ 6 ] ) ;
dev_id - > id [ 7 ] = ' \0 ' ;
dev_id - > next = NULL ;
2005-04-17 02:20:36 +04:00
ptr = dev - > id ;
while ( ptr & & ptr - > next )
ptr = ptr - > next ;
if ( ptr )
2008-04-29 02:33:52 +04:00
ptr - > next = dev_id ;
2005-04-17 02:20:36 +04:00
else
2008-04-29 02:33:52 +04:00
dev - > id = dev_id ;
return dev_id ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( pnp_register_driver ) ;
EXPORT_SYMBOL ( pnp_unregister_driver ) ;
EXPORT_SYMBOL ( pnp_device_attach ) ;
EXPORT_SYMBOL ( pnp_device_detach ) ;