2012-02-15 23:48:01 +04:00
/* The industrial I/O core in kernel channel mapping
*
* Copyright ( c ) 2011 Jonathan Cameron
*
* 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 .
*/
# include <linux/err.h>
# include <linux/export.h>
# include <linux/slab.h>
# include <linux/mutex.h>
2012-04-25 18:54:58 +04:00
# include <linux/iio/iio.h>
2012-02-15 23:48:01 +04:00
# include "iio_core.h"
2012-04-25 18:54:58 +04:00
# include <linux/iio/machine.h>
# include <linux/iio/driver.h>
# include <linux/iio/consumer.h>
2012-02-15 23:48:01 +04:00
struct iio_map_internal {
struct iio_dev * indio_dev ;
struct iio_map * map ;
struct list_head l ;
} ;
static LIST_HEAD ( iio_map_list ) ;
static DEFINE_MUTEX ( iio_map_list_lock ) ;
int iio_map_array_register ( struct iio_dev * indio_dev , struct iio_map * maps )
{
int i = 0 , ret = 0 ;
struct iio_map_internal * mapi ;
if ( maps = = NULL )
return 0 ;
mutex_lock ( & iio_map_list_lock ) ;
while ( maps [ i ] . consumer_dev_name ! = NULL ) {
mapi = kzalloc ( sizeof ( * mapi ) , GFP_KERNEL ) ;
if ( mapi = = NULL ) {
ret = - ENOMEM ;
goto error_ret ;
}
mapi - > map = & maps [ i ] ;
mapi - > indio_dev = indio_dev ;
list_add ( & mapi - > l , & iio_map_list ) ;
i + + ;
}
error_ret :
mutex_unlock ( & iio_map_list_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( iio_map_array_register ) ;
/* Assumes the exact same array (e.g. memory locations)
* used at unregistration as used at registration rather than
* more complex checking of contents .
*/
int iio_map_array_unregister ( struct iio_dev * indio_dev ,
struct iio_map * maps )
{
int i = 0 , ret = 0 ;
bool found_it ;
struct iio_map_internal * mapi ;
if ( maps = = NULL )
return 0 ;
mutex_lock ( & iio_map_list_lock ) ;
while ( maps [ i ] . consumer_dev_name ! = NULL ) {
found_it = false ;
list_for_each_entry ( mapi , & iio_map_list , l )
if ( & maps [ i ] = = mapi - > map ) {
list_del ( & mapi - > l ) ;
kfree ( mapi ) ;
found_it = true ;
break ;
}
2012-10-18 18:43:00 +04:00
if ( ! found_it ) {
2012-02-15 23:48:01 +04:00
ret = - ENODEV ;
goto error_ret ;
}
2012-03-24 10:19:25 +04:00
i + + ;
2012-02-15 23:48:01 +04:00
}
error_ret :
mutex_unlock ( & iio_map_list_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( iio_map_array_unregister ) ;
static const struct iio_chan_spec
2012-05-02 00:04:24 +04:00
* iio_chan_spec_from_name ( const struct iio_dev * indio_dev , const char * name )
2012-02-15 23:48:01 +04:00
{
int i ;
const struct iio_chan_spec * chan = NULL ;
for ( i = 0 ; i < indio_dev - > num_channels ; i + + )
if ( indio_dev - > channels [ i ] . datasheet_name & &
strcmp ( name , indio_dev - > channels [ i ] . datasheet_name ) = = 0 ) {
chan = & indio_dev - > channels [ i ] ;
break ;
}
return chan ;
}
2012-05-02 00:04:24 +04:00
struct iio_channel * iio_channel_get ( const char * name , const char * channel_name )
2012-02-15 23:48:01 +04:00
{
struct iio_map_internal * c_i = NULL , * c = NULL ;
struct iio_channel * channel ;
2012-09-18 08:56:00 +04:00
int err ;
2012-02-15 23:48:01 +04:00
if ( name = = NULL & & channel_name = = NULL )
return ERR_PTR ( - ENODEV ) ;
/* first find matching entry the channel map */
mutex_lock ( & iio_map_list_lock ) ;
list_for_each_entry ( c_i , & iio_map_list , l ) {
if ( ( name & & strcmp ( name , c_i - > map - > consumer_dev_name ) ! = 0 ) | |
( channel_name & &
strcmp ( channel_name , c_i - > map - > consumer_channel ) ! = 0 ) )
continue ;
c = c_i ;
2012-06-04 12:50:03 +04:00
iio_device_get ( c - > indio_dev ) ;
2012-02-15 23:48:01 +04:00
break ;
}
mutex_unlock ( & iio_map_list_lock ) ;
if ( c = = NULL )
return ERR_PTR ( - ENODEV ) ;
2012-09-14 05:24:00 +04:00
channel = kzalloc ( sizeof ( * channel ) , GFP_KERNEL ) ;
2012-09-18 08:56:00 +04:00
if ( channel = = NULL ) {
err = - ENOMEM ;
2012-09-18 08:55:00 +04:00
goto error_no_mem ;
2012-09-18 08:56:00 +04:00
}
2012-02-15 23:48:01 +04:00
channel - > indio_dev = c - > indio_dev ;
2012-09-17 12:44:00 +04:00
if ( c - > map - > adc_channel_label ) {
2012-02-15 23:48:01 +04:00
channel - > channel =
iio_chan_spec_from_name ( channel - > indio_dev ,
c - > map - > adc_channel_label ) ;
2012-09-18 08:56:00 +04:00
if ( channel - > channel = = NULL ) {
err = - EINVAL ;
2012-09-17 12:44:00 +04:00
goto error_no_chan ;
2012-09-18 08:56:00 +04:00
}
2012-09-17 12:44:00 +04:00
}
2012-02-15 23:48:01 +04:00
return channel ;
2012-09-17 12:44:00 +04:00
error_no_chan :
kfree ( channel ) ;
2012-09-18 08:55:00 +04:00
error_no_mem :
iio_device_put ( c - > indio_dev ) ;
2012-09-18 08:56:00 +04:00
return ERR_PTR ( err ) ;
2012-02-15 23:48:01 +04:00
}
2012-05-02 00:04:24 +04:00
EXPORT_SYMBOL_GPL ( iio_channel_get ) ;
2012-02-15 23:48:01 +04:00
2012-05-02 00:04:24 +04:00
void iio_channel_release ( struct iio_channel * channel )
2012-02-15 23:48:01 +04:00
{
2012-06-04 12:50:03 +04:00
iio_device_put ( channel - > indio_dev ) ;
2012-02-15 23:48:01 +04:00
kfree ( channel ) ;
}
2012-05-02 00:04:24 +04:00
EXPORT_SYMBOL_GPL ( iio_channel_release ) ;
2012-02-15 23:48:01 +04:00
2012-05-02 00:04:24 +04:00
struct iio_channel * iio_channel_get_all ( const char * name )
2012-02-15 23:48:01 +04:00
{
struct iio_channel * chans ;
struct iio_map_internal * c = NULL ;
int nummaps = 0 ;
int mapind = 0 ;
int i , ret ;
if ( name = = NULL )
return ERR_PTR ( - EINVAL ) ;
mutex_lock ( & iio_map_list_lock ) ;
/* first count the matching maps */
list_for_each_entry ( c , & iio_map_list , l )
if ( name & & strcmp ( name , c - > map - > consumer_dev_name ) ! = 0 )
continue ;
else
nummaps + + ;
if ( nummaps = = 0 ) {
ret = - ENODEV ;
goto error_ret ;
}
/* NULL terminated array to save passing size */
chans = kzalloc ( sizeof ( * chans ) * ( nummaps + 1 ) , GFP_KERNEL ) ;
if ( chans = = NULL ) {
ret = - ENOMEM ;
goto error_ret ;
}
/* for each map fill in the chans element */
list_for_each_entry ( c , & iio_map_list , l ) {
if ( name & & strcmp ( name , c - > map - > consumer_dev_name ) ! = 0 )
continue ;
chans [ mapind ] . indio_dev = c - > indio_dev ;
2012-06-30 23:06:00 +04:00
chans [ mapind ] . data = c - > map - > consumer_data ;
2012-02-15 23:48:01 +04:00
chans [ mapind ] . channel =
iio_chan_spec_from_name ( chans [ mapind ] . indio_dev ,
c - > map - > adc_channel_label ) ;
if ( chans [ mapind ] . channel = = NULL ) {
ret = - EINVAL ;
goto error_free_chans ;
}
2012-06-04 12:50:03 +04:00
iio_device_get ( chans [ mapind ] . indio_dev ) ;
2012-02-15 23:48:01 +04:00
mapind + + ;
}
if ( mapind = = 0 ) {
ret = - ENODEV ;
goto error_free_chans ;
}
2012-07-11 10:34:00 +04:00
mutex_unlock ( & iio_map_list_lock ) ;
2012-02-15 23:48:01 +04:00
return chans ;
error_free_chans :
for ( i = 0 ; i < nummaps ; i + + )
2012-06-04 12:50:03 +04:00
iio_device_put ( chans [ i ] . indio_dev ) ;
2012-02-15 23:48:01 +04:00
kfree ( chans ) ;
error_ret :
mutex_unlock ( & iio_map_list_lock ) ;
return ERR_PTR ( ret ) ;
}
2012-05-02 00:04:24 +04:00
EXPORT_SYMBOL_GPL ( iio_channel_get_all ) ;
2012-02-15 23:48:01 +04:00
2012-05-02 00:04:24 +04:00
void iio_channel_release_all ( struct iio_channel * channels )
2012-02-15 23:48:01 +04:00
{
struct iio_channel * chan = & channels [ 0 ] ;
while ( chan - > indio_dev ) {
2012-06-04 12:50:03 +04:00
iio_device_put ( chan - > indio_dev ) ;
2012-02-15 23:48:01 +04:00
chan + + ;
}
kfree ( channels ) ;
}
2012-05-02 00:04:24 +04:00
EXPORT_SYMBOL_GPL ( iio_channel_release_all ) ;
2012-02-15 23:48:01 +04:00
2012-09-17 16:17:00 +04:00
static int iio_channel_read ( struct iio_channel * chan , int * val , int * val2 ,
enum iio_chan_info_enum info )
{
int unused ;
if ( val2 = = NULL )
val2 = & unused ;
return chan - > indio_dev - > info - > read_raw ( chan - > indio_dev , chan - > channel ,
val , val2 , info ) ;
}
2012-05-02 00:04:24 +04:00
int iio_read_channel_raw ( struct iio_channel * chan , int * val )
2012-02-15 23:48:01 +04:00
{
2012-09-17 16:17:00 +04:00
int ret ;
2012-02-15 23:48:01 +04:00
mutex_lock ( & chan - > indio_dev - > info_exist_lock ) ;
if ( chan - > indio_dev - > info = = NULL ) {
ret = - ENODEV ;
goto err_unlock ;
}
2012-09-17 16:17:00 +04:00
ret = iio_channel_read ( chan , val , NULL , IIO_CHAN_INFO_RAW ) ;
2012-02-15 23:48:01 +04:00
err_unlock :
mutex_unlock ( & chan - > indio_dev - > info_exist_lock ) ;
return ret ;
}
2012-05-02 00:04:24 +04:00
EXPORT_SYMBOL_GPL ( iio_read_channel_raw ) ;
2012-02-15 23:48:01 +04:00
2012-09-17 16:17:00 +04:00
static int iio_convert_raw_to_processed_unlocked ( struct iio_channel * chan ,
int raw , int * processed , unsigned int scale )
{
int scale_type , scale_val , scale_val2 , offset ;
s64 raw64 = raw ;
int ret ;
ret = iio_channel_read ( chan , & offset , NULL , IIO_CHAN_INFO_SCALE ) ;
if ( ret = = 0 )
raw64 + = offset ;
scale_type = iio_channel_read ( chan , & scale_val , & scale_val2 ,
IIO_CHAN_INFO_SCALE ) ;
if ( scale_type < 0 )
return scale_type ;
switch ( scale_type ) {
case IIO_VAL_INT :
* processed = raw64 * scale_val ;
break ;
case IIO_VAL_INT_PLUS_MICRO :
if ( scale_val2 < 0 )
* processed = - raw64 * scale_val ;
else
* processed = raw64 * scale_val ;
* processed + = div_s64 ( raw64 * ( s64 ) scale_val2 * scale ,
1000000LL ) ;
break ;
case IIO_VAL_INT_PLUS_NANO :
if ( scale_val2 < 0 )
* processed = - raw64 * scale_val ;
else
* processed = raw64 * scale_val ;
* processed + = div_s64 ( raw64 * ( s64 ) scale_val2 * scale ,
1000000000LL ) ;
break ;
case IIO_VAL_FRACTIONAL :
* processed = div_s64 ( raw64 * ( s64 ) scale_val * scale ,
scale_val2 ) ;
break ;
2012-10-16 20:29:00 +04:00
case IIO_VAL_FRACTIONAL_LOG2 :
* processed = ( raw64 * ( s64 ) scale_val * scale ) > > scale_val2 ;
break ;
2012-09-17 16:17:00 +04:00
default :
return - EINVAL ;
}
return 0 ;
}
int iio_convert_raw_to_processed ( struct iio_channel * chan , int raw ,
int * processed , unsigned int scale )
{
int ret ;
mutex_lock ( & chan - > indio_dev - > info_exist_lock ) ;
if ( chan - > indio_dev - > info = = NULL ) {
ret = - ENODEV ;
goto err_unlock ;
}
ret = iio_convert_raw_to_processed_unlocked ( chan , raw , processed ,
scale ) ;
err_unlock :
mutex_unlock ( & chan - > indio_dev - > info_exist_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( iio_convert_raw_to_processed ) ;
int iio_read_channel_processed ( struct iio_channel * chan , int * val )
{
int ret ;
mutex_lock ( & chan - > indio_dev - > info_exist_lock ) ;
if ( chan - > indio_dev - > info = = NULL ) {
ret = - ENODEV ;
goto err_unlock ;
}
if ( iio_channel_has_info ( chan - > channel , IIO_CHAN_INFO_PROCESSED ) ) {
ret = iio_channel_read ( chan , val , NULL ,
IIO_CHAN_INFO_PROCESSED ) ;
} else {
ret = iio_channel_read ( chan , val , NULL , IIO_CHAN_INFO_RAW ) ;
if ( ret < 0 )
goto err_unlock ;
ret = iio_convert_raw_to_processed_unlocked ( chan , * val , val , 1 ) ;
}
err_unlock :
mutex_unlock ( & chan - > indio_dev - > info_exist_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( iio_read_channel_processed ) ;
2012-05-02 00:04:24 +04:00
int iio_read_channel_scale ( struct iio_channel * chan , int * val , int * val2 )
2012-02-15 23:48:01 +04:00
{
int ret ;
mutex_lock ( & chan - > indio_dev - > info_exist_lock ) ;
if ( chan - > indio_dev - > info = = NULL ) {
ret = - ENODEV ;
goto err_unlock ;
}
2012-09-17 16:17:00 +04:00
ret = iio_channel_read ( chan , val , val2 , IIO_CHAN_INFO_SCALE ) ;
2012-02-15 23:48:01 +04:00
err_unlock :
mutex_unlock ( & chan - > indio_dev - > info_exist_lock ) ;
return ret ;
}
2012-05-02 00:04:24 +04:00
EXPORT_SYMBOL_GPL ( iio_read_channel_scale ) ;
2012-02-15 23:48:01 +04:00
2012-05-02 00:04:24 +04:00
int iio_get_channel_type ( struct iio_channel * chan , enum iio_chan_type * type )
2012-02-15 23:48:01 +04:00
{
int ret = 0 ;
/* Need to verify underlying driver has not gone away */
mutex_lock ( & chan - > indio_dev - > info_exist_lock ) ;
if ( chan - > indio_dev - > info = = NULL ) {
ret = - ENODEV ;
goto err_unlock ;
}
* type = chan - > channel - > type ;
err_unlock :
mutex_unlock ( & chan - > indio_dev - > info_exist_lock ) ;
return ret ;
}
2012-05-02 00:04:24 +04:00
EXPORT_SYMBOL_GPL ( iio_get_channel_type ) ;