2016-10-19 15:05:47 +03:00
/*
* Remote Processor Framework
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/remoteproc.h>
# include "remoteproc_internal.h"
# define to_rproc(d) container_of(d, struct rproc, dev)
/* Expose the loaded / running firmware name via sysfs */
static ssize_t firmware_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct rproc * rproc = to_rproc ( dev ) ;
return sprintf ( buf , " %s \n " , rproc - > firmware ) ;
}
/* Change firmware name via sysfs */
static ssize_t firmware_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct rproc * rproc = to_rproc ( dev ) ;
char * p ;
int err , len = count ;
err = mutex_lock_interruptible ( & rproc - > lock ) ;
if ( err ) {
dev_err ( dev , " can't lock rproc %s: %d \n " , rproc - > name , err ) ;
return - EINVAL ;
}
if ( rproc - > state ! = RPROC_OFFLINE ) {
dev_err ( dev , " can't change firmware while running \n " ) ;
err = - EBUSY ;
goto out ;
}
len = strcspn ( buf , " \n " ) ;
2018-09-15 03:37:22 +03:00
if ( ! len ) {
dev_err ( dev , " can't provide a NULL firmware \n " ) ;
err = - EINVAL ;
goto out ;
}
2016-10-19 15:05:47 +03:00
p = kstrndup ( buf , len , GFP_KERNEL ) ;
if ( ! p ) {
err = - ENOMEM ;
goto out ;
}
kfree ( rproc - > firmware ) ;
rproc - > firmware = p ;
out :
mutex_unlock ( & rproc - > lock ) ;
return err ? err : count ;
}
static DEVICE_ATTR_RW ( firmware ) ;
/*
* A state - to - string lookup table , for exposing a human readable state
* via sysfs . Always keep in sync with enum rproc_state
*/
static const char * const rproc_state_string [ ] = {
[ RPROC_OFFLINE ] = " offline " ,
[ RPROC_SUSPENDED ] = " suspended " ,
[ RPROC_RUNNING ] = " running " ,
[ RPROC_CRASHED ] = " crashed " ,
2017-01-24 04:53:18 +03:00
[ RPROC_DELETED ] = " deleted " ,
2016-10-19 15:05:47 +03:00
[ RPROC_LAST ] = " invalid " ,
} ;
/* Expose the state of the remote processor via sysfs */
static ssize_t state_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct rproc * rproc = to_rproc ( dev ) ;
unsigned int state ;
state = rproc - > state > RPROC_LAST ? RPROC_LAST : rproc - > state ;
return sprintf ( buf , " %s \n " , rproc_state_string [ state ] ) ;
}
/* Change remote processor state via sysfs */
static ssize_t state_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct rproc * rproc = to_rproc ( dev ) ;
int ret = 0 ;
if ( sysfs_streq ( buf , " start " ) ) {
if ( rproc - > state = = RPROC_RUNNING )
return - EBUSY ;
ret = rproc_boot ( rproc ) ;
if ( ret )
dev_err ( & rproc - > dev , " Boot failed: %d \n " , ret ) ;
} else if ( sysfs_streq ( buf , " stop " ) ) {
if ( rproc - > state ! = RPROC_RUNNING )
return - EINVAL ;
rproc_shutdown ( rproc ) ;
} else {
dev_err ( & rproc - > dev , " Unrecognised option: %s \n " , buf ) ;
ret = - EINVAL ;
}
return ret ? ret : count ;
}
static DEVICE_ATTR_RW ( state ) ;
static struct attribute * rproc_attrs [ ] = {
& dev_attr_firmware . attr ,
& dev_attr_state . attr ,
NULL
} ;
static const struct attribute_group rproc_devgroup = {
. attrs = rproc_attrs
} ;
static const struct attribute_group * rproc_devgroups [ ] = {
& rproc_devgroup ,
NULL
} ;
struct class rproc_class = {
. name = " remoteproc " ,
. dev_groups = rproc_devgroups ,
} ;
int __init rproc_init_sysfs ( void )
{
/* create remoteproc device class for sysfs */
int err = class_register ( & rproc_class ) ;
if ( err )
pr_err ( " remoteproc: unable to register class \n " ) ;
return err ;
}
void __exit rproc_exit_sysfs ( void )
{
class_unregister ( & rproc_class ) ;
}