OpenCloudOS-Kernel/drivers/iio/dummy/iio_simple_dummy.c

723 lines
18 KiB
C

// 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, 0x8 }, /* 0.000100 */
{ 0, 133, 0x7 }, /* 0.000133 */
{ 733, 13, 0x9 }, /* 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 */
},
};
/**
* 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 = -EINVAL;
mutex_lock(&st->lock);
switch (mask) {
case IIO_CHAN_INFO_RAW: /* magic value - channel value read */
switch (chan->type) {
case IIO_VOLTAGE:
if (chan->output) {
/* Set integer part to cached value */
*val = st->dac_val;
ret = IIO_VAL_INT;
} else if (chan->differential) {
if (chan->channel == 1)
*val = st->differential_adc_val[0];
else
*val = st->differential_adc_val[1];
ret = IIO_VAL_INT;
} else {
*val = st->single_ended_adc_val;
ret = IIO_VAL_INT;
}
break;
case IIO_ACCEL:
*val = st->accel_val;
ret = IIO_VAL_INT;
break;
default:
break;
}
break;
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_STEPS:
*val = st->steps;
ret = IIO_VAL_INT;
break;
case IIO_ACTIVITY:
switch (chan->channel2) {
case IIO_MOD_RUNNING:
*val = st->activity_running;
ret = IIO_VAL_INT;
break;
case IIO_MOD_WALKING:
*val = st->activity_walking;
ret = IIO_VAL_INT;
break;
default:
break;
}
break;
default:
break;
}
break;
case IIO_CHAN_INFO_OFFSET:
/* only single ended adc -> 7 */
*val = 7;
ret = IIO_VAL_INT;
break;
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;
ret = IIO_VAL_INT_PLUS_MICRO;
break;
case 1:
/* all differential adc -> 0.000001344 */
*val = 0;
*val2 = 1344;
ret = IIO_VAL_INT_PLUS_NANO;
}
break;
default:
break;
}
break;
case IIO_CHAN_INFO_CALIBBIAS:
/* only the acceleration axis - read from cache */
*val = st->accel_calibbias;
ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_CALIBSCALE:
*val = st->accel_calibscale->val;
*val2 = st->accel_calibscale->val2;
ret = IIO_VAL_INT_PLUS_MICRO;
break;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = 3;
*val2 = 33;
ret = IIO_VAL_INT_PLUS_NANO;
break;
case IIO_CHAN_INFO_ENABLE:
switch (chan->type) {
case IIO_STEPS:
*val = st->steps_enabled;
ret = IIO_VAL_INT;
break;
default:
break;
}
break;
case IIO_CHAN_INFO_CALIBHEIGHT:
switch (chan->type) {
case IIO_STEPS:
*val = st->height;
ret = IIO_VAL_INT;
break;
default:
break;
}
break;
default:
break;
}
mutex_unlock(&st->lock);
return ret;
}
/**
* 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;
int ret = 0;
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;
/* Locking not required as writing single value */
mutex_lock(&st->lock);
st->dac_val = val;
mutex_unlock(&st->lock);
return 0;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_STEPS:
mutex_lock(&st->lock);
st->steps = val;
mutex_unlock(&st->lock);
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:
mutex_lock(&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))
ret = -EINVAL;
else
st->accel_calibscale = &dummy_scales[i];
mutex_unlock(&st->lock);
return ret;
case IIO_CHAN_INFO_CALIBBIAS:
mutex_lock(&st->lock);
st->accel_calibbias = val;
mutex_unlock(&st->lock);
return 0;
case IIO_CHAN_INFO_ENABLE:
switch (chan->type) {
case IIO_STEPS:
mutex_lock(&st->lock);
st->steps_enabled = val;
mutex_unlock(&st->lock);
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");