2019-04-02 09:30:36 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Generic Counter interface
* Copyright ( C ) 2018 William Breathitt Gray
*/
# include <linux/counter.h>
# include <linux/device.h>
# include <linux/err.h>
# include <linux/export.h>
# include <linux/fs.h>
# include <linux/gfp.h>
# include <linux/idr.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/printk.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/sysfs.h>
# include <linux/types.h>
const char * const counter_count_direction_str [ 2 ] = {
[ COUNTER_COUNT_DIRECTION_FORWARD ] = " forward " ,
[ COUNTER_COUNT_DIRECTION_BACKWARD ] = " backward "
} ;
EXPORT_SYMBOL_GPL ( counter_count_direction_str ) ;
const char * const counter_count_mode_str [ 4 ] = {
[ COUNTER_COUNT_MODE_NORMAL ] = " normal " ,
[ COUNTER_COUNT_MODE_RANGE_LIMIT ] = " range limit " ,
[ COUNTER_COUNT_MODE_NON_RECYCLE ] = " non-recycle " ,
[ COUNTER_COUNT_MODE_MODULO_N ] = " modulo-n "
} ;
EXPORT_SYMBOL_GPL ( counter_count_mode_str ) ;
ssize_t counter_signal_enum_read ( struct counter_device * counter ,
struct counter_signal * signal , void * priv ,
char * buf )
{
const struct counter_signal_enum_ext * const e = priv ;
int err ;
size_t index ;
if ( ! e - > get )
return - EINVAL ;
err = e - > get ( counter , signal , & index ) ;
if ( err )
return err ;
if ( index > = e - > num_items )
return - EINVAL ;
return sprintf ( buf , " %s \n " , e - > items [ index ] ) ;
}
EXPORT_SYMBOL_GPL ( counter_signal_enum_read ) ;
ssize_t counter_signal_enum_write ( struct counter_device * counter ,
struct counter_signal * signal , void * priv ,
const char * buf , size_t len )
{
const struct counter_signal_enum_ext * const e = priv ;
ssize_t index ;
int err ;
if ( ! e - > set )
return - EINVAL ;
index = __sysfs_match_string ( e - > items , e - > num_items , buf ) ;
if ( index < 0 )
return index ;
err = e - > set ( counter , signal , index ) ;
if ( err )
return err ;
return len ;
}
EXPORT_SYMBOL_GPL ( counter_signal_enum_write ) ;
ssize_t counter_signal_enum_available_read ( struct counter_device * counter ,
struct counter_signal * signal ,
void * priv , char * buf )
{
const struct counter_signal_enum_ext * const e = priv ;
size_t i ;
size_t len = 0 ;
if ( ! e - > num_items )
return 0 ;
for ( i = 0 ; i < e - > num_items ; i + + )
len + = sprintf ( buf + len , " %s \n " , e - > items [ i ] ) ;
return len ;
}
EXPORT_SYMBOL_GPL ( counter_signal_enum_available_read ) ;
ssize_t counter_count_enum_read ( struct counter_device * counter ,
struct counter_count * count , void * priv ,
char * buf )
{
const struct counter_count_enum_ext * const e = priv ;
int err ;
size_t index ;
if ( ! e - > get )
return - EINVAL ;
err = e - > get ( counter , count , & index ) ;
if ( err )
return err ;
if ( index > = e - > num_items )
return - EINVAL ;
return sprintf ( buf , " %s \n " , e - > items [ index ] ) ;
}
EXPORT_SYMBOL_GPL ( counter_count_enum_read ) ;
ssize_t counter_count_enum_write ( struct counter_device * counter ,
struct counter_count * count , void * priv ,
const char * buf , size_t len )
{
const struct counter_count_enum_ext * const e = priv ;
ssize_t index ;
int err ;
if ( ! e - > set )
return - EINVAL ;
index = __sysfs_match_string ( e - > items , e - > num_items , buf ) ;
if ( index < 0 )
return index ;
err = e - > set ( counter , count , index ) ;
if ( err )
return err ;
return len ;
}
EXPORT_SYMBOL_GPL ( counter_count_enum_write ) ;
ssize_t counter_count_enum_available_read ( struct counter_device * counter ,
struct counter_count * count ,
void * priv , char * buf )
{
const struct counter_count_enum_ext * const e = priv ;
size_t i ;
size_t len = 0 ;
if ( ! e - > num_items )
return 0 ;
for ( i = 0 ; i < e - > num_items ; i + + )
len + = sprintf ( buf + len , " %s \n " , e - > items [ i ] ) ;
return len ;
}
EXPORT_SYMBOL_GPL ( counter_count_enum_available_read ) ;
ssize_t counter_device_enum_read ( struct counter_device * counter , void * priv ,
char * buf )
{
const struct counter_device_enum_ext * const e = priv ;
int err ;
size_t index ;
if ( ! e - > get )
return - EINVAL ;
err = e - > get ( counter , & index ) ;
if ( err )
return err ;
if ( index > = e - > num_items )
return - EINVAL ;
return sprintf ( buf , " %s \n " , e - > items [ index ] ) ;
}
EXPORT_SYMBOL_GPL ( counter_device_enum_read ) ;
ssize_t counter_device_enum_write ( struct counter_device * counter , void * priv ,
const char * buf , size_t len )
{
const struct counter_device_enum_ext * const e = priv ;
ssize_t index ;
int err ;
if ( ! e - > set )
return - EINVAL ;
index = __sysfs_match_string ( e - > items , e - > num_items , buf ) ;
if ( index < 0 )
return index ;
err = e - > set ( counter , index ) ;
if ( err )
return err ;
return len ;
}
EXPORT_SYMBOL_GPL ( counter_device_enum_write ) ;
ssize_t counter_device_enum_available_read ( struct counter_device * counter ,
void * priv , char * buf )
{
const struct counter_device_enum_ext * const e = priv ;
size_t i ;
size_t len = 0 ;
if ( ! e - > num_items )
return 0 ;
for ( i = 0 ; i < e - > num_items ; i + + )
len + = sprintf ( buf + len , " %s \n " , e - > items [ i ] ) ;
return len ;
}
EXPORT_SYMBOL_GPL ( counter_device_enum_available_read ) ;
struct counter_attr_parm {
struct counter_device_attr_group * group ;
const char * prefix ;
const char * name ;
ssize_t ( * show ) ( struct device * dev , struct device_attribute * attr ,
char * buf ) ;
ssize_t ( * store ) ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t len ) ;
void * component ;
} ;
struct counter_device_attr {
struct device_attribute dev_attr ;
struct list_head l ;
void * component ;
} ;
static int counter_attribute_create ( const struct counter_attr_parm * const parm )
{
struct counter_device_attr * counter_attr ;
struct device_attribute * dev_attr ;
int err ;
struct list_head * const attr_list = & parm - > group - > attr_list ;
/* Allocate a Counter device attribute */
counter_attr = kzalloc ( sizeof ( * counter_attr ) , GFP_KERNEL ) ;
if ( ! counter_attr )
return - ENOMEM ;
dev_attr = & counter_attr - > dev_attr ;
sysfs_attr_init ( & dev_attr - > attr ) ;
/* Configure device attribute */
dev_attr - > attr . name = kasprintf ( GFP_KERNEL , " %s%s " , parm - > prefix ,
parm - > name ) ;
if ( ! dev_attr - > attr . name ) {
err = - ENOMEM ;
goto err_free_counter_attr ;
}
if ( parm - > show ) {
dev_attr - > attr . mode | = 0444 ;
dev_attr - > show = parm - > show ;
}
if ( parm - > store ) {
dev_attr - > attr . mode | = 0200 ;
dev_attr - > store = parm - > store ;
}
/* Store associated Counter component with attribute */
counter_attr - > component = parm - > component ;
/* Keep track of the attribute for later cleanup */
list_add ( & counter_attr - > l , attr_list ) ;
parm - > group - > num_attr + + ;
return 0 ;
err_free_counter_attr :
kfree ( counter_attr ) ;
return err ;
}
# define to_counter_attr(_dev_attr) \
container_of ( _dev_attr , struct counter_device_attr , dev_attr )
struct counter_signal_unit {
struct counter_signal * signal ;
} ;
2019-10-06 23:03:09 +03:00
static const char * const counter_signal_value_str [ ] = {
[ COUNTER_SIGNAL_LOW ] = " low " ,
[ COUNTER_SIGNAL_HIGH ] = " high "
} ;
2019-04-02 09:30:36 +03:00
static ssize_t counter_signal_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct counter_device * const counter = dev_get_drvdata ( dev ) ;
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_signal_unit * const component = devattr - > component ;
struct counter_signal * const signal = component - > signal ;
int err ;
2019-10-06 23:03:09 +03:00
enum counter_signal_value val ;
2019-04-02 09:30:36 +03:00
err = counter - > ops - > signal_read ( counter , signal , & val ) ;
if ( err )
return err ;
2019-10-06 23:03:09 +03:00
return sprintf ( buf , " %s \n " , counter_signal_value_str [ val ] ) ;
2019-04-02 09:30:36 +03:00
}
struct counter_name_unit {
const char * name ;
} ;
static ssize_t counter_device_attr_name_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
const struct counter_name_unit * const comp = to_counter_attr ( attr ) - > component ;
return sprintf ( buf , " %s \n " , comp - > name ) ;
}
static int counter_name_attribute_create (
struct counter_device_attr_group * const group ,
const char * const name )
{
struct counter_name_unit * name_comp ;
struct counter_attr_parm parm ;
int err ;
/* Skip if no name */
if ( ! name )
return 0 ;
/* Allocate name attribute component */
name_comp = kmalloc ( sizeof ( * name_comp ) , GFP_KERNEL ) ;
if ( ! name_comp )
return - ENOMEM ;
name_comp - > name = name ;
/* Allocate Signal name attribute */
parm . group = group ;
parm . prefix = " " ;
parm . name = " name " ;
parm . show = counter_device_attr_name_show ;
parm . store = NULL ;
parm . component = name_comp ;
err = counter_attribute_create ( & parm ) ;
if ( err )
goto err_free_name_comp ;
return 0 ;
err_free_name_comp :
kfree ( name_comp ) ;
return err ;
}
struct counter_signal_ext_unit {
struct counter_signal * signal ;
const struct counter_signal_ext * ext ;
} ;
static ssize_t counter_signal_ext_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_signal_ext_unit * const comp = devattr - > component ;
const struct counter_signal_ext * const ext = comp - > ext ;
return ext - > read ( dev_get_drvdata ( dev ) , comp - > signal , ext - > priv , buf ) ;
}
static ssize_t counter_signal_ext_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len )
{
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_signal_ext_unit * const comp = devattr - > component ;
const struct counter_signal_ext * const ext = comp - > ext ;
return ext - > write ( dev_get_drvdata ( dev ) , comp - > signal , ext - > priv , buf ,
len ) ;
}
static void counter_device_attr_list_free ( struct list_head * attr_list )
{
struct counter_device_attr * p , * n ;
list_for_each_entry_safe ( p , n , attr_list , l ) {
/* free attribute name and associated component memory */
kfree ( p - > dev_attr . attr . name ) ;
kfree ( p - > component ) ;
list_del ( & p - > l ) ;
kfree ( p ) ;
}
}
static int counter_signal_ext_register (
struct counter_device_attr_group * const group ,
struct counter_signal * const signal )
{
const size_t num_ext = signal - > num_ext ;
size_t i ;
const struct counter_signal_ext * ext ;
struct counter_signal_ext_unit * signal_ext_comp ;
struct counter_attr_parm parm ;
int err ;
/* Create an attribute for each extension */
for ( i = 0 ; i < num_ext ; i + + ) {
ext = signal - > ext + i ;
/* Allocate signal_ext attribute component */
signal_ext_comp = kmalloc ( sizeof ( * signal_ext_comp ) , GFP_KERNEL ) ;
if ( ! signal_ext_comp ) {
err = - ENOMEM ;
goto err_free_attr_list ;
}
signal_ext_comp - > signal = signal ;
signal_ext_comp - > ext = ext ;
/* Allocate a Counter device attribute */
parm . group = group ;
parm . prefix = " " ;
parm . name = ext - > name ;
parm . show = ( ext - > read ) ? counter_signal_ext_show : NULL ;
parm . store = ( ext - > write ) ? counter_signal_ext_store : NULL ;
parm . component = signal_ext_comp ;
err = counter_attribute_create ( & parm ) ;
if ( err ) {
kfree ( signal_ext_comp ) ;
goto err_free_attr_list ;
}
}
return 0 ;
err_free_attr_list :
counter_device_attr_list_free ( & group - > attr_list ) ;
return err ;
}
static int counter_signal_attributes_create (
struct counter_device_attr_group * const group ,
const struct counter_device * const counter ,
struct counter_signal * const signal )
{
struct counter_signal_unit * signal_comp ;
struct counter_attr_parm parm ;
int err ;
/* Allocate Signal attribute component */
signal_comp = kmalloc ( sizeof ( * signal_comp ) , GFP_KERNEL ) ;
if ( ! signal_comp )
return - ENOMEM ;
signal_comp - > signal = signal ;
/* Create main Signal attribute */
parm . group = group ;
parm . prefix = " " ;
parm . name = " signal " ;
parm . show = ( counter - > ops - > signal_read ) ? counter_signal_show : NULL ;
parm . store = NULL ;
parm . component = signal_comp ;
err = counter_attribute_create ( & parm ) ;
if ( err ) {
kfree ( signal_comp ) ;
return err ;
}
/* Create Signal name attribute */
err = counter_name_attribute_create ( group , signal - > name ) ;
if ( err )
goto err_free_attr_list ;
/* Register Signal extension attributes */
err = counter_signal_ext_register ( group , signal ) ;
if ( err )
goto err_free_attr_list ;
return 0 ;
err_free_attr_list :
counter_device_attr_list_free ( & group - > attr_list ) ;
return err ;
}
static int counter_signals_register (
struct counter_device_attr_group * const groups_list ,
const struct counter_device * const counter )
{
const size_t num_signals = counter - > num_signals ;
size_t i ;
struct counter_signal * signal ;
const char * name ;
int err ;
/* Register each Signal */
for ( i = 0 ; i < num_signals ; i + + ) {
signal = counter - > signals + i ;
/* Generate Signal attribute directory name */
name = kasprintf ( GFP_KERNEL , " signal%d " , signal - > id ) ;
if ( ! name ) {
err = - ENOMEM ;
goto err_free_attr_groups ;
}
groups_list [ i ] . attr_group . name = name ;
/* Create all attributes associated with Signal */
err = counter_signal_attributes_create ( groups_list + i , counter ,
signal ) ;
if ( err )
goto err_free_attr_groups ;
}
return 0 ;
err_free_attr_groups :
do {
kfree ( groups_list [ i ] . attr_group . name ) ;
counter_device_attr_list_free ( & groups_list [ i ] . attr_list ) ;
} while ( i - - ) ;
return err ;
}
static const char * const counter_synapse_action_str [ ] = {
[ COUNTER_SYNAPSE_ACTION_NONE ] = " none " ,
[ COUNTER_SYNAPSE_ACTION_RISING_EDGE ] = " rising edge " ,
[ COUNTER_SYNAPSE_ACTION_FALLING_EDGE ] = " falling edge " ,
[ COUNTER_SYNAPSE_ACTION_BOTH_EDGES ] = " both edges "
} ;
struct counter_action_unit {
struct counter_synapse * synapse ;
struct counter_count * count ;
} ;
static ssize_t counter_action_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
int err ;
struct counter_device * const counter = dev_get_drvdata ( dev ) ;
const struct counter_action_unit * const component = devattr - > component ;
struct counter_count * const count = component - > count ;
struct counter_synapse * const synapse = component - > synapse ;
size_t action_index ;
enum counter_synapse_action action ;
err = counter - > ops - > action_get ( counter , count , synapse , & action_index ) ;
if ( err )
return err ;
synapse - > action = action_index ;
action = synapse - > actions_list [ action_index ] ;
return sprintf ( buf , " %s \n " , counter_synapse_action_str [ action ] ) ;
}
static ssize_t counter_action_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len )
{
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_action_unit * const component = devattr - > component ;
struct counter_synapse * const synapse = component - > synapse ;
size_t action_index ;
const size_t num_actions = synapse - > num_actions ;
enum counter_synapse_action action ;
int err ;
struct counter_device * const counter = dev_get_drvdata ( dev ) ;
struct counter_count * const count = component - > count ;
/* Find requested action mode */
for ( action_index = 0 ; action_index < num_actions ; action_index + + ) {
action = synapse - > actions_list [ action_index ] ;
if ( sysfs_streq ( buf , counter_synapse_action_str [ action ] ) )
break ;
}
/* If requested action mode not found */
if ( action_index > = num_actions )
return - EINVAL ;
err = counter - > ops - > action_set ( counter , count , synapse , action_index ) ;
if ( err )
return err ;
synapse - > action = action_index ;
return len ;
}
struct counter_action_avail_unit {
const enum counter_synapse_action * actions_list ;
size_t num_actions ;
} ;
static ssize_t counter_synapse_action_available_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_action_avail_unit * const component = devattr - > component ;
size_t i ;
enum counter_synapse_action action ;
ssize_t len = 0 ;
for ( i = 0 ; i < component - > num_actions ; i + + ) {
action = component - > actions_list [ i ] ;
len + = sprintf ( buf + len , " %s \n " ,
counter_synapse_action_str [ action ] ) ;
}
return len ;
}
static int counter_synapses_register (
struct counter_device_attr_group * const group ,
const struct counter_device * const counter ,
struct counter_count * const count , const char * const count_attr_name )
{
size_t i ;
struct counter_synapse * synapse ;
const char * prefix ;
struct counter_action_unit * action_comp ;
struct counter_attr_parm parm ;
int err ;
struct counter_action_avail_unit * avail_comp ;
/* Register each Synapse */
for ( i = 0 ; i < count - > num_synapses ; i + + ) {
synapse = count - > synapses + i ;
/* Generate attribute prefix */
prefix = kasprintf ( GFP_KERNEL , " signal%d_ " ,
synapse - > signal - > id ) ;
if ( ! prefix ) {
err = - ENOMEM ;
goto err_free_attr_list ;
}
/* Allocate action attribute component */
action_comp = kmalloc ( sizeof ( * action_comp ) , GFP_KERNEL ) ;
if ( ! action_comp ) {
err = - ENOMEM ;
goto err_free_prefix ;
}
action_comp - > synapse = synapse ;
action_comp - > count = count ;
/* Create action attribute */
parm . group = group ;
parm . prefix = prefix ;
parm . name = " action " ;
parm . show = ( counter - > ops - > action_get ) ? counter_action_show : NULL ;
parm . store = ( counter - > ops - > action_set ) ? counter_action_store : NULL ;
parm . component = action_comp ;
err = counter_attribute_create ( & parm ) ;
if ( err ) {
kfree ( action_comp ) ;
goto err_free_prefix ;
}
/* Allocate action available attribute component */
avail_comp = kmalloc ( sizeof ( * avail_comp ) , GFP_KERNEL ) ;
if ( ! avail_comp ) {
err = - ENOMEM ;
goto err_free_prefix ;
}
avail_comp - > actions_list = synapse - > actions_list ;
avail_comp - > num_actions = synapse - > num_actions ;
/* Create action_available attribute */
parm . group = group ;
parm . prefix = prefix ;
parm . name = " action_available " ;
parm . show = counter_synapse_action_available_show ;
parm . store = NULL ;
parm . component = avail_comp ;
err = counter_attribute_create ( & parm ) ;
if ( err ) {
kfree ( avail_comp ) ;
goto err_free_prefix ;
}
kfree ( prefix ) ;
}
return 0 ;
err_free_prefix :
kfree ( prefix ) ;
err_free_attr_list :
counter_device_attr_list_free ( & group - > attr_list ) ;
return err ;
}
struct counter_count_unit {
struct counter_count * count ;
} ;
static ssize_t counter_count_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct counter_device * const counter = dev_get_drvdata ( dev ) ;
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_count_unit * const component = devattr - > component ;
struct counter_count * const count = component - > count ;
int err ;
2019-10-06 23:03:09 +03:00
unsigned long val ;
2019-04-02 09:30:36 +03:00
err = counter - > ops - > count_read ( counter , count , & val ) ;
if ( err )
return err ;
2019-10-06 23:03:09 +03:00
return sprintf ( buf , " %lu \n " , val ) ;
2019-04-02 09:30:36 +03:00
}
static ssize_t counter_count_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len )
{
struct counter_device * const counter = dev_get_drvdata ( dev ) ;
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_count_unit * const component = devattr - > component ;
struct counter_count * const count = component - > count ;
int err ;
2019-10-06 23:03:09 +03:00
unsigned long val ;
err = kstrtoul ( buf , 0 , & val ) ;
if ( err )
return err ;
2019-04-02 09:30:36 +03:00
2019-10-06 23:03:09 +03:00
err = counter - > ops - > count_write ( counter , count , val ) ;
2019-04-02 09:30:36 +03:00
if ( err )
return err ;
return len ;
}
static const char * const counter_count_function_str [ ] = {
[ COUNTER_COUNT_FUNCTION_INCREASE ] = " increase " ,
[ COUNTER_COUNT_FUNCTION_DECREASE ] = " decrease " ,
[ COUNTER_COUNT_FUNCTION_PULSE_DIRECTION ] = " pulse-direction " ,
[ COUNTER_COUNT_FUNCTION_QUADRATURE_X1_A ] = " quadrature x1 a " ,
[ COUNTER_COUNT_FUNCTION_QUADRATURE_X1_B ] = " quadrature x1 b " ,
[ COUNTER_COUNT_FUNCTION_QUADRATURE_X2_A ] = " quadrature x2 a " ,
[ COUNTER_COUNT_FUNCTION_QUADRATURE_X2_B ] = " quadrature x2 b " ,
[ COUNTER_COUNT_FUNCTION_QUADRATURE_X4 ] = " quadrature x4 "
} ;
static ssize_t counter_function_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
int err ;
struct counter_device * const counter = dev_get_drvdata ( dev ) ;
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_count_unit * const component = devattr - > component ;
struct counter_count * const count = component - > count ;
size_t func_index ;
enum counter_count_function function ;
err = counter - > ops - > function_get ( counter , count , & func_index ) ;
if ( err )
return err ;
count - > function = func_index ;
function = count - > functions_list [ func_index ] ;
return sprintf ( buf , " %s \n " , counter_count_function_str [ function ] ) ;
}
static ssize_t counter_function_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len )
{
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_count_unit * const component = devattr - > component ;
struct counter_count * const count = component - > count ;
const size_t num_functions = count - > num_functions ;
size_t func_index ;
enum counter_count_function function ;
int err ;
struct counter_device * const counter = dev_get_drvdata ( dev ) ;
/* Find requested Count function mode */
for ( func_index = 0 ; func_index < num_functions ; func_index + + ) {
function = count - > functions_list [ func_index ] ;
if ( sysfs_streq ( buf , counter_count_function_str [ function ] ) )
break ;
}
/* Return error if requested Count function mode not found */
if ( func_index > = num_functions )
return - EINVAL ;
err = counter - > ops - > function_set ( counter , count , func_index ) ;
if ( err )
return err ;
count - > function = func_index ;
return len ;
}
struct counter_count_ext_unit {
struct counter_count * count ;
const struct counter_count_ext * ext ;
} ;
static ssize_t counter_count_ext_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_count_ext_unit * const comp = devattr - > component ;
const struct counter_count_ext * const ext = comp - > ext ;
return ext - > read ( dev_get_drvdata ( dev ) , comp - > count , ext - > priv , buf ) ;
}
static ssize_t counter_count_ext_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len )
{
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_count_ext_unit * const comp = devattr - > component ;
const struct counter_count_ext * const ext = comp - > ext ;
return ext - > write ( dev_get_drvdata ( dev ) , comp - > count , ext - > priv , buf ,
len ) ;
}
static int counter_count_ext_register (
struct counter_device_attr_group * const group ,
struct counter_count * const count )
{
size_t i ;
const struct counter_count_ext * ext ;
struct counter_count_ext_unit * count_ext_comp ;
struct counter_attr_parm parm ;
int err ;
/* Create an attribute for each extension */
for ( i = 0 ; i < count - > num_ext ; i + + ) {
ext = count - > ext + i ;
/* Allocate count_ext attribute component */
count_ext_comp = kmalloc ( sizeof ( * count_ext_comp ) , GFP_KERNEL ) ;
if ( ! count_ext_comp ) {
err = - ENOMEM ;
goto err_free_attr_list ;
}
count_ext_comp - > count = count ;
count_ext_comp - > ext = ext ;
/* Allocate count_ext attribute */
parm . group = group ;
parm . prefix = " " ;
parm . name = ext - > name ;
parm . show = ( ext - > read ) ? counter_count_ext_show : NULL ;
parm . store = ( ext - > write ) ? counter_count_ext_store : NULL ;
parm . component = count_ext_comp ;
err = counter_attribute_create ( & parm ) ;
if ( err ) {
kfree ( count_ext_comp ) ;
goto err_free_attr_list ;
}
}
return 0 ;
err_free_attr_list :
counter_device_attr_list_free ( & group - > attr_list ) ;
return err ;
}
struct counter_func_avail_unit {
const enum counter_count_function * functions_list ;
size_t num_functions ;
} ;
static ssize_t counter_count_function_available_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_func_avail_unit * const component = devattr - > component ;
const enum counter_count_function * const func_list = component - > functions_list ;
const size_t num_functions = component - > num_functions ;
size_t i ;
enum counter_count_function function ;
ssize_t len = 0 ;
for ( i = 0 ; i < num_functions ; i + + ) {
function = func_list [ i ] ;
len + = sprintf ( buf + len , " %s \n " ,
counter_count_function_str [ function ] ) ;
}
return len ;
}
static int counter_count_attributes_create (
struct counter_device_attr_group * const group ,
const struct counter_device * const counter ,
struct counter_count * const count )
{
struct counter_count_unit * count_comp ;
struct counter_attr_parm parm ;
int err ;
struct counter_count_unit * func_comp ;
struct counter_func_avail_unit * avail_comp ;
/* Allocate count attribute component */
count_comp = kmalloc ( sizeof ( * count_comp ) , GFP_KERNEL ) ;
if ( ! count_comp )
return - ENOMEM ;
count_comp - > count = count ;
/* Create main Count attribute */
parm . group = group ;
parm . prefix = " " ;
parm . name = " count " ;
parm . show = ( counter - > ops - > count_read ) ? counter_count_show : NULL ;
parm . store = ( counter - > ops - > count_write ) ? counter_count_store : NULL ;
parm . component = count_comp ;
err = counter_attribute_create ( & parm ) ;
if ( err ) {
kfree ( count_comp ) ;
return err ;
}
/* Allocate function attribute component */
func_comp = kmalloc ( sizeof ( * func_comp ) , GFP_KERNEL ) ;
if ( ! func_comp ) {
err = - ENOMEM ;
goto err_free_attr_list ;
}
func_comp - > count = count ;
/* Create Count function attribute */
parm . group = group ;
parm . prefix = " " ;
parm . name = " function " ;
parm . show = ( counter - > ops - > function_get ) ? counter_function_show : NULL ;
parm . store = ( counter - > ops - > function_set ) ? counter_function_store : NULL ;
parm . component = func_comp ;
err = counter_attribute_create ( & parm ) ;
if ( err ) {
kfree ( func_comp ) ;
goto err_free_attr_list ;
}
/* Allocate function available attribute component */
avail_comp = kmalloc ( sizeof ( * avail_comp ) , GFP_KERNEL ) ;
if ( ! avail_comp ) {
err = - ENOMEM ;
goto err_free_attr_list ;
}
avail_comp - > functions_list = count - > functions_list ;
avail_comp - > num_functions = count - > num_functions ;
/* Create Count function_available attribute */
parm . group = group ;
parm . prefix = " " ;
parm . name = " function_available " ;
parm . show = counter_count_function_available_show ;
parm . store = NULL ;
parm . component = avail_comp ;
err = counter_attribute_create ( & parm ) ;
if ( err ) {
kfree ( avail_comp ) ;
goto err_free_attr_list ;
}
/* Create Count name attribute */
err = counter_name_attribute_create ( group , count - > name ) ;
if ( err )
goto err_free_attr_list ;
/* Register Count extension attributes */
err = counter_count_ext_register ( group , count ) ;
if ( err )
goto err_free_attr_list ;
return 0 ;
err_free_attr_list :
counter_device_attr_list_free ( & group - > attr_list ) ;
return err ;
}
static int counter_counts_register (
struct counter_device_attr_group * const groups_list ,
const struct counter_device * const counter )
{
size_t i ;
struct counter_count * count ;
const char * name ;
int err ;
/* Register each Count */
for ( i = 0 ; i < counter - > num_counts ; i + + ) {
count = counter - > counts + i ;
/* Generate Count attribute directory name */
name = kasprintf ( GFP_KERNEL , " count%d " , count - > id ) ;
if ( ! name ) {
err = - ENOMEM ;
goto err_free_attr_groups ;
}
groups_list [ i ] . attr_group . name = name ;
/* Register the Synapses associated with each Count */
err = counter_synapses_register ( groups_list + i , counter , count ,
name ) ;
if ( err )
goto err_free_attr_groups ;
/* Create all attributes associated with Count */
err = counter_count_attributes_create ( groups_list + i , counter ,
count ) ;
if ( err )
goto err_free_attr_groups ;
}
return 0 ;
err_free_attr_groups :
do {
kfree ( groups_list [ i ] . attr_group . name ) ;
counter_device_attr_list_free ( & groups_list [ i ] . attr_list ) ;
} while ( i - - ) ;
return err ;
}
struct counter_size_unit {
size_t size ;
} ;
static ssize_t counter_device_attr_size_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
const struct counter_size_unit * const comp = to_counter_attr ( attr ) - > component ;
return sprintf ( buf , " %zu \n " , comp - > size ) ;
}
static int counter_size_attribute_create (
struct counter_device_attr_group * const group ,
const size_t size , const char * const name )
{
struct counter_size_unit * size_comp ;
struct counter_attr_parm parm ;
int err ;
/* Allocate size attribute component */
size_comp = kmalloc ( sizeof ( * size_comp ) , GFP_KERNEL ) ;
if ( ! size_comp )
return - ENOMEM ;
size_comp - > size = size ;
parm . group = group ;
parm . prefix = " " ;
parm . name = name ;
parm . show = counter_device_attr_size_show ;
parm . store = NULL ;
parm . component = size_comp ;
err = counter_attribute_create ( & parm ) ;
if ( err )
goto err_free_size_comp ;
return 0 ;
err_free_size_comp :
kfree ( size_comp ) ;
return err ;
}
struct counter_ext_unit {
const struct counter_device_ext * ext ;
} ;
static ssize_t counter_device_ext_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_ext_unit * const component = devattr - > component ;
const struct counter_device_ext * const ext = component - > ext ;
return ext - > read ( dev_get_drvdata ( dev ) , ext - > priv , buf ) ;
}
static ssize_t counter_device_ext_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len )
{
const struct counter_device_attr * const devattr = to_counter_attr ( attr ) ;
const struct counter_ext_unit * const component = devattr - > component ;
const struct counter_device_ext * const ext = component - > ext ;
return ext - > write ( dev_get_drvdata ( dev ) , ext - > priv , buf , len ) ;
}
static int counter_device_ext_register (
struct counter_device_attr_group * const group ,
struct counter_device * const counter )
{
size_t i ;
struct counter_ext_unit * ext_comp ;
struct counter_attr_parm parm ;
int err ;
/* Create an attribute for each extension */
for ( i = 0 ; i < counter - > num_ext ; i + + ) {
/* Allocate extension attribute component */
ext_comp = kmalloc ( sizeof ( * ext_comp ) , GFP_KERNEL ) ;
if ( ! ext_comp ) {
err = - ENOMEM ;
goto err_free_attr_list ;
}
ext_comp - > ext = counter - > ext + i ;
/* Allocate extension attribute */
parm . group = group ;
parm . prefix = " " ;
parm . name = counter - > ext [ i ] . name ;
parm . show = ( counter - > ext [ i ] . read ) ? counter_device_ext_show : NULL ;
parm . store = ( counter - > ext [ i ] . write ) ? counter_device_ext_store : NULL ;
parm . component = ext_comp ;
err = counter_attribute_create ( & parm ) ;
if ( err ) {
kfree ( ext_comp ) ;
goto err_free_attr_list ;
}
}
return 0 ;
err_free_attr_list :
counter_device_attr_list_free ( & group - > attr_list ) ;
return err ;
}
static int counter_global_attr_register (
struct counter_device_attr_group * const group ,
struct counter_device * const counter )
{
int err ;
/* Create name attribute */
err = counter_name_attribute_create ( group , counter - > name ) ;
if ( err )
return err ;
/* Create num_counts attribute */
err = counter_size_attribute_create ( group , counter - > num_counts ,
" num_counts " ) ;
if ( err )
goto err_free_attr_list ;
/* Create num_signals attribute */
err = counter_size_attribute_create ( group , counter - > num_signals ,
" num_signals " ) ;
if ( err )
goto err_free_attr_list ;
/* Register Counter device extension attributes */
err = counter_device_ext_register ( group , counter ) ;
if ( err )
goto err_free_attr_list ;
return 0 ;
err_free_attr_list :
counter_device_attr_list_free ( & group - > attr_list ) ;
return err ;
}
static void counter_device_groups_list_free (
struct counter_device_attr_group * const groups_list ,
const size_t num_groups )
{
struct counter_device_attr_group * group ;
size_t i ;
/* loop through all attribute groups (signals, counts, global, etc.) */
for ( i = 0 ; i < num_groups ; i + + ) {
group = groups_list + i ;
/* free all attribute group and associated attributes memory */
kfree ( group - > attr_group . name ) ;
kfree ( group - > attr_group . attrs ) ;
counter_device_attr_list_free ( & group - > attr_list ) ;
}
kfree ( groups_list ) ;
}
static int counter_device_groups_list_prepare (
struct counter_device * const counter )
{
const size_t total_num_groups =
counter - > num_signals + counter - > num_counts + 1 ;
struct counter_device_attr_group * groups_list ;
size_t i ;
int err ;
size_t num_groups = 0 ;
/* Allocate space for attribute groups (signals, counts, and ext) */
groups_list = kcalloc ( total_num_groups , sizeof ( * groups_list ) ,
GFP_KERNEL ) ;
if ( ! groups_list )
return - ENOMEM ;
/* Initialize attribute lists */
for ( i = 0 ; i < total_num_groups ; i + + )
INIT_LIST_HEAD ( & groups_list [ i ] . attr_list ) ;
/* Register Signals */
err = counter_signals_register ( groups_list , counter ) ;
if ( err )
goto err_free_groups_list ;
num_groups + = counter - > num_signals ;
/* Register Counts and respective Synapses */
err = counter_counts_register ( groups_list + num_groups , counter ) ;
if ( err )
goto err_free_groups_list ;
num_groups + = counter - > num_counts ;
/* Register Counter global attributes */
err = counter_global_attr_register ( groups_list + num_groups , counter ) ;
if ( err )
goto err_free_groups_list ;
num_groups + + ;
/* Store groups_list in device_state */
counter - > device_state - > groups_list = groups_list ;
counter - > device_state - > num_groups = num_groups ;
return 0 ;
err_free_groups_list :
counter_device_groups_list_free ( groups_list , num_groups ) ;
return err ;
}
static int counter_device_groups_prepare (
struct counter_device_state * const device_state )
{
size_t i , j ;
struct counter_device_attr_group * group ;
int err ;
struct counter_device_attr * p ;
/* Allocate attribute groups for association with device */
device_state - > groups = kcalloc ( device_state - > num_groups + 1 ,
sizeof ( * device_state - > groups ) ,
GFP_KERNEL ) ;
if ( ! device_state - > groups )
return - ENOMEM ;
/* Prepare each group of attributes for association */
for ( i = 0 ; i < device_state - > num_groups ; i + + ) {
group = device_state - > groups_list + i ;
/* Allocate space for attribute pointers in attribute group */
group - > attr_group . attrs = kcalloc ( group - > num_attr + 1 ,
sizeof ( * group - > attr_group . attrs ) , GFP_KERNEL ) ;
if ( ! group - > attr_group . attrs ) {
err = - ENOMEM ;
goto err_free_groups ;
}
/* Add attribute pointers to attribute group */
j = 0 ;
list_for_each_entry ( p , & group - > attr_list , l )
group - > attr_group . attrs [ j + + ] = & p - > dev_attr . attr ;
/* Group attributes in attribute group */
device_state - > groups [ i ] = & group - > attr_group ;
}
/* Associate attributes with device */
device_state - > dev . groups = device_state - > groups ;
return 0 ;
err_free_groups :
do {
group = device_state - > groups_list + i ;
kfree ( group - > attr_group . attrs ) ;
group - > attr_group . attrs = NULL ;
} while ( i - - ) ;
kfree ( device_state - > groups ) ;
return err ;
}
/* Provides a unique ID for each counter device */
static DEFINE_IDA ( counter_ida ) ;
static void counter_device_release ( struct device * dev )
{
struct counter_device * const counter = dev_get_drvdata ( dev ) ;
struct counter_device_state * const device_state = counter - > device_state ;
kfree ( device_state - > groups ) ;
counter_device_groups_list_free ( device_state - > groups_list ,
device_state - > num_groups ) ;
ida_simple_remove ( & counter_ida , device_state - > id ) ;
kfree ( device_state ) ;
}
static struct device_type counter_device_type = {
. name = " counter_device " ,
. release = counter_device_release
} ;
static struct bus_type counter_bus_type = {
. name = " counter "
} ;
/**
* counter_register - register Counter to the system
* @ counter : pointer to Counter to register
*
* This function registers a Counter to the system . A sysfs " counter " directory
* will be created and populated with sysfs attributes correlating with the
* Counter Signals , Synapses , and Counts respectively .
*/
int counter_register ( struct counter_device * const counter )
{
struct counter_device_state * device_state ;
int err ;
/* Allocate internal state container for Counter device */
device_state = kzalloc ( sizeof ( * device_state ) , GFP_KERNEL ) ;
if ( ! device_state )
return - ENOMEM ;
counter - > device_state = device_state ;
/* Acquire unique ID */
device_state - > id = ida_simple_get ( & counter_ida , 0 , 0 , GFP_KERNEL ) ;
if ( device_state - > id < 0 ) {
err = device_state - > id ;
goto err_free_device_state ;
}
/* Configure device structure for Counter */
device_state - > dev . type = & counter_device_type ;
device_state - > dev . bus = & counter_bus_type ;
if ( counter - > parent ) {
device_state - > dev . parent = counter - > parent ;
device_state - > dev . of_node = counter - > parent - > of_node ;
}
dev_set_name ( & device_state - > dev , " counter%d " , device_state - > id ) ;
device_initialize ( & device_state - > dev ) ;
dev_set_drvdata ( & device_state - > dev , counter ) ;
/* Prepare device attributes */
err = counter_device_groups_list_prepare ( counter ) ;
if ( err )
goto err_free_id ;
/* Organize device attributes to groups and match to device */
err = counter_device_groups_prepare ( device_state ) ;
if ( err )
goto err_free_groups_list ;
/* Add device to system */
err = device_add ( & device_state - > dev ) ;
if ( err )
goto err_free_groups ;
return 0 ;
err_free_groups :
kfree ( device_state - > groups ) ;
err_free_groups_list :
counter_device_groups_list_free ( device_state - > groups_list ,
device_state - > num_groups ) ;
err_free_id :
ida_simple_remove ( & counter_ida , device_state - > id ) ;
err_free_device_state :
kfree ( device_state ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( counter_register ) ;
/**
* counter_unregister - unregister Counter from the system
* @ counter : pointer to Counter to unregister
*
* The Counter is unregistered from the system ; all allocated memory is freed .
*/
void counter_unregister ( struct counter_device * const counter )
{
if ( counter )
device_del ( & counter - > device_state - > dev ) ;
}
EXPORT_SYMBOL_GPL ( counter_unregister ) ;
static void devm_counter_unreg ( struct device * dev , void * res )
{
counter_unregister ( * ( struct counter_device * * ) res ) ;
}
/**
* devm_counter_register - Resource - managed counter_register
* @ dev : device to allocate counter_device for
* @ counter : pointer to Counter to register
*
* Managed counter_register . The Counter registered with this function is
* automatically unregistered on driver detach . This function calls
* counter_register internally . Refer to that function for more information .
*
* If an Counter registered with this function needs to be unregistered
* separately , devm_counter_unregister must be used .
*
* RETURNS :
* 0 on success , negative error number on failure .
*/
int devm_counter_register ( struct device * dev ,
struct counter_device * const counter )
{
struct counter_device * * ptr ;
int ret ;
ptr = devres_alloc ( devm_counter_unreg , sizeof ( * ptr ) , GFP_KERNEL ) ;
if ( ! ptr )
return - ENOMEM ;
ret = counter_register ( counter ) ;
if ( ! ret ) {
* ptr = counter ;
devres_add ( dev , ptr ) ;
} else {
devres_free ( ptr ) ;
}
return ret ;
}
EXPORT_SYMBOL_GPL ( devm_counter_register ) ;
static int devm_counter_match ( struct device * dev , void * res , void * data )
{
struct counter_device * * r = res ;
if ( ! r | | ! * r ) {
WARN_ON ( ! r | | ! * r ) ;
return 0 ;
}
return * r = = data ;
}
/**
* devm_counter_unregister - Resource - managed counter_unregister
* @ dev : device this counter_device belongs to
* @ counter : pointer to Counter associated with the device
*
* Unregister Counter registered with devm_counter_register .
*/
void devm_counter_unregister ( struct device * dev ,
struct counter_device * const counter )
{
int rc ;
rc = devres_release ( dev , devm_counter_unreg , devm_counter_match ,
counter ) ;
WARN_ON ( rc ) ;
}
EXPORT_SYMBOL_GPL ( devm_counter_unregister ) ;
static int __init counter_init ( void )
{
return bus_register ( & counter_bus_type ) ;
}
static void __exit counter_exit ( void )
{
bus_unregister ( & counter_bus_type ) ;
}
subsys_initcall ( counter_init ) ;
module_exit ( counter_exit ) ;
MODULE_AUTHOR ( " William Breathitt Gray <vilhelm.gray@gmail.com> " ) ;
MODULE_DESCRIPTION ( " Generic Counter interface " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;