2005-04-17 02:20:36 +04:00
/*
* sys . c - pseudo - bus for system ' devices ' ( cpus , PICs , timers , etc )
*
* Copyright ( c ) 2002 - 3 Patrick Mochel
* 2002 - 3 Open Source Development Lab
*
* This file is released under the GPLv2
*
* This exports a ' system ' bus type .
* By default , a ' sys ' bus gets added to the root of the system . There will
* always be core system devices . Devices can use sysdev_register ( ) to
* add themselves as children of the system bus .
*/
# include <linux/sysdev.h>
# include <linux/err.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/string.h>
2005-04-17 02:25:24 +04:00
# include <linux/pm.h>
2006-01-19 19:30:17 +03:00
# include <linux/device.h>
2007-05-24 01:19:42 +04:00
# include <linux/mutex.h>
2009-03-17 00:34:06 +03:00
# include <linux/interrupt.h>
2005-04-17 02:20:36 +04:00
2006-01-19 19:30:17 +03:00
# include "base.h"
2005-04-17 02:20:36 +04:00
# define to_sysdev(k) container_of(k, struct sys_device, kobj)
# define to_sysdev_attr(a) container_of(a, struct sysdev_attribute, attr)
static ssize_t
2009-03-03 13:36:02 +03:00
sysdev_show ( struct kobject * kobj , struct attribute * attr , char * buffer )
2005-04-17 02:20:36 +04:00
{
2009-03-03 13:36:02 +03:00
struct sys_device * sysdev = to_sysdev ( kobj ) ;
struct sysdev_attribute * sysdev_attr = to_sysdev_attr ( attr ) ;
2005-04-17 02:20:36 +04:00
if ( sysdev_attr - > show )
2008-07-01 20:48:41 +04:00
return sysdev_attr - > show ( sysdev , sysdev_attr , buffer ) ;
2005-04-29 10:23:47 +04:00
return - EIO ;
2005-04-17 02:20:36 +04:00
}
static ssize_t
2009-03-03 13:36:02 +03:00
sysdev_store ( struct kobject * kobj , struct attribute * attr ,
const char * buffer , size_t count )
2005-04-17 02:20:36 +04:00
{
2009-03-03 13:36:02 +03:00
struct sys_device * sysdev = to_sysdev ( kobj ) ;
struct sysdev_attribute * sysdev_attr = to_sysdev_attr ( attr ) ;
2005-04-17 02:20:36 +04:00
if ( sysdev_attr - > store )
2008-07-01 20:48:41 +04:00
return sysdev_attr - > store ( sysdev , sysdev_attr , buffer , count ) ;
2005-04-29 10:23:47 +04:00
return - EIO ;
2005-04-17 02:20:36 +04:00
}
2010-01-19 04:58:23 +03:00
static const struct sysfs_ops sysfs_ops = {
2005-04-17 02:20:36 +04:00
. show = sysdev_show ,
. store = sysdev_store ,
} ;
static struct kobj_type ktype_sysdev = {
. sysfs_ops = & sysfs_ops ,
} ;
2009-03-03 13:36:02 +03:00
int sysdev_create_file ( struct sys_device * s , struct sysdev_attribute * a )
2005-04-17 02:20:36 +04:00
{
return sysfs_create_file ( & s - > kobj , & a - > attr ) ;
}
2009-03-03 13:36:02 +03:00
void sysdev_remove_file ( struct sys_device * s , struct sysdev_attribute * a )
2005-04-17 02:20:36 +04:00
{
sysfs_remove_file ( & s - > kobj , & a - > attr ) ;
}
EXPORT_SYMBOL_GPL ( sysdev_create_file ) ;
EXPORT_SYMBOL_GPL ( sysdev_remove_file ) ;
2006-05-08 09:45:57 +04:00
# define to_sysdev_class(k) container_of(k, struct sysdev_class, kset.kobj)
# define to_sysdev_class_attr(a) container_of(a, \
struct sysdev_class_attribute , attr )
static ssize_t sysdev_class_show ( struct kobject * kobj , struct attribute * attr ,
char * buffer )
{
2009-03-03 13:36:02 +03:00
struct sysdev_class * class = to_sysdev_class ( kobj ) ;
2006-05-08 09:45:57 +04:00
struct sysdev_class_attribute * class_attr = to_sysdev_class_attr ( attr ) ;
if ( class_attr - > show )
2010-01-05 14:47:58 +03:00
return class_attr - > show ( class , class_attr , buffer ) ;
2006-05-08 09:45:57 +04:00
return - EIO ;
}
static ssize_t sysdev_class_store ( struct kobject * kobj , struct attribute * attr ,
const char * buffer , size_t count )
{
2009-03-03 13:36:02 +03:00
struct sysdev_class * class = to_sysdev_class ( kobj ) ;
struct sysdev_class_attribute * class_attr = to_sysdev_class_attr ( attr ) ;
2006-05-08 09:45:57 +04:00
if ( class_attr - > store )
2010-01-05 14:47:58 +03:00
return class_attr - > store ( class , class_attr , buffer , count ) ;
2006-05-08 09:45:57 +04:00
return - EIO ;
}
2010-01-19 04:58:23 +03:00
static const struct sysfs_ops sysfs_class_ops = {
2006-05-08 09:45:57 +04:00
. show = sysdev_class_show ,
. store = sysdev_class_store ,
} ;
static struct kobj_type ktype_sysdev_class = {
. sysfs_ops = & sysfs_class_ops ,
} ;
int sysdev_class_create_file ( struct sysdev_class * c ,
struct sysdev_class_attribute * a )
{
return sysfs_create_file ( & c - > kset . kobj , & a - > attr ) ;
}
EXPORT_SYMBOL_GPL ( sysdev_class_create_file ) ;
void sysdev_class_remove_file ( struct sysdev_class * c ,
struct sysdev_class_attribute * a )
{
sysfs_remove_file ( & c - > kset . kobj , & a - > attr ) ;
}
EXPORT_SYMBOL_GPL ( sysdev_class_remove_file ) ;
2007-11-01 18:29:06 +03:00
static struct kset * system_kset ;
2005-04-17 02:20:36 +04:00
2009-03-03 13:36:02 +03:00
int sysdev_class_register ( struct sysdev_class * cls )
2005-04-17 02:20:36 +04:00
{
2009-05-11 10:18:55 +04:00
int retval ;
2008-06-12 22:00:34 +04:00
pr_debug ( " Registering sysdev class '%s' \n " , cls - > name ) ;
2005-04-17 02:20:36 +04:00
INIT_LIST_HEAD ( & cls - > drivers ) ;
2008-03-09 01:07:16 +03:00
memset ( & cls - > kset . kobj , 0x00 , sizeof ( struct kobject ) ) ;
2007-11-01 18:29:06 +03:00
cls - > kset . kobj . parent = & system_kset - > kobj ;
2007-10-16 20:11:44 +04:00
cls - > kset . kobj . ktype = & ktype_sysdev_class ;
2007-11-01 18:29:06 +03:00
cls - > kset . kobj . kset = system_kset ;
2009-05-11 10:18:55 +04:00
2009-06-03 02:39:55 +04:00
retval = kobject_set_name ( & cls - > kset . kobj , " %s " , cls - > name ) ;
2009-05-11 10:18:55 +04:00
if ( retval )
return retval ;
2010-01-05 14:48:02 +03:00
retval = kset_register ( & cls - > kset ) ;
if ( ! retval & & cls - > attrs )
retval = sysfs_create_files ( & cls - > kset . kobj ,
( const struct attribute * * ) cls - > attrs ) ;
return retval ;
2005-04-17 02:20:36 +04:00
}
2009-03-03 13:36:02 +03:00
void sysdev_class_unregister ( struct sysdev_class * cls )
2005-04-17 02:20:36 +04:00
{
pr_debug ( " Unregistering sysdev class '%s' \n " ,
kobject_name ( & cls - > kset . kobj ) ) ;
2010-01-05 14:48:02 +03:00
if ( cls - > attrs )
sysfs_remove_files ( & cls - > kset . kobj ,
( const struct attribute * * ) cls - > attrs ) ;
2005-04-17 02:20:36 +04:00
kset_unregister ( & cls - > kset ) ;
}
EXPORT_SYMBOL_GPL ( sysdev_class_register ) ;
EXPORT_SYMBOL_GPL ( sysdev_class_unregister ) ;
2007-05-24 01:19:42 +04:00
static DEFINE_MUTEX ( sysdev_drivers_lock ) ;
2005-04-17 02:20:36 +04:00
/**
* sysdev_driver_register - Register auxillary driver
2007-08-19 11:51:14 +04:00
* @ cls : Device class driver belongs to .
2005-04-17 02:20:36 +04:00
* @ drv : Driver .
*
2007-08-19 11:51:14 +04:00
* @ drv is inserted into @ cls - > drivers to be
2005-04-17 02:20:36 +04:00
* called on each operation on devices of that class . The refcount
* of @ cls is incremented .
*/
2007-08-19 11:51:14 +04:00
int sysdev_driver_register ( struct sysdev_class * cls , struct sysdev_driver * drv )
2005-04-17 02:20:36 +04:00
{
2007-08-19 11:51:14 +04:00
int err = 0 ;
2008-03-05 02:09:06 +03:00
if ( ! cls ) {
2011-02-01 19:19:57 +03:00
WARN ( 1 , KERN_WARNING " sysdev: invalid class passed to %s! \n " ,
__func__ ) ;
2008-03-05 02:09:06 +03:00
return - EINVAL ;
}
/* Check whether this driver has already been added to a class. */
2008-07-26 06:45:39 +04:00
if ( drv - > entry . next & & ! list_empty ( & drv - > entry ) )
WARN ( 1 , KERN_WARNING " sysdev: class %s: driver (%p) has already "
2008-03-05 02:09:06 +03:00
" been registered to a class, something is wrong, but "
" will forge on! \n " , cls - > name , drv ) ;
2007-05-24 01:19:42 +04:00
mutex_lock ( & sysdev_drivers_lock ) ;
2005-04-17 02:20:36 +04:00
if ( cls & & kset_get ( & cls - > kset ) ) {
list_add_tail ( & drv - > entry , & cls - > drivers ) ;
/* If devices of this class already exist, tell the driver */
if ( drv - > add ) {
struct sys_device * dev ;
list_for_each_entry ( dev , & cls - > kset . list , kobj . entry )
drv - > add ( dev ) ;
}
2007-08-19 11:51:14 +04:00
} else {
err = - EINVAL ;
2008-07-26 06:45:39 +04:00
WARN ( 1 , KERN_ERR " %s: invalid device class \n " , __func__ ) ;
2007-08-19 11:51:14 +04:00
}
2007-05-24 01:19:42 +04:00
mutex_unlock ( & sysdev_drivers_lock ) ;
2007-08-19 11:51:14 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
/**
* sysdev_driver_unregister - Remove an auxillary driver .
* @ cls : Class driver belongs to .
* @ drv : Driver .
*/
2009-03-03 13:36:02 +03:00
void sysdev_driver_unregister ( struct sysdev_class * cls ,
struct sysdev_driver * drv )
2005-04-17 02:20:36 +04:00
{
2007-05-24 01:19:42 +04:00
mutex_lock ( & sysdev_drivers_lock ) ;
2005-04-17 02:20:36 +04:00
list_del_init ( & drv - > entry ) ;
if ( cls ) {
if ( drv - > remove ) {
struct sys_device * dev ;
list_for_each_entry ( dev , & cls - > kset . list , kobj . entry )
drv - > remove ( dev ) ;
}
kset_put ( & cls - > kset ) ;
}
2007-05-24 01:19:42 +04:00
mutex_unlock ( & sysdev_drivers_lock ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL_GPL ( sysdev_driver_register ) ;
EXPORT_SYMBOL_GPL ( sysdev_driver_unregister ) ;
/**
* sysdev_register - add a system device to the tree
* @ sysdev : device in question
*
*/
2009-03-03 13:36:02 +03:00
int sysdev_register ( struct sys_device * sysdev )
2005-04-17 02:20:36 +04:00
{
int error ;
2009-03-03 13:36:02 +03:00
struct sysdev_class * cls = sysdev - > cls ;
2005-04-17 02:20:36 +04:00
if ( ! cls )
return - EINVAL ;
2008-06-12 22:00:34 +04:00
pr_debug ( " Registering sys device of class '%s' \n " ,
kobject_name ( & cls - > kset . kobj ) ) ;
2007-12-17 22:54:39 +03:00
2008-03-09 01:07:16 +03:00
/* initialize the kobject to 0, in case it had previously been used */
memset ( & sysdev - > kobj , 0x00 , sizeof ( struct kobject ) ) ;
2005-04-17 02:20:36 +04:00
/* Make sure the kset is set */
sysdev - > kobj . kset = & cls - > kset ;
/* Register the object */
2007-12-17 22:54:39 +03:00
error = kobject_init_and_add ( & sysdev - > kobj , & ktype_sysdev , NULL ,
" %s%d " , kobject_name ( & cls - > kset . kobj ) ,
sysdev - > id ) ;
2005-04-17 02:20:36 +04:00
if ( ! error ) {
2009-03-03 13:36:02 +03:00
struct sysdev_driver * drv ;
2005-04-17 02:20:36 +04:00
2008-06-12 22:00:34 +04:00
pr_debug ( " Registering sys device '%s' \n " ,
kobject_name ( & sysdev - > kobj ) ) ;
2007-05-24 01:19:42 +04:00
mutex_lock ( & sysdev_drivers_lock ) ;
2005-04-17 02:20:36 +04:00
/* Generic notification is implicit, because it's that
* code that should have called us .
*/
/* Notify class auxillary drivers */
list_for_each_entry ( drv , & cls - > drivers , entry ) {
if ( drv - > add )
drv - > add ( sysdev ) ;
}
2007-05-24 01:19:42 +04:00
mutex_unlock ( & sysdev_drivers_lock ) ;
2009-07-24 13:31:41 +04:00
kobject_uevent ( & sysdev - > kobj , KOBJ_ADD ) ;
2005-04-17 02:20:36 +04:00
}
2008-06-12 22:00:34 +04:00
2005-04-17 02:20:36 +04:00
return error ;
}
2009-03-03 13:36:02 +03:00
void sysdev_unregister ( struct sys_device * sysdev )
2005-04-17 02:20:36 +04:00
{
2009-03-03 13:36:02 +03:00
struct sysdev_driver * drv ;
2005-04-17 02:20:36 +04:00
2007-05-24 01:19:42 +04:00
mutex_lock ( & sysdev_drivers_lock ) ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( drv , & sysdev - > cls - > drivers , entry ) {
if ( drv - > remove )
drv - > remove ( sysdev ) ;
}
2007-05-24 01:19:42 +04:00
mutex_unlock ( & sysdev_drivers_lock ) ;
2005-04-17 02:20:36 +04:00
2007-12-20 19:13:05 +03:00
kobject_put ( & sysdev - > kobj ) ;
2005-04-17 02:20:36 +04:00
}
/**
* sysdev_shutdown - Shut down all system devices .
*
* Loop over each class of system devices , and the devices in each
* of those classes . For each device , we call the shutdown method for
2007-08-19 11:51:14 +04:00
* each driver registered for the device - the auxillaries ,
2005-04-17 02:20:36 +04:00
* and the class driver .
*
* Note : The list is iterated in reverse order , so that we shut down
2009-01-13 01:35:46 +03:00
* child devices before we shut down their parents . The list ordering
2005-04-17 02:20:36 +04:00
* is guaranteed by virtue of the fact that child devices are registered
* after their parents .
*/
void sysdev_shutdown ( void )
{
2009-03-03 13:36:02 +03:00
struct sysdev_class * cls ;
2005-04-17 02:20:36 +04:00
pr_debug ( " Shutting Down System Devices \n " ) ;
2007-05-24 01:19:42 +04:00
mutex_lock ( & sysdev_drivers_lock ) ;
2007-11-01 18:29:06 +03:00
list_for_each_entry_reverse ( cls , & system_kset - > list , kset . kobj . entry ) {
2009-03-03 13:36:02 +03:00
struct sys_device * sysdev ;
2005-04-17 02:20:36 +04:00
pr_debug ( " Shutting down type '%s': \n " ,
kobject_name ( & cls - > kset . kobj ) ) ;
list_for_each_entry ( sysdev , & cls - > kset . list , kobj . entry ) {
2009-03-03 13:36:02 +03:00
struct sysdev_driver * drv ;
2005-04-17 02:20:36 +04:00
pr_debug ( " %s \n " , kobject_name ( & sysdev - > kobj ) ) ;
2007-08-19 11:51:14 +04:00
/* Call auxillary drivers first */
2005-04-17 02:20:36 +04:00
list_for_each_entry ( drv , & cls - > drivers , entry ) {
if ( drv - > shutdown )
drv - > shutdown ( sysdev ) ;
}
/* Now call the generic one */
if ( cls - > shutdown )
cls - > shutdown ( sysdev ) ;
}
}
2007-05-24 01:19:42 +04:00
mutex_unlock ( & sysdev_drivers_lock ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-11 06:37:39 +04:00
static void __sysdev_resume ( struct sys_device * dev )
{
struct sysdev_class * cls = dev - > cls ;
struct sysdev_driver * drv ;
/* First, call the class-specific one */
if ( cls - > resume )
cls - > resume ( dev ) ;
2009-04-18 15:45:13 +04:00
WARN_ONCE ( ! irqs_disabled ( ) ,
" Interrupts enabled after %pF \n " , cls - > resume ) ;
2005-08-11 06:37:39 +04:00
/* Call auxillary drivers next. */
list_for_each_entry ( drv , & cls - > drivers , entry ) {
if ( drv - > resume )
drv - > resume ( dev ) ;
2009-04-18 15:45:13 +04:00
WARN_ONCE ( ! irqs_disabled ( ) ,
" Interrupts enabled after %pF \n " , drv - > resume ) ;
2005-08-11 06:37:39 +04:00
}
}
2005-04-17 02:20:36 +04:00
/**
* sysdev_suspend - Suspend all system devices .
* @ state : Power state to enter .
*
2008-10-13 14:05:04 +04:00
* We perform an almost identical operation as sysdev_shutdown ( )
2005-04-17 02:20:36 +04:00
* above , though calling - > suspend ( ) instead . Interrupts are disabled
* when this called . Devices are responsible for both saving state and
* quiescing or powering down the device .
*
* This is only called by the device PM core , so we let them handle
* all synchronization .
*/
2005-04-17 02:25:24 +04:00
int sysdev_suspend ( pm_message_t state )
2005-04-17 02:20:36 +04:00
{
2009-03-03 13:36:02 +03:00
struct sysdev_class * cls ;
2005-08-11 06:37:39 +04:00
struct sys_device * sysdev , * err_dev ;
struct sysdev_driver * drv , * err_drv ;
int ret ;
2005-04-17 02:20:36 +04:00
2009-03-17 00:34:06 +03:00
pr_debug ( " Checking wake-up interrupts \n " ) ;
/* Return error code if there are any wake-up interrupts pending */
ret = check_wakeup_irqs ( ) ;
if ( ret )
return ret ;
2009-04-18 15:45:13 +04:00
WARN_ONCE ( ! irqs_disabled ( ) ,
" Interrupts enabled while suspending system devices \n " ) ;
2005-04-17 02:20:36 +04:00
pr_debug ( " Suspending System Devices \n " ) ;
2007-11-01 18:29:06 +03:00
list_for_each_entry_reverse ( cls , & system_kset - > list , kset . kobj . entry ) {
2005-04-17 02:20:36 +04:00
pr_debug ( " Suspending type '%s': \n " ,
kobject_name ( & cls - > kset . kobj ) ) ;
list_for_each_entry ( sysdev , & cls - > kset . list , kobj . entry ) {
pr_debug ( " %s \n " , kobject_name ( & sysdev - > kobj ) ) ;
2007-08-19 11:51:14 +04:00
/* Call auxillary drivers first */
2005-04-17 02:20:36 +04:00
list_for_each_entry ( drv , & cls - > drivers , entry ) {
2005-08-11 06:37:39 +04:00
if ( drv - > suspend ) {
ret = drv - > suspend ( sysdev , state ) ;
if ( ret )
goto aux_driver ;
}
2009-04-18 15:45:13 +04:00
WARN_ONCE ( ! irqs_disabled ( ) ,
" Interrupts enabled after %pF \n " ,
drv - > suspend ) ;
2005-04-17 02:20:36 +04:00
}
/* Now call the generic one */
2005-08-11 06:37:39 +04:00
if ( cls - > suspend ) {
ret = cls - > suspend ( sysdev , state ) ;
if ( ret )
goto cls_driver ;
2009-04-18 15:45:13 +04:00
WARN_ONCE ( ! irqs_disabled ( ) ,
" Interrupts enabled after %pF \n " ,
cls - > suspend ) ;
2005-08-11 06:37:39 +04:00
}
2005-04-17 02:20:36 +04:00
}
}
return 0 ;
2005-08-11 06:37:39 +04:00
/* resume current sysdev */
cls_driver :
drv = NULL ;
2010-10-20 22:22:42 +04:00
printk ( KERN_ERR " Class suspend failed for %s: %d \n " ,
kobject_name ( & sysdev - > kobj ) , ret ) ;
2005-08-11 06:37:39 +04:00
aux_driver :
if ( drv )
2010-10-20 22:22:42 +04:00
printk ( KERN_ERR " Class driver suspend failed for %s: %d \n " ,
kobject_name ( & sysdev - > kobj ) , ret ) ;
2005-08-11 06:37:39 +04:00
list_for_each_entry ( err_drv , & cls - > drivers , entry ) {
if ( err_drv = = drv )
break ;
if ( err_drv - > resume )
err_drv - > resume ( sysdev ) ;
}
/* resume other sysdevs in current class */
list_for_each_entry ( err_dev , & cls - > kset . list , kobj . entry ) {
if ( err_dev = = sysdev )
break ;
pr_debug ( " %s \n " , kobject_name ( & err_dev - > kobj ) ) ;
__sysdev_resume ( err_dev ) ;
}
/* resume other classes */
2007-11-01 18:29:06 +03:00
list_for_each_entry_continue ( cls , & system_kset - > list , kset . kobj . entry ) {
2005-08-11 06:37:39 +04:00
list_for_each_entry ( err_dev , & cls - > kset . list , kobj . entry ) {
pr_debug ( " %s \n " , kobject_name ( & err_dev - > kobj ) ) ;
__sysdev_resume ( err_dev ) ;
}
}
return ret ;
2005-04-17 02:20:36 +04:00
}
2009-02-23 00:07:03 +03:00
EXPORT_SYMBOL_GPL ( sysdev_suspend ) ;
2005-04-17 02:20:36 +04:00
/**
* sysdev_resume - Bring system devices back to life .
*
2008-10-13 14:05:04 +04:00
* Similar to sysdev_suspend ( ) , but we iterate the list forwards
2005-04-17 02:20:36 +04:00
* to guarantee that parent devices are resumed before their children .
*
* Note : Interrupts are disabled when called .
*/
int sysdev_resume ( void )
{
2009-03-03 13:36:02 +03:00
struct sysdev_class * cls ;
2005-04-17 02:20:36 +04:00
2009-04-18 15:45:13 +04:00
WARN_ONCE ( ! irqs_disabled ( ) ,
" Interrupts enabled while resuming system devices \n " ) ;
2005-04-17 02:20:36 +04:00
pr_debug ( " Resuming System Devices \n " ) ;
2007-11-01 18:29:06 +03:00
list_for_each_entry ( cls , & system_kset - > list , kset . kobj . entry ) {
2009-03-03 13:36:02 +03:00
struct sys_device * sysdev ;
2005-04-17 02:20:36 +04:00
pr_debug ( " Resuming type '%s': \n " ,
kobject_name ( & cls - > kset . kobj ) ) ;
list_for_each_entry ( sysdev , & cls - > kset . list , kobj . entry ) {
pr_debug ( " %s \n " , kobject_name ( & sysdev - > kobj ) ) ;
2005-08-11 06:37:39 +04:00
__sysdev_resume ( sysdev ) ;
2005-04-17 02:20:36 +04:00
}
}
return 0 ;
}
2009-02-23 00:07:03 +03:00
EXPORT_SYMBOL_GPL ( sysdev_resume ) ;
2005-04-17 02:20:36 +04:00
int __init system_bus_init ( void )
{
2007-11-01 18:29:06 +03:00
system_kset = kset_create_and_add ( " system " , NULL , & devices_kset - > kobj ) ;
if ( ! system_kset )
return - ENOMEM ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL_GPL ( sysdev_register ) ;
EXPORT_SYMBOL_GPL ( sysdev_unregister ) ;
2008-07-01 20:48:42 +04:00
# define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr)
ssize_t sysdev_store_ulong ( struct sys_device * sysdev ,
struct sysdev_attribute * attr ,
const char * buf , size_t size )
{
struct sysdev_ext_attribute * ea = to_ext_attr ( attr ) ;
char * end ;
unsigned long new = simple_strtoul ( buf , & end , 0 ) ;
if ( end = = buf )
return - EINVAL ;
* ( unsigned long * ) ( ea - > var ) = new ;
2008-10-13 14:03:03 +04:00
/* Always return full write size even if we didn't consume all */
return size ;
2008-07-01 20:48:42 +04:00
}
EXPORT_SYMBOL_GPL ( sysdev_store_ulong ) ;
ssize_t sysdev_show_ulong ( struct sys_device * sysdev ,
struct sysdev_attribute * attr ,
char * buf )
{
struct sysdev_ext_attribute * ea = to_ext_attr ( attr ) ;
return snprintf ( buf , PAGE_SIZE , " %lx \n " , * ( unsigned long * ) ( ea - > var ) ) ;
}
EXPORT_SYMBOL_GPL ( sysdev_show_ulong ) ;
ssize_t sysdev_store_int ( struct sys_device * sysdev ,
struct sysdev_attribute * attr ,
const char * buf , size_t size )
{
struct sysdev_ext_attribute * ea = to_ext_attr ( attr ) ;
char * end ;
long new = simple_strtol ( buf , & end , 0 ) ;
if ( end = = buf | | new > INT_MAX | | new < INT_MIN )
return - EINVAL ;
* ( int * ) ( ea - > var ) = new ;
2008-10-13 14:03:03 +04:00
/* Always return full write size even if we didn't consume all */
return size ;
2008-07-01 20:48:42 +04:00
}
EXPORT_SYMBOL_GPL ( sysdev_store_int ) ;
ssize_t sysdev_show_int ( struct sys_device * sysdev ,
struct sysdev_attribute * attr ,
char * buf )
{
struct sysdev_ext_attribute * ea = to_ext_attr ( attr ) ;
return snprintf ( buf , PAGE_SIZE , " %d \n " , * ( int * ) ( ea - > var ) ) ;
}
EXPORT_SYMBOL_GPL ( sysdev_show_int ) ;