// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright ( c ) 2011 Jonathan Cameron
*
* A reference industrial I / O driver to illustrate the functionality available .
*
* There are numerous real drivers to illustrate the finer points .
* The purpose of this driver is to provide a driver with far more comments
* and explanatory notes than any ' real ' driver would have .
* Anyone starting out writing an IIO driver should first make sure they
* understand all of this driver except those bits specifically marked
* as being present to allow us to ' fake ' the presence of hardware .
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
#include <linux/iio/buffer.h>
#include <linux/iio/sw_device.h>
#include "iio_simple_dummy.h"
static const struct config_item_type iio_dummy_type = {
.ct_owner = THIS_MODULE,
};
/**
* struct iio_dummy_accel_calibscale - realworld to register mapping
* @ val : first value in read_raw - here integer part .
* @ val2 : second value in read_raw etc - here micro part .
* @ regval : register value - magic device specific numbers .
*/
struct iio_dummy_accel_calibscale {
int val;
int val2;
int regval; /* what would be written to hardware */
};
static const struct iio_dummy_accel_calibscale dummy_scales[] = {
{ 0 , 100 , 0 x8 }, /* 0.000100 */
{ 0 , 133 , 0 x7 }, /* 0.000133 */
{ 733 , 13 , 0 x9 }, /* 733.000013 */
};
#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
/*
* simple event - triggered when value rises above
* a threshold
*/
static const struct iio_event_spec iio_dummy_event = {
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
};
/*
* simple step detect event - triggered when a step is detected
*/
static const struct iio_event_spec step_detect_event = {
.type = IIO_EV_TYPE_CHANGE,
.dir = IIO_EV_DIR_NONE,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
};
/*
* simple transition event - triggered when the reported running confidence
* value rises above a threshold value
*/
static const struct iio_event_spec iio_running_event = {
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
};
/*
* simple transition event - triggered when the reported walking confidence
* value falls under a threshold value
*/
static const struct iio_event_spec iio_walking_event = {
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
};
#endif
/*
* iio_dummy_channels - Description of available channels
*
* This array of structures tells the IIO core about what the device
* actually provides for a given channel .
*/
static const struct iio_chan_spec iio_dummy_channels[] = {
/* indexed ADC channel in_voltage0_raw etc */
{
.type = IIO_VOLTAGE,
/* Channel has a numeric index of 0 */
.indexed = 1 ,
.channel = 0 ,
/* What other information is available? */
.info_mask_separate =
/*
* in_voltage0_raw
* Raw ( unscaled no bias removal etc ) measurement
* from the device .
*/
BIT(IIO_CHAN_INFO_RAW) |
/*
* in_voltage0_offset
* Offset for userspace to apply prior to scale
* when converting to standard units ( microvolts )
*/
BIT(IIO_CHAN_INFO_OFFSET) |
/*
* in_voltage0_scale
* Multipler for userspace to apply post offset
* when converting to standard units ( microvolts )
*/
BIT(IIO_CHAN_INFO_SCALE),
/*
* sampling_frequency
* The frequency in Hz at which the channels are sampled
*/
.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),
/* The ordering of elements in the buffer via an enum */
.scan_index = DUMMY_INDEX_VOLTAGE_0,
.scan_type = { /* Description of storage in buffer */
.sign = 'u' , /* unsigned */
.realbits = 13 , /* 13 bits */
.storagebits = 16 , /* 16 bits used for storage */
.shift = 0 , /* zero shift */
},
#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
.event_spec = &iio_dummy_event,
.num_event_specs = 1 ,
#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
},
/* Differential ADC channel in_voltage1-voltage2_raw etc*/
{
.type = IIO_VOLTAGE,
.differential = 1 ,
/*
* Indexing for differential channels uses channel
* for the positive part , channel2 for the negative .
*/
.indexed = 1 ,
.channel = 1 ,
.channel2 = 2 ,
/*
* in_voltage1 - voltage2_raw
* Raw ( unscaled no bias removal etc ) measurement
* from the device .
*/
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
/*
* in_voltage - voltage_scale
* Shared version of scale - shared by differential
* input channels of type IIO_VOLTAGE .
*/
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
/*
* sampling_frequency
* The frequency in Hz at which the channels are sampled
*/
.scan_index = DUMMY_INDEX_DIFFVOLTAGE_1M2,
.scan_type = { /* Description of storage in buffer */
.sign = 's' , /* signed */
.realbits = 12 , /* 12 bits */
.storagebits = 16 , /* 16 bits used for storage */
.shift = 0 , /* zero shift */
},
},
/* Differential ADC channel in_voltage3-voltage4_raw etc*/
{
.type = IIO_VOLTAGE,
.differential = 1 ,
.indexed = 1 ,
.channel = 3 ,
.channel2 = 4 ,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.scan_index = DUMMY_INDEX_DIFFVOLTAGE_3M4,
.scan_type = {
.sign = 's' ,
.realbits = 11 ,
.storagebits = 16 ,
.shift = 0 ,
},
},
/*
* ' modified ' ( i . e . axis specified ) acceleration channel
* in_accel_z_raw
*/
{
.type = IIO_ACCEL,
.modified = 1 ,
/* Channel 2 is use for modifiers */
.channel2 = IIO_MOD_X,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
/*
* Internal bias and gain correction values . Applied
* by the hardware or driver prior to userspace
* seeing the readings . Typically part of hardware
* calibration .
*/
BIT(IIO_CHAN_INFO_CALIBSCALE) |
BIT(IIO_CHAN_INFO_CALIBBIAS),
.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.scan_index = DUMMY_INDEX_ACCELX,
.scan_type = { /* Description of storage in buffer */
.sign = 's' , /* signed */
.realbits = 16 , /* 16 bits */
.storagebits = 16 , /* 16 bits used for storage */
.shift = 0 , /* zero shift */
},
},
/*
* Convenience macro for timestamps . 4 is the index in
* the buffer .
*/
IIO_CHAN_SOFT_TIMESTAMP(4 ),
/* DAC channel out_voltage0_raw */
{
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.scan_index = -1 , /* No buffer support */
.output = 1 ,
.indexed = 1 ,
.channel = 0 ,
},
{
.type = IIO_STEPS,
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_ENABLE) |
BIT(IIO_CHAN_INFO_CALIBHEIGHT),
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.scan_index = -1 , /* No buffer support */
#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
.event_spec = &step_detect_event,
.num_event_specs = 1 ,
#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
},
{
.type = IIO_ACTIVITY,
.modified = 1 ,
.channel2 = IIO_MOD_RUNNING,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.scan_index = -1 , /* No buffer support */
#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
.event_spec = &iio_running_event,
.num_event_specs = 1 ,
#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
},
{
.type = IIO_ACTIVITY,
.modified = 1 ,
.channel2 = IIO_MOD_WALKING,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.scan_index = -1 , /* No buffer support */
#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
.event_spec = &iio_walking_event,
.num_event_specs = 1 ,
#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
},
};
static int __iio_dummy_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val)
{
struct iio_dummy_state *st = iio_priv(indio_dev);
guard(mutex)(&st->lock);
switch (chan->type) {
case IIO_VOLTAGE:
if (chan->output) {
/* Set integer part to cached value */
*val = st->dac_val;
return IIO_VAL_INT;
} else if (chan->differential) {
if (chan->channel == 1 )
*val = st->differential_adc_val[0 ];
else
*val = st->differential_adc_val[1 ];
return IIO_VAL_INT;
} else {
*val = st->single_ended_adc_val;
return IIO_VAL_INT;
}
case IIO_ACCEL:
*val = st->accel_val;
return IIO_VAL_INT;
default :
return -EINVAL;
}
}
static int __iio_dummy_read_processed(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val)
{
struct iio_dummy_state *st = iio_priv(indio_dev);
guard(mutex)(&st->lock);
switch (chan->type) {
case IIO_STEPS:
*val = st->steps;
return IIO_VAL_INT;
case IIO_ACTIVITY:
switch (chan->channel2) {
case IIO_MOD_RUNNING:
*val = st->activity_running;
return IIO_VAL_INT;
case IIO_MOD_WALKING:
*val = st->activity_walking;
return IIO_VAL_INT;
default :
return -EINVAL;
}
default :
return -EINVAL;
}
}
/**
* iio_dummy_read_raw ( ) - data read function .
* @ indio_dev : the struct iio_dev associated with this device instance
* @ chan : the channel whose data is to be read
* @ val : first element of returned value ( typically INT )
* @ val2 : second element of returned value ( typically MICRO )
* @ mask : what we actually want to read as per the info_mask_ *
* in iio_chan_spec .
*/
static int iio_dummy_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long mask)
{
struct iio_dummy_state *st = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW: /* magic value - channel value read */
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __iio_dummy_read_raw(indio_dev, chan, val);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_PROCESSED:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __iio_dummy_read_processed(indio_dev, chan, val);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_OFFSET:
/* only single ended adc -> 7 */
*val = 7 ;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_VOLTAGE:
switch (chan->differential) {
case 0 :
/* only single ended adc -> 0.001333 */
*val = 0 ;
*val2 = 1333 ;
return IIO_VAL_INT_PLUS_MICRO;
case 1 :
/* all differential adc -> 0.000001344 */
*val = 0 ;
*val2 = 1344 ;
return IIO_VAL_INT_PLUS_NANO;
default :
return -EINVAL;
}
default :
return -EINVAL;
}
case IIO_CHAN_INFO_CALIBBIAS: {
guard(mutex)(&st->lock);
/* only the acceleration axis - read from cache */
*val = st->accel_calibbias;
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_CALIBSCALE: {
guard(mutex)(&st->lock);
*val = st->accel_calibscale->val;
*val2 = st->accel_calibscale->val2;
return IIO_VAL_INT_PLUS_MICRO;
}
case IIO_CHAN_INFO_SAMP_FREQ:
*val = 3 ;
*val2 = 33 ;
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_ENABLE: {
guard(mutex)(&st->lock);
switch (chan->type) {
case IIO_STEPS:
*val = st->steps_enabled;
return IIO_VAL_INT;
default :
return -EINVAL;
}
}
case IIO_CHAN_INFO_CALIBHEIGHT: {
guard(mutex)(&st->lock);
switch (chan->type) {
case IIO_STEPS:
*val = st->height;
return IIO_VAL_INT;
default :
return -EINVAL;
}
}
default :
return -EINVAL;
}
}
/**
* iio_dummy_write_raw ( ) - data write function .
* @ indio_dev : the struct iio_dev associated with this device instance
* @ chan : the channel whose data is to be written
* @ val : first element of value to set ( typically INT )
* @ val2 : second element of value to set ( typically MICRO )
* @ mask : what we actually want to write as per the info_mask_ *
* in iio_chan_spec .
*
* Note that all raw writes are assumed IIO_VAL_INT and info mask elements
* are assumed to be IIO_INT_PLUS_MICRO unless the callback write_raw_get_fmt
* in struct iio_info is provided by the driver .
*/
static int iio_dummy_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask)
{
int i;
struct iio_dummy_state *st = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_VOLTAGE:
if (chan->output == 0 )
return -EINVAL;
scoped_guard(mutex, &st->lock) {
/* Locking not required as writing single value */
st->dac_val = val;
}
return 0 ;
default :
return -EINVAL;
}
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_STEPS:
scoped_guard(mutex, &st->lock) {
st->steps = val;
}
return 0 ;
case IIO_ACTIVITY:
if (val < 0 )
val = 0 ;
if (val > 100 )
val = 100 ;
switch (chan->channel2) {
case IIO_MOD_RUNNING:
st->activity_running = val;
return 0 ;
case IIO_MOD_WALKING:
st->activity_walking = val;
return 0 ;
default :
return -EINVAL;
}
break ;
default :
return -EINVAL;
}
case IIO_CHAN_INFO_CALIBSCALE: {
guard(mutex)(&st->lock);
/* Compare against table - hard matching here */
for (i = 0 ; i < ARRAY_SIZE(dummy_scales); i++)
if (val == dummy_scales[i].val &&
val2 == dummy_scales[i].val2)
break ;
if (i == ARRAY_SIZE(dummy_scales))
return -EINVAL;
st->accel_calibscale = &dummy_scales[i];
return 0 ;
}
case IIO_CHAN_INFO_CALIBBIAS:
scoped_guard(mutex, &st->lock) {
st->accel_calibbias = val;
}
return 0 ;
case IIO_CHAN_INFO_ENABLE:
switch (chan->type) {
case IIO_STEPS:
scoped_guard(mutex, &st->lock) {
st->steps_enabled = val;
}
return 0 ;
default :
return -EINVAL;
}
case IIO_CHAN_INFO_CALIBHEIGHT:
switch (chan->type) {
case IIO_STEPS:
st->height = val;
return 0 ;
default :
return -EINVAL;
}
default :
return -EINVAL;
}
}
/*
* Device type specific information .
*/
static const struct iio_info iio_dummy_info = {
.read_raw = &iio_dummy_read_raw,
.write_raw = &iio_dummy_write_raw,
#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
.read_event_config = &iio_simple_dummy_read_event_config,
.write_event_config = &iio_simple_dummy_write_event_config,
.read_event_value = &iio_simple_dummy_read_event_value,
.write_event_value = &iio_simple_dummy_write_event_value,
#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
};
/**
* iio_dummy_init_device ( ) - device instance specific init
* @ indio_dev : the iio device structure
*
* Most drivers have one of these to set up default values ,
* reset the device to known state etc .
*/
static int iio_dummy_init_device(struct iio_dev *indio_dev)
{
struct iio_dummy_state *st = iio_priv(indio_dev);
st->dac_val = 0 ;
st->single_ended_adc_val = 73 ;
st->differential_adc_val[0 ] = 33 ;
st->differential_adc_val[1 ] = -34 ;
st->accel_val = 34 ;
st->accel_calibbias = -7 ;
st->accel_calibscale = &dummy_scales[0 ];
st->steps = 47 ;
st->activity_running = 98 ;
st->activity_walking = 4 ;
return 0 ;
}
/**
* iio_dummy_probe ( ) - device instance probe
* @ name : name of this instance .
*
* Arguments are bus type specific .
* I2C : iio_dummy_probe ( struct i2c_client * client ,
* const struct i2c_device_id * id )
* SPI : iio_dummy_probe ( struct spi_device * spi )
*/
static struct iio_sw_device *iio_dummy_probe(const char *name)
{
int ret;
struct iio_dev *indio_dev;
struct iio_dummy_state *st;
struct iio_sw_device *swd;
struct device *parent = NULL;
/*
* With hardware : Set the parent device .
* parent = & spi - > dev ;
* parent = & client - > dev ;
*/
swd = kzalloc(sizeof (*swd), GFP_KERNEL);
if (!swd)
return ERR_PTR(-ENOMEM);
/*
* Allocate an IIO device .
*
* This structure contains all generic state
* information about the device instance .
* It also has a region ( accessed by iio_priv ( )
* for chip specific state information .
*/
indio_dev = iio_device_alloc(parent, sizeof (*st));
if (!indio_dev) {
ret = -ENOMEM;
goto error_free_swd;
}
st = iio_priv(indio_dev);
mutex_init(&st->lock);
iio_dummy_init_device(indio_dev);
/*
* Make the iio_dev struct available to remove function .
* Bus equivalents
* i2c_set_clientdata ( client , indio_dev ) ;
* spi_set_drvdata ( spi , indio_dev ) ;
*/
swd->device = indio_dev;
/*
* Set the device name .
*
* This is typically a part number and obtained from the module
* id table .
* e . g . for i2c and spi :
* indio_dev - > name = id - > name ;
* indio_dev - > name = spi_get_device_id ( spi ) - > name ;
*/
indio_dev->name = kstrdup(name, GFP_KERNEL);
if (!indio_dev->name) {
ret = -ENOMEM;
goto error_free_device;
}
/* Provide description of available channels */
indio_dev->channels = iio_dummy_channels;
indio_dev->num_channels = ARRAY_SIZE(iio_dummy_channels);
/*
* Provide device type specific interface functions and
* constant data .
*/
indio_dev->info = &iio_dummy_info;
/* Specify that device provides sysfs type interfaces */
indio_dev->modes = INDIO_DIRECT_MODE;
ret = iio_simple_dummy_events_register(indio_dev);
if (ret < 0 )
goto error_free_name;
ret = iio_simple_dummy_configure_buffer(indio_dev);
if (ret < 0 )
goto error_unregister_events;
ret = iio_device_register(indio_dev);
if (ret < 0 )
goto error_unconfigure_buffer;
iio_swd_group_init_type_name(swd, name, &iio_dummy_type);
return swd;
error_unconfigure_buffer:
iio_simple_dummy_unconfigure_buffer(indio_dev);
error_unregister_events:
iio_simple_dummy_events_unregister(indio_dev);
error_free_name:
kfree(indio_dev->name);
error_free_device:
iio_device_free(indio_dev);
error_free_swd:
kfree(swd);
return ERR_PTR(ret);
}
/**
* iio_dummy_remove ( ) - device instance removal function
* @ swd : pointer to software IIO device abstraction
*
* Parameters follow those of iio_dummy_probe for buses .
*/
static int iio_dummy_remove(struct iio_sw_device *swd)
{
/*
* Get a pointer to the device instance iio_dev structure
* from the bus subsystem . E . g .
* struct iio_dev * indio_dev = i2c_get_clientdata ( client ) ;
* struct iio_dev * indio_dev = spi_get_drvdata ( spi ) ;
*/
struct iio_dev *indio_dev = swd->device;
/* Unregister the device */
iio_device_unregister(indio_dev);
/* Device specific code to power down etc */
/* Buffered capture related cleanup */
iio_simple_dummy_unconfigure_buffer(indio_dev);
iio_simple_dummy_events_unregister(indio_dev);
/* Free all structures */
kfree(indio_dev->name);
iio_device_free(indio_dev);
return 0 ;
}
/*
* module_iio_sw_device_driver ( ) - device driver registration
*
* Varies depending on bus type of the device . As there is no device
* here , call probe directly . For information on device registration
* i2c :
* Documentation / i2c / writing - clients . rst
* spi :
* Documentation / spi / spi - summary . rst
*/
static const struct iio_sw_device_ops iio_dummy_device_ops = {
.probe = iio_dummy_probe,
.remove = iio_dummy_remove,
};
static struct iio_sw_device_type iio_dummy_device = {
.name = "dummy" ,
.owner = THIS_MODULE,
.ops = &iio_dummy_device_ops,
};
module_iio_sw_device_driver(iio_dummy_device);
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>" );
MODULE_DESCRIPTION("IIO dummy driver" );
MODULE_LICENSE("GPL v2" );
Messung V0.5 in Prozent C=90 H=95 G=92
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-10-01)
¤
*© Formatika GbR, Deutschland