diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-06-10 20:48:34 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-06-10 20:48:34 -0700 |
commit | 78a66b00d97c89a43b1ee753814913c55ec2e3ee (patch) | |
tree | e5cb4bcf3db64b0fb0f68c4578e4722397605b71 | |
parent | e703f23747e25db6ccc1f752f787925f86c27f31 (diff) | |
parent | bbf5f037fad47e4affef6696aaf88a40b261e639 (diff) |
Merge tag 'iio-for-v4.2c' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next
Jonathan writes:
Third round of new IIO drivers, cleanups and functionality for the 4.2 cycle.
Given Linus announced a 4.8rc coming up, hopefully time for one more
lot of IIO patches this cycle. Some of these are actually
improvements / fixes for patches earlier in the cycle.
New device support
* st_accel driver - support devices with 8 bit channels.
Cleanup
* A general cleanup of the iio tools under /tools/ from Hartmut.
I'm more than a little embarassed by how bad some of these were! Are well,
much more refined and less bug prone now.
These cover lots of stuff like unhandled error returns, memory leaks as
well as general refactoring to tidy the code up.
* iio_simple_dummy - fix memory leaks in the init functions, drop some
pointless error returns from functions that never generate errors and
make the module parameter explicitly unsigned.
* More buffer handling reworks from Lars-Peter, this time targetting hardware
buffers (a little used corner that looks likely to get more use in the near
future). Specifically:
- Always compute the masklength as inkernel buffer users may need it.
- Add a means of labeling which buffer modes a given buffer implementation
supports.
- In the case of hardware buffers, require strict scan matching rather than
matching to a superset. Currently the demux is bypassed by these drivers
(this may well not change for efficiency reasons) so allowing a superset
of channels to be selected would otherwise lead to more data than requested
confusing userspace.
Driver funcationality improvments
* mmc35240 - adds a compensation to the raw values as borrowed form Memsic's
own input driver.
* mma8452
- event support
- event debouncing
- high pass filter configuration
- triggers
* vf610 - allow conversion mode to be adjusted
Fixlets
* mmc35240
- Off by one error that by coincidence had no real effect.
- i2c_device_name should be lowercase.
- Lack of null terminator at end of attributes array.
- Avoid computing the fractional part of the magnetic field by moving
the scaling into userspace where floating point is available to simplify
the maths.
- Use a smaller sleep before assuming the measurement is done. This is
safe and improves the possible polling rate.
- Fix sensitivity on z-axis - datasheet disagrees with Memsic's releasedd
code and the value used in the code seems to be correct.
* stk3310 - make a local variable signed to ensure error handling works.
* twl4030
- fix calculation of the temperature sense current - bug unlikely
to have ever been noticed as the difference is small.
- Fix errors in descriptions.
27 files changed, 1471 insertions, 317 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-vf610 b/Documentation/ABI/testing/sysfs-bus-iio-vf610 new file mode 100644 index 000000000000..ecbc1f4af921 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-vf610 @@ -0,0 +1,7 @@ +What: /sys/bus/iio/devices/iio:deviceX/conversion_mode +KernelVersion: 4.2 +Contact: linux-iio@vger.kernel.org +Description: + Specifies the hardware conversion mode used. The three + available modes are "normal", "high-speed" and "low-power", + where the last is the default mode. diff --git a/Documentation/devicetree/bindings/iio/adc/vf610-adc.txt b/Documentation/devicetree/bindings/iio/adc/vf610-adc.txt index 1a4a43d5c9ea..3eb40e20c143 100644 --- a/Documentation/devicetree/bindings/iio/adc/vf610-adc.txt +++ b/Documentation/devicetree/bindings/iio/adc/vf610-adc.txt @@ -11,6 +11,13 @@ Required properties: - clock-names: Must contain "adc", matching entry in the clocks property. - vref-supply: The regulator supply ADC reference voltage. +Recommended properties: +- fsl,adck-max-frequency: Maximum frequencies according to datasheets operating + requirements. Three values are required, depending on conversion mode: + - Frequency in normal mode (ADLPC=0, ADHSC=0) + - Frequency in high-speed mode (ADLPC=0, ADHSC=1) + - Frequency in low-power mode (ADLPC=1, ADHSC=0) + Example: adc0: adc@4003b000 { compatible = "fsl,vf610-adc"; @@ -18,5 +25,7 @@ adc0: adc@4003b000 { interrupts = <0 53 0x04>; clocks = <&clks VF610_CLK_ADC0>; clock-names = "adc"; + fsl,adck-max-frequency = <30000000>, <40000000>, + <20000000>; vref-supply = <®_vcc_3v3_mcu>; }; diff --git a/Documentation/devicetree/bindings/iio/st-sensors.txt b/Documentation/devicetree/bindings/iio/st-sensors.txt index fb5e0c2d18b5..8a6be3bdf267 100644 --- a/Documentation/devicetree/bindings/iio/st-sensors.txt +++ b/Documentation/devicetree/bindings/iio/st-sensors.txt @@ -30,6 +30,7 @@ Accelerometers: - st,lsm330d-accel - st,lsm330dl-accel - st,lsm330dlc-accel +- st,lis331dl-accel - st,lis331dlh-accel - st,lsm303dl-accel - st,lsm303dlm-accel diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 877ce2954196..e8e2077c7244 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -9,7 +9,7 @@ * * 7-bit I2C slave address 0x1c/0x1d (pin selectable) * - * TODO: interrupt, thresholding, orientation / freefall events, autosleep + * TODO: orientation / freefall events, autosleep */ #include <linux/module.h> @@ -18,21 +18,40 @@ #include <linux/iio/sysfs.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> +#include <linux/iio/events.h> #include <linux/delay.h> #define MMA8452_STATUS 0x00 #define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */ #define MMA8452_OUT_Y 0x03 #define MMA8452_OUT_Z 0x05 +#define MMA8452_INT_SRC 0x0c #define MMA8452_WHO_AM_I 0x0d #define MMA8452_DATA_CFG 0x0e +#define MMA8452_HP_FILTER_CUTOFF 0x0f +#define MMA8452_HP_FILTER_CUTOFF_SEL_MASK (BIT(0) | BIT(1)) +#define MMA8452_TRANSIENT_CFG 0x1d +#define MMA8452_TRANSIENT_CFG_ELE BIT(4) +#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1) +#define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0) +#define MMA8452_TRANSIENT_SRC 0x1e +#define MMA8452_TRANSIENT_SRC_XTRANSE BIT(1) +#define MMA8452_TRANSIENT_SRC_YTRANSE BIT(3) +#define MMA8452_TRANSIENT_SRC_ZTRANSE BIT(5) +#define MMA8452_TRANSIENT_THS 0x1f +#define MMA8452_TRANSIENT_THS_MASK 0x7f +#define MMA8452_TRANSIENT_COUNT 0x20 #define MMA8452_OFF_X 0x2f #define MMA8452_OFF_Y 0x30 #define MMA8452_OFF_Z 0x31 #define MMA8452_CTRL_REG1 0x2a #define MMA8452_CTRL_REG2 0x2b #define MMA8452_CTRL_REG2_RST BIT(6) +#define MMA8452_CTRL_REG4 0x2d +#define MMA8452_CTRL_REG5 0x2e #define MMA8452_MAX_REG 0x31 @@ -47,6 +66,10 @@ #define MMA8452_DATA_CFG_FS_2G 0 #define MMA8452_DATA_CFG_FS_4G 1 #define MMA8452_DATA_CFG_FS_8G 2 +#define MMA8452_DATA_CFG_HPF_MASK BIT(4) + +#define MMA8452_INT_DRDY BIT(0) +#define MMA8452_INT_TRANS BIT(5) #define MMA8452_DEVICE_ID 0x2a @@ -109,6 +132,12 @@ static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n, return -EINVAL; } +static int mma8452_get_odr_index(struct mma8452_data *data) +{ + return (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >> + MMA8452_CTRL_DR_SHIFT; +} + static const int mma8452_samp_freq[8][2] = { {800, 0}, {400, 0}, {200, 0}, {100, 0}, {50, 0}, {12, 500000}, {6, 250000}, {1, 560000} @@ -124,6 +153,30 @@ static const int mma8452_scales[3][2] = { {0, 9577}, {0, 19154}, {0, 38307} }; +/* Datasheet table 35 (step time vs sample frequency) */ +static const int mma8452_transient_time_step_us[8] = { + 1250, + 2500, + 5000, + 10000, + 20000, + 20000, + 20000, + 20000 +}; + +/* Datasheet table 18 (normal mode) */ +static const int mma8452_hp_filter_cutoff[8][4][2] = { + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 800 Hz sample */ + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 400 Hz sample */ + { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, /* 200 Hz sample */ + { {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, /* 100 Hz sample */ + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 50 Hz sample */ + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 12.5 Hz sample */ + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 6.25 Hz sample */ + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} } /* 1.56 Hz sample */ +}; + static ssize_t mma8452_show_samp_freq_avail(struct device *dev, struct device_attribute *attr, char *buf) { @@ -138,9 +191,23 @@ static ssize_t mma8452_show_scale_avail(struct device *dev, ARRAY_SIZE(mma8452_scales)); } +static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mma8452_data *data = iio_priv(indio_dev); + int i = mma8452_get_odr_index(data); + + return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[i], + ARRAY_SIZE(mma8452_hp_filter_cutoff[0])); +} + static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail); static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO, mma8452_show_scale_avail, NULL, 0); +static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available, + S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0); static int mma8452_get_samp_freq_index(struct mma8452_data *data, int val, int val2) @@ -156,6 +223,31 @@ static int mma8452_get_scale_index(struct mma8452_data *data, ARRAY_SIZE(mma8452_scales), val, val2); } +static int mma8452_get_hp_filter_index(struct mma8452_data *data, + int val, int val2) +{ + int i = mma8452_get_odr_index(data); + + return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i], + ARRAY_SIZE(mma8452_scales[0]), val, val2); +} + +static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz) +{ + int i, ret; + + ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF); + if (ret < 0) + return ret; + + i = mma8452_get_odr_index(data); + ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK; + *hz = mma8452_hp_filter_cutoff[i][ret][0]; + *uHz = mma8452_hp_filter_cutoff[i][ret][1]; + + return 0; +} + static int mma8452_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -183,8 +275,7 @@ static int mma8452_read_raw(struct iio_dev *indio_dev, *val2 = mma8452_scales[i][1]; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: - i = (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >> - MMA8452_CTRL_DR_SHIFT; + i = mma8452_get_odr_index(data); *val = mma8452_samp_freq[i][0]; *val2 = mma8452_samp_freq[i][1]; return IIO_VAL_INT_PLUS_MICRO; @@ -195,6 +286,16 @@ static int mma8452_read_raw(struct iio_dev *indio_dev, return ret; *val = sign_extend32(ret, 7); return IIO_VAL_INT; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + if (data->data_cfg & MMA8452_DATA_CFG_HPF_MASK) { + ret = mma8452_read_hp_filter(data, val, val2); + if (ret < 0) + return ret; + } else { + *val = 0; + *val2 = 0; + } + return IIO_VAL_INT_PLUS_MICRO; } return -EINVAL; } @@ -236,12 +337,31 @@ fail: return ret; } +static int mma8452_set_hp_filter_frequency(struct mma8452_data *data, + int val, int val2) +{ + int i, reg; + + i = mma8452_get_hp_filter_index(data, val, val2); + if (i < 0) + return -EINVAL; + + reg = i2c_smbus_read_byte_data(data->client, + MMA8452_HP_FILTER_CUTOFF); + if (reg < 0) + return reg; + reg &= ~MMA8452_HP_FILTER_CUTOFF_SEL_MASK; + reg |= i; + + return mma8452_change_config(data, MMA8452_HP_FILTER_CUTOFF, reg); +} + static int mma8452_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct mma8452_data *data = iio_priv(indio_dev); - int i; + int i, ret; if (iio_buffer_enabled(indio_dev)) return -EBUSY; @@ -269,11 +389,217 @@ static int mma8452_write_raw(struct iio_dev *indio_dev, return -EINVAL; return mma8452_change_config(data, MMA8452_OFF_X + chan->scan_index, val); + + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + if (val == 0 && val2 == 0) { + data->data_cfg &= ~MMA8452_DATA_CFG_HPF_MASK; + } else { + data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK; + ret = mma8452_set_hp_filter_frequency(data, val, val2); + if (ret < 0) + return ret; + } + return mma8452_change_config(data, MMA8452_DATA_CFG, + data->data_cfg); + default: return -EINVAL; } } +static int mma8452_read_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct mma8452_data *data = iio_priv(indio_dev); + int ret, us; + + switch (info) { + case IIO_EV_INFO_VALUE: + ret = i2c_smbus_read_byte_data(data->client, + MMA8452_TRANSIENT_THS); + if (ret < 0) + return ret; + + *val = ret & MMA8452_TRANSIENT_THS_MASK; + return IIO_VAL_INT; + + case IIO_EV_INFO_PERIOD: + ret = i2c_smbus_read_byte_data(data->client, + MMA8452_TRANSIENT_COUNT); + if (ret < 0) + return ret; + + us = ret * mma8452_transient_time_step_us[ + mma8452_get_odr_index(data)]; + *val = us / USEC_PER_SEC; + *val2 = us % USEC_PER_SEC; + return IIO_VAL_INT_PLUS_MICRO; + + case IIO_EV_INFO_HIGH_PASS_FILTER_3DB: + ret = i2c_smbus_read_byte_data(data->client, + MMA8452_TRANSIENT_CFG); + if (ret < 0) + return ret; + + if (ret & MMA8452_TRANSIENT_CFG_HPF_BYP) { + *val = 0; + *val2 = 0; + } else { + ret = mma8452_read_hp_filter(data, val, val2); + if (ret < 0) + return ret; + } + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int mma8452_write_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct mma8452_data *data = iio_priv(indio_dev); + int ret, reg, steps; + + switch (info) { + case IIO_EV_INFO_VALUE: + return mma8452_change_config(data, MMA8452_TRANSIENT_THS, + val & MMA8452_TRANSIENT_THS_MASK); + + case IIO_EV_INFO_PERIOD: + steps = (val * USEC_PER_SEC + val2) / + mma8452_transient_time_step_us[ + mma8452_get_odr_index(data)]; + + if (steps > 0xff) + return -EINVAL; + + return mma8452_change_config(data, MMA8452_TRANSIENT_COUNT, + steps); + case IIO_EV_INFO_HIGH_PASS_FILTER_3DB: + reg = i2c_smbus_read_byte_data(data->client, + MMA8452_TRANSIENT_CFG); + if (reg < 0) + return reg; + + if (val == 0 && val2 == 0) { + reg |= MMA8452_TRANSIENT_CFG_HPF_BYP; + } else { + reg &= ~MMA8452_TRANSIENT_CFG_HPF_BYP; + ret = mma8452_set_hp_filter_frequency(data, val, val2); + if (ret < 0) + return ret; + } + return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, reg); + + default: + return -EINVAL; + } +} + +static int mma8452_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct mma8452_data *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG); + if (ret < 0) + return ret; + + return ret & MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index) ? 1 : 0; +} + +static int mma8452_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct mma8452_data *data = iio_priv(indio_dev); + int val; + + val = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG); + if (val < 0) + return val; + + if (state) + val |= MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index); + else + val &= ~MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index); + + val |= MMA8452_TRANSIENT_CFG_ELE; + + return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, val); +} + +static void mma8452_transient_interrupt(struct iio_dev *indio_dev) +{ + struct mma8452_data *data = iio_priv(indio_dev); + s64 ts = iio_get_time_ns(); + int src; + + src = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_SRC); + if (src < 0) + return; + + if (src & MMA8452_TRANSIENT_SRC_XTRANSE) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + ts); + + if (src & MMA8452_TRANSIENT_SRC_YTRANSE) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Y, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + ts); + + if (src & MMA8452_TRANSIENT_SRC_ZTRANSE) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Z, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + ts); +} + +static irqreturn_t mma8452_interrupt(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct mma8452_data *data = iio_priv(indio_dev); + int ret = IRQ_NONE; + int src; + + src = i2c_smbus_read_byte_data(data->client, MMA8452_INT_SRC); + if (src < 0) + return IRQ_NONE; + + if (src & MMA8452_INT_DRDY) { + iio_trigger_poll_chained(indio_dev->trig); + ret = IRQ_HANDLED; + } + + if (src & MMA8452_INT_TRANS) { + mma8452_transient_interrupt(indio_dev); + ret = IRQ_HANDLED; + } + + return ret; +} + static irqreturn_t mma8452_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -316,6 +642,33 @@ static int mma8452_reg_access_dbg(struct iio_dev *indio_dev, return 0; } +static const struct iio_event_spec mma8452_transient_event[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB) + }, +}; + +/* + * Threshold is configured in fixed 8G/127 steps regardless of + * currently selected scale for measurement. + */ +static IIO_CONST_ATTR_NAMED(accel_transient_scale, in_accel_scale, "0.617742"); + +static struct attribute *mma8452_event_attributes[] = { + &iio_const_attr_accel_transient_scale.dev_attr.attr, + NULL, +}; + +static struct attribute_group mma8452_event_attribute_group = { + .attrs = mma8452_event_attributes, + .name = "events", +}; + #define MMA8452_CHANNEL(axis, idx) { \ .type = IIO_ACCEL, \ .modified = 1, \ @@ -323,7 +676,8 @@ static int mma8452_reg_access_dbg(struct iio_dev *indio_dev, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ - BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ .scan_index = idx, \ .scan_type = { \ .sign = 's', \ @@ -332,6 +686,8 @@ static int mma8452_reg_access_dbg(struct iio_dev *indio_dev, .shift = 4, \ .endianness = IIO_BE, \ }, \ + .event_spec = mma8452_transient_event, \ + .num_event_specs = ARRAY_SIZE(mma8452_transient_event), \ } static const struct iio_chan_spec mma8452_channels[] = { @@ -344,6 +700,7 @@ static const struct iio_chan_spec mma8452_channels[] = { static struct attribute *mma8452_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_accel_scale_available.dev_attr.attr, + &iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr, NULL }; @@ -355,12 +712,83 @@ static const struct iio_info mma8452_info = { .attrs = &mma8452_group, .read_raw = &mma8452_read_raw, .write_raw = &mma8452_write_raw, + .event_attrs = &mma8452_event_attribute_group, + .read_event_value = &mma8452_read_thresh, + .write_event_value = &mma8452_write_thresh, + .read_event_config = &mma8452_read_event_config, + .write_event_config = &mma8452_write_event_config, .debugfs_reg_access = &mma8452_reg_access_dbg, .driver_module = THIS_MODULE, }; static const unsigned long mma8452_scan_masks[] = {0x7, 0}; +static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct mma8452_data *data = iio_priv(indio_dev); + int reg; + + reg = i2c_smbus_read_byte_data(data->client, MMA8452_CTRL_REG4); + if (reg < 0) + return reg; + + if (state) + reg |= MMA8452_INT_DRDY; + else + reg &= ~MMA8452_INT_DRDY; + + return mma8452_change_config(data, MMA8452_CTRL_REG4, reg); +} + +static int mma8452_validate_device(struct iio_trigger *trig, + struct iio_dev *indio_dev) +{ + struct iio_dev *indio = iio_trigger_get_drvdata(trig); + + if (indio != indio_dev) + return -EINVAL; + + return 0; +} + +static const struct iio_trigger_ops mma8452_trigger_ops = { + .set_trigger_state = mma8452_data_rdy_trigger_set_state, + .validate_device = mma8452_validate_device, + .owner = THIS_MODULE, +}; + +static int mma8452_trigger_setup(struct iio_dev *indio_dev) +{ + struct mma8452_data *data = iio_priv(indio_dev); + struct iio_trigger *trig; + int ret; + + trig = devm_iio_trigger_alloc(&data->client->dev, "%s-dev%d", + indio_dev->name, + indio_dev->id); + if (!trig) + return -ENOMEM; + + trig->dev.parent = &data->client->dev; + trig->ops = &mma8452_trigger_ops; + iio_trigger_set_drvdata(trig, indio_dev); + + ret = iio_trigger_register(trig); + if (ret) + return ret; + + indio_dev->trig = trig; + return 0; +} + +static void mma8452_trigger_cleanup(struct iio_dev *indio_dev) +{ + if (indio_dev->trig) + iio_trigger_unregister(indio_dev->trig); +} + static int mma8452_reset(struct i2c_client *client) { int i; @@ -425,25 +853,77 @@ static int mma8452_probe(struct i2c_client *client, if (ret < 0) return ret; + /* + * By default set transient threshold to max to avoid events if + * enabling without configuring threshold. + */ + ret = i2c_smbus_write_byte_data(client, MMA8452_TRANSIENT_THS, + MMA8452_TRANSIENT_THS_MASK); + if (ret < 0) + return ret; + + if (client->irq) { + /* + * Although we enable the transient interrupt source once and + * for all here the transient event detection itself is not + * enabled until userspace asks for it by + * mma8452_write_event_config() + */ + int supported_interrupts = MMA8452_INT_DRDY | MMA8452_INT_TRANS; + int enabled_interrupts = MMA8452_INT_TRANS; + + /* Assume wired to INT1 pin */ + ret = i2c_smbus_write_byte_data(client, + MMA8452_CTRL_REG5, + supported_interrupts); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, + MMA8452_CTRL_REG4, + enabled_interrupts); + if (ret < 0) + return ret; + + ret = mma8452_trigger_setup(indio_dev); + if (ret < 0) + return ret; + } + data->ctrl_reg1 = MMA8452_CTRL_ACTIVE | (MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT); ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1, data->ctrl_reg1); if (ret < 0) - return ret; + goto trigger_cleanup; ret = iio_triggered_buffer_setup(indio_dev, NULL, mma8452_trigger_handler, NULL); if (ret < 0) - return ret; + goto trigger_cleanup; + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, + client->irq, + NULL, mma8452_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + client->name, indio_dev); + if (ret) + goto buffer_cleanup; + } ret = iio_device_register(indio_dev); if (ret < 0) goto buffer_cleanup; + return 0; buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); + +trigger_cleanup: + mma8452_trigger_cleanup(indio_dev); + return ret; } @@ -453,6 +933,7 @@ static int mma8452_remove(struct i2c_client *client) iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); + mma8452_trigger_cleanup(indio_dev); mma8452_standby(iio_priv(indio_dev)); return 0; diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 7ee9724b1428..aa1001931d0c 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -20,6 +20,7 @@ #define LSM330D_ACCEL_DEV_NAME "lsm330d_accel" #define LSM330DL_ACCEL_DEV_NAME "lsm330dl_accel" #define LSM330DLC_ACCEL_DEV_NAME "lsm330dlc_accel" +#define LIS331DL_ACCEL_DEV_NAME "lis331dl_accel" #define LIS331DLH_ACCEL_DEV_NAME "lis331dlh" #define LSM303DL_ACCEL_DEV_NAME "lsm303dl_accel" #define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel" diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 211b13271c61..4002e6410444 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -153,6 +153,44 @@ #define ST_ACCEL_4_IG1_EN_MASK 0x08 #define ST_ACCEL_4_MULTIREAD_BIT true +/* CUSTOM VALUES FOR SENSOR 5 */ +#define ST_ACCEL_5_WAI_EXP 0x3b +#define ST_ACCEL_5_ODR_ADDR 0x20 +#define ST_ACCEL_5_ODR_MASK 0x80 +#define ST_ACCEL_5_ODR_AVL_100HZ_VAL 0x00 +#define ST_ACCEL_5_ODR_AVL_400HZ_VAL 0x01 +#define ST_ACCEL_5_PW_ADDR 0x20 +#define ST_ACCEL_5_PW_MASK 0x40 +#define ST_ACCEL_5_FS_ADDR 0x20 +#define ST_ACCEL_5_FS_MASK 0x20 +#define ST_ACCEL_5_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_5_FS_AVL_8_VAL 0X01 +/* TODO: check these resulting gain settings, these are not in the datsheet */ +#define ST_ACCEL_5_FS_AVL_2_GAIN IIO_G_TO_M_S_2(18000) +#define ST_ACCEL_5_FS_AVL_8_GAIN IIO_G_TO_M_S_2(72000) +#define ST_ACCEL_5_DRDY_IRQ_ADDR 0x22 +#define ST_ACCEL_5_DRDY_IRQ_INT1_MASK 0x04 +#define ST_ACCEL_5_DRDY_IRQ_INT2_MASK 0x20 +#define ST_ACCEL_5_IG1_EN_ADDR 0x21 +#define ST_ACCEL_5_IG1_EN_MASK 0x08 +#define ST_ACCEL_5_MULTIREAD_BIT false + +static const struct iio_chan_spec st_accel_8bit_channels[] = { + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 8, 8, + ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 8, 8, + ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 8, 8, + ST_ACCEL_DEFAULT_OUT_Z_L_ADDR+1), + IIO_CHAN_SOFT_TIMESTAMP(3) +}; + static const struct iio_chan_spec st_accel_12bit_channels[] = { ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), @@ -454,6 +492,54 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT, .bootime = 2, /* guess */ }, + { + .wai = ST_ACCEL_5_WAI_EXP, + .sensors_supported = { + [0] = LIS331DL_ACCEL_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_accel_8bit_channels, + .odr = { + .addr = ST_ACCEL_5_ODR_ADDR, + .mask = ST_ACCEL_5_ODR_MASK, + .odr_avl = { + { 100, ST_ACCEL_5_ODR_AVL_100HZ_VAL }, + { 400, ST_ACCEL_5_ODR_AVL_400HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_ACCEL_5_PW_ADDR, + .mask = ST_ACCEL_5_PW_MASK, + .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_ACCEL_5_FS_ADDR, + .mask = ST_ACCEL_5_FS_MASK, + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = ST_ACCEL_5_FS_AVL_2_VAL, + .gain = ST_ACCEL_5_FS_AVL_2_GAIN, + }, + [1] = { + .num = ST_ACCEL_FS_AVL_8G, + .value = ST_ACCEL_5_FS_AVL_8_VAL, + .gain = ST_ACCEL_5_FS_AVL_8_GAIN, + }, + }, + }, + .drdy_irq = { + .addr = ST_ACCEL_5_DRDY_IRQ_ADDR, + .mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK, + .mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK, + }, + .multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT, + .bootime = 2, /* guess */ + }, }; static int st_accel_read_raw(struct iio_dev *indio_dev, diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 6b720c190b2d..d4ad72ca4a3d 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -49,6 +49,10 @@ static const struct of_device_id st_accel_of_match[] = { .data = LSM330DLC_ACCEL_DEV_NAME, }, { + .compatible = "st,lis331dl-accel", + .data = LIS331DL_ACCEL_DEV_NAME, + }, + { .compatible = "st,lis331dlh-accel", .data = LIS331DLH_ACCEL_DEV_NAME, }, diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index 94c5f05b4bc1..06f4792240f0 100644 --- a/drivers/iio/adc/twl4030-madc.c +++ b/drivers/iio/adc/twl4030-madc.c @@ -235,7 +235,7 @@ static int twl4030battery_temperature(int raw_volt) if (ret < 0) return ret; - curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; + curr = ((val & TWL4030_BCI_ITHSENS) + 1) * 10; /* Getting and calculating the thermistor resistance in ohms */ res = volt * 1000 / curr; /* calculating temperature */ @@ -662,10 +662,8 @@ EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion); * * @madc: pointer to twl4030_madc_data struct * @chan: can be one of the two values: - * TWL4030_BCI_ITHEN - * Enables bias current for main battery type reading - * TWL4030_BCI_TYPEN - * Enables bias current for main battery temperature sensing + * 0 - Enables bias current for main battery type reading + * 1 - Enables bias current for main battery temperature sensing * @on: enable or disable chan. * * Function to enable or disable bias current for diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 56292ae4538d..480f335a0f9f 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -118,15 +118,21 @@ enum average_sel { VF610_ADC_SAMPLE_32, }; +enum conversion_mode_sel { + VF610_ADC_CONV_NORMAL, + VF610_ADC_CONV_HIGH_SPEED, + VF610_ADC_CONV_LOW_POWER, +}; + struct vf610_adc_feature { enum clk_sel clk_sel; enum vol_ref vol_ref; + enum conversion_mode_sel conv_mode; int clk_div; int sample_rate; int res_mode; - bool lpm; bool calibration; bool ovwren; }; @@ -139,6 +145,8 @@ struct vf610_adc { u32 vref_uv; u32 value; struct regulator *vref; + + u32 max_adck_rate[3]; struct vf610_adc_feature adc_feature; u32 sample_freq_avail[5]; @@ -148,46 +156,22 @@ struct vf610_adc { static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 }; -#define VF610_ADC_CHAN(_idx, _chan_type) { \ - .type = (_chan_type), \ - .indexed = 1, \ - .channel = (_idx), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ), \ -} - -#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \ - .type = (_chan_type), \ - .channel = (_idx), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ -} - -static const struct iio_chan_spec vf610_adc_iio_channels[] = { - VF610_ADC_CHAN(0, IIO_VOLTAGE), - VF610_ADC_CHAN(1, IIO_VOLTAGE), - VF610_ADC_CHAN(2, IIO_VOLTAGE), - VF610_ADC_CHAN(3, IIO_VOLTAGE), - VF610_ADC_CHAN(4, IIO_VOLTAGE), - VF610_ADC_CHAN(5, IIO_VOLTAGE), - VF610_ADC_CHAN(6, IIO_VOLTAGE), - VF610_ADC_CHAN(7, IIO_VOLTAGE), - VF610_ADC_CHAN(8, IIO_VOLTAGE), - VF610_ADC_CHAN(9, IIO_VOLTAGE), - VF610_ADC_CHAN(10, IIO_VOLTAGE), - VF610_ADC_CHAN(11, IIO_VOLTAGE), - VF610_ADC_CHAN(12, IIO_VOLTAGE), - VF610_ADC_CHAN(13, IIO_VOLTAGE), - VF610_ADC_CHAN(14, IIO_VOLTAGE), - VF610_ADC_CHAN(15, IIO_VOLTAGE), - VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP), - /* sentinel */ -}; - static inline void vf610_adc_calculate_rates(struct vf610_adc *info) { + struct vf610_adc_feature *adc_feature = &info->adc_feature; unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk); - int i; + int divisor, i; + + adck_rate = info->max_adck_rate[adc_feature->conv_mode]; + + if (adck_rate) { + /* calculate clk divider which is within specification */ + divisor = ipg_rate / adck_rate; + adc_feature->clk_div = 1 << fls(divisor + 1); + } else { + /* fall-back value using a safe divisor */ + adc_feature->clk_div = 8; + } /* * Calculate ADC sample frequencies @@ -219,10 +203,8 @@ static inline void vf610_adc_cfg_init(struct vf610_adc *info) adc_feature->res_mode = 12; adc_feature->sample_rate = 1; - adc_feature->lpm = true; - /* Use a save ADCK which is below 20MHz on all devices */ - adc_feature->clk_div = 8; + adc_feature->conv_mode = VF610_ADC_CONV_LOW_POWER; vf610_adc_calculate_rates(info); } @@ -304,10 +286,12 @@ static void vf610_adc_cfg_set(struct vf610_adc *info) cfg_data = readl(info->regs + VF610_REG_ADC_CFG); cfg_data &= ~VF610_ADC_ADLPC_EN; - if (adc_feature->lpm) + if (adc_feature->conv_mode == VF610_ADC_CONV_LOW_POWER) cfg_data |= VF610_ADC_ADLPC_EN; cfg_data &= ~VF610_ADC_ADHSC_EN; + if (adc_feature->conv_mode == VF610_ADC_CONV_HIGH_SPEED) + cfg_data |= VF610_ADC_ADHSC_EN; writel(cfg_data, info->regs + VF610_REG_ADC_CFG); } @@ -409,6 +393,81 @@ static void vf610_adc_hw_init(struct vf610_adc *info) vf610_adc_cfg_set(info); } +static int vf610_set_conversion_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct vf610_adc *info = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + info->adc_feature.conv_mode = mode; + vf610_adc_calculate_rates(info); + vf610_adc_hw_init(info); + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static int vf610_get_conversion_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct vf610_adc *info = iio_priv(indio_dev); + + return info->adc_feature.conv_mode; +} + +static const char * const vf610_conv_modes[] = { "normal", "high-speed", + "low-power" }; + +static const struct iio_enum vf610_conversion_mode = { + .items = vf610_conv_modes, + .num_items = ARRAY_SIZE(vf610_conv_modes), + .get = vf610_get_conversion_mode, + .set = vf610_set_conversion_mode, +}; + +static const struct iio_chan_spec_ext_info vf610_ext_info[] = { + IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, &vf610_conversion_mode), + {}, +}; + +#define VF610_ADC_CHAN(_idx, _chan_type) { \ + .type = (_chan_type), \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .ext_info = vf610_ext_info, \ +} + +#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \ + .type = (_chan_type), \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ +} + +static const struct iio_chan_spec vf610_adc_iio_channels[] = { + VF610_ADC_CHAN(0, IIO_VOLTAGE), + VF610_ADC_CHAN(1, IIO_VOLTAGE), + VF610_ADC_CHAN(2, IIO_VOLTAGE), + VF610_ADC_CHAN(3, IIO_VOLTAGE), + VF610_ADC_CHAN(4, IIO_VOLTAGE), + VF610_ADC_CHAN(5, IIO_VOLTAGE), + VF610_ADC_CHAN(6, IIO_VOLTAGE), + VF610_ADC_CHAN(7, IIO_VOLTAGE), + VF610_ADC_CHAN(8, IIO_VOLTAGE), + VF610_ADC_CHAN(9, IIO_VOLTAGE), + VF610_ADC_CHAN(10, IIO_VOLTAGE), + VF610_ADC_CHAN(11, IIO_VOLTAGE), + VF610_ADC_CHAN(12, IIO_VOLTAGE), + VF610_ADC_CHAN(13, IIO_VOLTAGE), + VF610_ADC_CHAN(14, IIO_VOLTAGE), + VF610_ADC_CHAN(15, IIO_VOLTAGE), + VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP), + /* sentinel */ +}; + static int vf610_adc_read_data(struct vf610_adc *info) { int result; @@ -651,6 +710,9 @@ static int vf610_adc_probe(struct platform_device *pdev) info->vref_uv = regulator_get_voltage(info->vref); + of_property_read_u32_array(pdev->dev.of_node, "fsl,adck-max-frequency", + info->max_adck_rate, 3); + platform_set_drvdata(pdev, indio_dev); init_completion(&info->completion); diff --git a/drivers/iio/buffer_cb.c b/drivers/iio/buffer_cb.c index eb46e728aa2e..1648e6e5a848 100644 --- a/drivers/iio/buffer_cb.c +++ b/drivers/iio/buffer_cb.c @@ -33,6 +33,8 @@ static void iio_buffer_cb_release(struct iio_buffer *buffer) static const struct iio_buffer_access_funcs iio_cb_access = { .store_to = &iio_buffer_cb_store_to, .release = &iio_buffer_cb_release, + + .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED, }; struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index e1634d2b6ae8..8086cbcff87d 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -431,7 +431,9 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev, if (err < 0) goto st_sensors_free_memory; - if (byte_for_channel == 2) + if (byte_for_channel == 1) + *data = (s8)*outdata; + else if (byte_for_channel == 2) *data = (s16)get_unaligned_le16(outdata); else if (byte_for_channel == 3) *data = (s32)st_sensors_get_unaligned_le24(outdata); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 11291259b7b9..6eee1b044c60 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -239,13 +239,19 @@ static ssize_t iio_scan_el_show(struct device *dev, /* Note NULL used as error indicator as it doesn't make sense. */ static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks, unsigned int masklength, - const unsigned long *mask) + const unsigned long *mask, + bool strict) { if (bitmap_empty(mask, masklength)) return NULL; while (*av_masks) { - if (bitmap_subset(mask, av_masks, masklength)) - return av_masks; + if (strict) { + if (bitmap_equal(mask, av_masks, masklength)) + return av_masks; + } else { + if (bitmap_subset(mask, av_masks, masklength)) + return av_masks; + } av_masks += BITS_TO_LONGS(masklength); } return NULL; @@ -295,7 +301,7 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev, if (indio_dev->available_scan_masks) { mask = iio_scan_mask_match(indio_dev->available_scan_masks, indio_dev->masklength, - trialmask); + trialmask, false); if (!mask) goto err_invalid_mask; } @@ -602,8 +608,10 @@ static int iio_verify_update(struct iio_dev *indio_dev, { unsigned long *compound_mask; const unsigned long *scan_mask; + bool strict_scanmask = false; struct iio_buffer *buffer; bool scan_timestamp; + unsigned int modes; memset(config, 0, sizeof(*config)); @@ -615,12 +623,30 @@ static int iio_verify_update(struct iio_dev *indio_dev, list_is_singular(&indio_dev->buffer_list)) return 0; + modes = indio_dev->modes; + + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) { + if (buffer == remove_buffer) + continue; + modes &= buffer->access->modes; + } + + if (insert_buffer) + modes &= insert_buffer->access->modes; + /* Definitely possible for devices to support both of these. */ - if ((indio_dev->modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) { + if ((modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) { config->mode = INDIO_BUFFER_TRIGGERED; - } else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) { + } else if (modes & INDIO_BUFFER_HARDWARE) { + /* + * Keep things simple for now and only allow a single buffer to + * be connected in hardware mode. + */ + if (insert_buffer && !list_empty(&indio_dev->buffer_list)) + return -EINVAL; config->mode = INDIO_BUFFER_HARDWARE; - } else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) { + strict_scanmask = true; + } else if (modes & INDIO_BUFFER_SOFTWARE) { config->mode = INDIO_BUFFER_SOFTWARE; } else { /* Can only occur on first buffer */ @@ -654,7 +680,8 @@ static int iio_verify_update(struct iio_dev *indio_dev, if (indio_dev->available_scan_masks) { scan_mask = iio_scan_mask_match(indio_dev->available_scan_masks, indio_dev->masklength, - compound_mask); + compound_mask, + strict_scanmask); kfree(compound_mask); if (scan_mask == NULL) return -EINVAL; @@ -888,8 +915,6 @@ static ssize_t iio_buffer_store_enable(struct device *dev, ret = __iio_update_buffers(indio_dev, NULL, indio_dev->buffer); - if (ret < 0) - goto done; done: mutex_unlock(&indio_dev->mlock); return (ret < 0) ? ret : len; @@ -968,6 +993,15 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) int ret, i, attrn, attrcount, attrcount_orig = 0; const struct iio_chan_spec *channels; + channels = indio_dev->channels; + if (channels) { + int ml = indio_dev->masklength; + + for (i = 0; i < indio_dev->num_channels; i++) + ml = max(ml, channels[i].scan_index + 1); + indio_dev->masklength = ml; + } + if (!buffer) return 0; @@ -1011,12 +1045,6 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) if (channels[i].scan_index < 0) continue; - /* Establish necessary mask length */ - if (channels[i].scan_index > - (int)indio_dev->masklength - 1) - indio_dev->masklength - = channels[i].scan_index + 1; - ret = iio_buffer_add_channel_sysfs(indio_dev, &channels[i]); if (ret < 0) diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c index 55c267bbfd2f..c5b999f0c519 100644 --- a/drivers/iio/kfifo_buf.c +++ b/drivers/iio/kfifo_buf.c @@ -136,6 +136,8 @@ static const struct iio_buffer_access_funcs kfifo_access_funcs = { .set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo, .set_length = &iio_set_length_kfifo, .release = &iio_kfifo_buffer_release, + + .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED, }; struct iio_buffer *iio_kfifo_allocate(void) diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index e79b9d89b024..fee4297d7c8f 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -370,7 +370,7 @@ static int stk3310_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { int ret; - unsigned int index; + int index; struct stk3310_data *data = iio_priv(indio_dev); switch (mask) { diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index aa6e25d3bfc3..7a2ea71c659a 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -58,6 +58,31 @@ #define MMC35240_WAIT_CHARGE_PUMP 50000 /* us */ #define MMC53240_WAIT_SET_RESET 1000 /* us */ +/* + * Memsic OTP process code piece is put here for reference: + * + * #define OTP_CONVERT(REG) ((float)((REG) >=32 ? (32 - (REG)) : (REG)) * 0.006 + * 1) For X axis, the COEFFICIENT is always 1. + * 2) For Y axis, the COEFFICIENT is as below: + * f_OTP_matrix[4] = OTP_CONVERT(((reg_data[1] & 0x03) << 4) | + * (reg_data[2] >> 4)) + 1.0; + * 3) For Z axis, the COEFFICIENT is as below: + * f_OTP_matrix[8] = (OTP_CONVERT(reg_data[3] & 0x3f) + 1) * 1.35; + * We implemented the OTP logic into driver. + */ + +/* scale = 1000 here for Y otp */ +#define MMC35240_OTP_CONVERT_Y(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 6) + +/* 0.6 * 1.35 = 0.81, scale 10000 for Z otp */ +#define MMC35240_OTP_CONVERT_Z(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 81) + +#define MMC35240_X_COEFF(x) (x) +#define MMC35240_Y_COEFF(y) (y + 1000) +#define MMC35240_Z_COEFF(z) (z + 13500) + +#define MMC35240_OTP_START_ADDR 0x1B + enum mmc35240_resolution { MMC35240_16_BITS_SLOW = 0, /* 100 Hz */ MMC35240_16_BITS_FAST, /* 200 Hz */ @@ -77,7 +102,7 @@ static const struct { } mmc35240_props_table[] = { /* 16 bits, 100Hz ODR */ { - {1024, 1024, 770}, + {1024, 1024, 1024}, 32768, }, /* 16 bits, 200Hz ODR */ @@ -102,6 +127,10 @@ struct mmc35240_data { struct mutex mutex; struct regmap *regmap; enum mmc35240_resolution res; + + /* OTP compensation */ + int axis_coef[3]; + int axis_scale[3]; }; static const int mmc35240_samp_freq[] = {100, 200, 333, 666}; @@ -113,8 +142,9 @@ static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 333 666"); .modified = 1, \ .channel2 = IIO_MOD_ ## _axis, \ .address = AXIS_ ## _axis, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE), \ } static const struct iio_chan_spec mmc35240_channels[] = { @@ -125,6 +155,7 @@ static const struct iio_chan_spec mmc35240_channels[] = { static struct attribute *mmc35240_attributes[] = { &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL }; static const struct attribute_group mmc35240_attribute_group = { @@ -170,8 +201,9 @@ static int mmc35240_hw_set(struct mmc35240_data *data, bool set) static int mmc35240_init(struct mmc35240_data *data) { - int ret; + int ret, y_convert, z_convert; unsigned int reg_id; + u8 otp_data[6]; ret = regmap_read(data->regmap, MMC35240_REG_ID, ®_id); if (ret < 0) { @@ -195,9 +227,30 @@ static int mmc35240_init(struct mmc35240_data *data) return ret; /* set default sampling frequency */ - return regmap_update_bits(data->regmap, MMC35240_REG_CTRL1, - MMC35240_CTRL1_BW_MASK, - data->res << MMC35240_CTRL1_BW_SHIFT); + ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1, + MMC35240_CTRL1_BW_MASK, + data->res << MMC35240_CTRL1_BW_SHIFT); + if (ret < 0) + return ret; + + ret = regmap_bulk_read(data->regmap, MMC35240_OTP_START_ADDR, + (u8 *)otp_data, sizeof(otp_data)); + if (ret < 0) + return ret; + + y_convert = MMC35240_OTP_CONVERT_Y(((otp_data[1] & 0x03) << 4) | + (otp_data[2] >> 4)); + z_convert = MMC35240_OTP_CONVERT_Z(otp_data[3] & 0x3f); + + data->axis_coef[0] = MMC35240_X_COEFF(1); + data->axis_coef[1] = MMC35240_Y_COEFF(y_convert); + data->axis_coef[2] = MMC35240_Z_COEFF(z_convert); + + data->axis_scale[0] = 1; + data->axis_scale[1] = 1000; + data->axis_scale[2] = 10000; + + return 0; } static int mmc35240_take_measurement(struct mmc35240_data *data) @@ -217,7 +270,8 @@ static int mmc35240_take_measurement(struct mmc35240_data *data) return ret; if (reg_status & MMC35240_STATUS_MEAS_DONE_BIT) break; - msleep(20); + /* minimum wait time to complete measurement is 10 ms */ + usleep_range(10000, 11000); } if (tries < 0) { @@ -240,9 +294,19 @@ static int mmc35240_read_measurement(struct mmc35240_data *data, __le16 buf[3]) 3 * sizeof(__le16)); } -static int mmc35240_raw_to_gauss(struct mmc35240_data *data, int index, - __le16 buf[], - int *val, int *val2) +/** + * mmc35240_raw_to_mgauss - convert raw readings to milli gauss. Also apply + compensation for output value. + * + * @data: device private data + * @index: axis index for which we want the conversion + * @buf: raw data to be converted, 2 bytes in little endian format + * @val: compensated output reading (unit is milli gauss) + * + * Returns: 0 in case of success, -EINVAL when @index is not valid + */ +static int mmc35240_raw_to_mgauss(struct mmc35240_data *data, int index, + __le16 buf[], int *val) { int raw_x, raw_y, raw_z; int sens_x, sens_y, sens_z; @@ -260,22 +324,22 @@ static int mmc35240_raw_to_gauss(struct mmc35240_data *data, int index, switch (index) { case AXIS_X: - *val = (raw_x - nfo) / sens_x; - *val2 = ((raw_x - nfo) % sens_x) * 1000000; + *val = (raw_x - nfo) * 1000 / sens_x; break; case AXIS_Y: - *val = (raw_y - nfo) / sens_y - (raw_z - nfo) / sens_z; - *val2 = (((raw_y - nfo) % sens_y - (raw_z - nfo) % sens_z)) - * 1000000; + *val = (raw_y - nfo) * 1000 / sens_y - + (raw_z - nfo) * 1000 / sens_z; break; case AXIS_Z: - *val = (raw_y - nfo) / sens_y + (raw_z - nfo) / sens_z; - *val2 = (((raw_y - nfo) % sens_y + (raw_z - nfo) % sens_z)) - * 1000000; + *val = (raw_y - nfo) * 1000 / sens_y + + (raw_z - nfo) * 1000 / sens_z; break; default: return -EINVAL; } + /* apply OTP compensation */ + *val = (*val) * data->axis_coef[index] / data->axis_scale[index]; + return 0; } @@ -289,16 +353,19 @@ static int mmc35240_read_raw(struct iio_dev *indio_dev, __le16 buf[3]; switch (mask) { - case IIO_CHAN_INFO_PROCESSED: + case IIO_CHAN_INFO_RAW: mutex_lock(&data->mutex); ret = mmc35240_read_measurement(data, buf); mutex_unlock(&data->mutex); if (ret < 0) return ret; - ret = mmc35240_raw_to_gauss(data, chan->address, - buf, val, val2); + ret = mmc35240_raw_to_mgauss(data, chan->address, buf, val); if (ret < 0) return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 1000; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: mutex_lock(&data->mutex); @@ -308,7 +375,7 @@ static int mmc35240_read_raw(struct iio_dev *indio_dev, return ret; i = (reg & MMC35240_CTRL1_BW_MASK) >> MMC35240_CTRL1_BW_SHIFT; - if (i < 0 || i > ARRAY_SIZE(mmc35240_samp_freq)) + if (i < 0 || i >= ARRAY_SIZE(mmc35240_samp_freq)) return -EINVAL; *val = mmc35240_samp_freq[i]; @@ -490,7 +557,7 @@ static const struct acpi_device_id mmc35240_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, mmc35240_acpi_match); static const struct i2c_device_id mmc35240_id[] = { - {"MMC35240", 0}, + {"mmc35240", 0}, {} }; MODULE_DEVICE_TABLE(i2c, mmc35240_id); diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c index 8589eade1057..23685e74917e 100644 --- a/drivers/staging/iio/accel/sca3000_ring.c +++ b/drivers/staging/iio/accel/sca3000_ring.c @@ -258,6 +258,8 @@ static const struct iio_buffer_access_funcs sca3000_ring_access_funcs = { .read_first_n = &sca3000_read_first_n_hw_rb, .data_available = sca3000_ring_buf_data_available, .release = sca3000_ring_release, + + .modes = INDIO_BUFFER_HARDWARE, }; int sca3000_configure_ring(struct iio_dev *indio_dev) diff --git a/drivers/staging/iio/iio_dummy_evgen.c b/drivers/staging/iio/iio_dummy_evgen.c index 0c9c86d7b509..c54d5b5443a6 100644 --- a/drivers/staging/iio/iio_dummy_evgen.c +++ b/drivers/staging/iio/iio_dummy_evgen.c @@ -128,13 +128,11 @@ EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq); * * Used by client driver instances to give the irqs back when they disconnect */ -int iio_dummy_evgen_release_irq(int irq) +void iio_dummy_evgen_release_irq(int irq) { mutex_lock(&iio_evgen->lock); iio_evgen->inuse[irq - iio_evgen->base] = false; mutex_unlock(&iio_evgen->lock); - - return 0; } EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq); diff --git a/drivers/staging/iio/iio_dummy_evgen.h b/drivers/staging/iio/iio_dummy_evgen.h index 2ac293ab7c8f..d044b946e74a 100644 --- a/drivers/staging/iio/iio_dummy_evgen.h +++ b/drivers/staging/iio/iio_dummy_evgen.h @@ -8,6 +8,6 @@ struct iio_dummy_regs { struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq); int iio_dummy_evgen_get_irq(void); -int iio_dummy_evgen_release_irq(int irq); +void iio_dummy_evgen_release_irq(int irq); #endif /* _IIO_DUMMY_EVGEN_H_ */ diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c index b47bf9fb64be..1629a8a6bf26 100644 --- a/drivers/staging/iio/iio_simple_dummy.c +++ b/drivers/staging/iio/iio_simple_dummy.c @@ -30,7 +30,7 @@ * dummy devices are registered. */ static unsigned instances = 1; -module_param(instances, int, 0); +module_param(instances, uint, 0); /* Pointer array used to fake bus elements */ static struct iio_dev **iio_dummy_devs; @@ -665,9 +665,8 @@ error_ret: * * Parameters follow those of iio_dummy_probe for buses. */ -static int iio_dummy_remove(int index) +static void iio_dummy_remove(int index) { - int ret; /* * Get a pointer to the device instance iio_dev structure * from the bus subsystem. E.g. @@ -685,15 +684,10 @@ static int iio_dummy_remove(int index) /* Buffered capture related cleanup */ iio_simple_dummy_unconfigure_buffer(indio_dev); - ret = iio_simple_dummy_events_unregister(indio_dev); - if (ret) - goto error_ret; + iio_simple_dummy_events_unregister(indio_dev); /* Free all structures */ iio_device_free(indio_dev); - -error_ret: - return ret; } /** @@ -722,9 +716,16 @@ static __init int iio_dummy_init(void) for (i = 0; i < instances; i++) { ret = iio_dummy_probe(i); if (ret < 0) - return ret; + goto error_remove_devs; } return 0; + +error_remove_devs: + while (i--) + iio_dummy_remove(i); + + kfree(iio_dummy_devs); + return ret; } module_init(iio_dummy_init); diff --git a/drivers/staging/iio/iio_simple_dummy.h b/drivers/staging/iio/iio_simple_dummy.h index d86ccb76eb6d..e877a99540ab 100644 --- a/drivers/staging/iio/iio_simple_dummy.h +++ b/drivers/staging/iio/iio_simple_dummy.h @@ -79,7 +79,7 @@ int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev, int val2); int iio_simple_dummy_events_register(struct iio_dev *indio_dev); -int iio_simple_dummy_events_unregister(struct iio_dev *indio_dev); +void iio_simple_dummy_events_unregister(struct iio_dev *indio_dev); #else /* Stubs for when events are disabled at compile time */ @@ -89,11 +89,9 @@ iio_simple_dummy_events_register(struct iio_dev *indio_dev) return 0; }; -static inline int +static inline void iio_simple_dummy_events_unregister(struct iio_dev *indio_dev) -{ - return 0; -}; +{ }; #endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS*/ diff --git a/drivers/staging/iio/iio_simple_dummy_events.c b/drivers/staging/iio/iio_simple_dummy_events.c index c32ef78d8e5f..ecc563cb6cb9 100644 --- a/drivers/staging/iio/iio_simple_dummy_events.c +++ b/drivers/staging/iio/iio_simple_dummy_events.c @@ -257,13 +257,11 @@ error_ret: * iio_simple_dummy_events_unregister() - tidy up interrupt handling on remove * @indio_dev: device instance data */ -int iio_simple_dummy_events_unregister(struct iio_dev *indio_dev) +void iio_simple_dummy_events_unregister(struct iio_dev *indio_dev) { struct iio_dummy_state *st = iio_priv(indio_dev); free_irq(st->event_irq, indio_dev); /* Not part of normal driver */ iio_dummy_evgen_release_irq(st->event_irq); - - return 0; } diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index eb8622b78ec9..1600c55828e0 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -29,6 +29,7 @@ struct iio_buffer; * @set_length: set number of datums in buffer * @release: called when the last reference to the buffer is dropped, * should free all resources allocated by the buffer. + * @modes: Supported operating modes by this buffer type * * The purpose of this structure is to make the buffer element * modular as event for a given driver, different usecases may require @@ -51,6 +52,8 @@ struct iio_buffer_access_funcs { int (*set_length)(struct iio_buffer *buffer, int length); void (*release)(struct iio_buffer *buffer); + + unsigned int modes; }; /** diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index f805493be3eb..4eebb6616e5c 100644 --- a/tools/iio/generic_buffer.c +++ b/tools/iio/generic_buffer.c @@ -59,33 +59,80 @@ int size_from_channelarray(struct iio_channel_info *channels, int num_channels) return bytes; } -void print2byte(int input, struct iio_channel_info *info) +void print2byte(uint16_t input, struct iio_channel_info *info) { /* First swap if incorrect endian */ if (info->be) - input = be16toh((uint16_t)input); + input = be16toh(input); else - input = le16toh((uint16_t)input); + input = le16toh(input); /* * Shift before conversion to avoid sign extension * of left aligned data */ input >>= info->shift; + input &= info->mask; if (info->is_signed) { - int16_t val = input; + int16_t val = (int16_t)(input << (16 - info->bits_used)) >> + (16 - info->bits_used); + printf("%05f ", ((float)val + info->offset) * info->scale); + } else { + printf("%05f ", ((float)input + info->offset) * info->scale); + } +} + +void print4byte(uint32_t input, struct iio_channel_info *info) +{ + /* First swap if incorrect endian */ + if (info->be) + input = be32toh(input); + else + input = le32toh(input); - val &= (1 << info->bits_used) - 1; - val = (int16_t)(val << (16 - info->bits_used)) >> - (16 - info->bits_used); - printf("%05f ", ((float)val + info->offset)*info->scale); + /* + * Shift before conversion to avoid sign extension + * of left aligned data + */ + input >>= info->shift; + input &= info->mask; + if (info->is_signed) { + int32_t val = (int32_t)(input << (32 - info->bits_used)) >> + (32 - info->bits_used); + printf("%05f ", ((float)val + info->offset) * info->scale); } else { - uint16_t val = input; + printf("%05f ", ((float)input + info->offset) * info->scale); + } +} - val &= (1 << info->bits_used) - 1; - printf("%05f ", ((float)val + info->offset)*info->scale); +void print8byte(uint64_t input, struct iio_channel_info *info) +{ + /* First swap if incorrect endian */ + if (info->be) + input = be64toh(input); + else + input = le64toh(input); + + /* + * Shift before conversion to avoid sign extension + * of left aligned data + */ + input >>= info->shift; + input &= info->mask; + if (info->is_signed) { + int64_t val = (int64_t)(input << (64 - info->bits_used)) >> + (64 - info->bits_used); + /* special case for timestamp */ + if (info->scale == 1.0f && info->offset == 0.0f) + printf("%" PRId64 " ", val); + else + printf("%05f ", + ((float)val + info->offset) * info->scale); + } else { + printf("%05f ", ((float)input + info->offset) * info->scale); } } + /** * process_scan() - print out the values in SI units * @data: pointer to the start of the scan @@ -108,32 +155,12 @@ void process_scan(char *data, &channels[k]); break; case 4: - if (!channels[k].is_signed) { - uint32_t val = *(uint32_t *) - (data + channels[k].location); - printf("%05f ", ((float)val + - channels[k].offset)* - channels[k].scale); - - } + print4byte(*(uint32_t *)(data + channels[k].location), + &channels[k]); break; case 8: - if (channels[k].is_signed) { - int64_t val = *(int64_t *) - (data + - channels[k].location); - if ((val >> channels[k].bits_used) & 1) - val = (val & channels[k].mask) | - ~channels[k].mask; - /* special case for timestamp */ - if (channels[k].scale == 1.0f && - channels[k].offset == 0.0f) - printf("%" PRId64 " ", val); - else - printf("%05f ", ((float)val + - channels[k].offset)* - channels[k].scale); - } + print8byte(*(uint64_t *)(data + channels[k].location), + &channels[k]); break; default: break; @@ -141,6 +168,19 @@ void process_scan(char *data, printf("\n"); } +void print_usage(void) +{ + printf("Usage: generic_buffer [options]...\n" + "Capture, convert and output data from IIO device buffer\n" + " -c <n> Do n conversions\n" + " -e Disable wait for event (new data)\n" + " -g Use trigger-less mode\n" + " -l <n> Set buffer length to n samples\n" + " -n <name> Set device name (mandatory)\n" + " -t <name> Set trigger name\n" + " -w <n> Set delay between reads in us (event-less mode)\n"); +} + int main(int argc, char **argv) { unsigned long num_loops = 2; @@ -166,8 +206,26 @@ int main(int argc, char **argv) struct iio_channel_info *channels; - while ((c = getopt(argc, argv, "l:w:c:et:n:g")) != -1) { + while ((c = getopt(argc, argv, "c:egl:n:t:w:")) != -1) { switch (c) { + case 'c': + errno = 0; + num_loops = strtoul(optarg, &dummy, 10); + if (errno) + return -errno; + break; + case 'e': + noevents = 1; + break; + case 'g': + notrigger = 1; + break; + case 'l': + errno = 0; + buf_len = strtoul(optarg, &dummy, 10); + if (errno) + return -errno; + break; case 'n': device_name = optarg; break; @@ -175,39 +233,35 @@ int main(int argc, char **argv) trigger_name = optarg; datardytrigger = 0; break; - case 'e': - noevents = 1; - break; - case 'c': - num_loops = strtoul(optarg, &dummy, 10); - break; case 'w': + errno = 0; timedelay = strtoul(optarg, &dummy, 10); - break; - case 'l': - buf_len = strtoul(optarg, &dummy, 10); - break; - case 'g': - notrigger = 1; + if (errno) + return -errno; break; case '?': + print_usage(); return -1; } } - if (device_name == NULL) + if (device_name == NULL) { + printf("Device name not set\n"); + print_usage(); return -1; + } /* Find the device requested */ dev_num = find_type_by_name(device_name, "iio:device"); if (dev_num < 0) { printf("Failed to find the %s\n", device_name); - ret = -ENODEV; - goto error_ret; + return dev_num; } printf("iio device number being used is %d\n", dev_num); - asprintf(&dev_dir_name, "%siio:device%d", iio_dir, dev_num); + ret = asprintf(&dev_dir_name, "%siio:device%d", iio_dir, dev_num); + if (ret < 0) + return -ENOMEM; if (!notrigger) { if (trigger_name == NULL) { @@ -220,7 +274,7 @@ int main(int argc, char **argv) "%s-dev%d", device_name, dev_num); if (ret < 0) { ret = -ENOMEM; - goto error_ret; + goto error_free_dev_dir_name; } } @@ -228,7 +282,7 @@ int main(int argc, char **argv) trig_num = find_type_by_name(trigger_name, "trigger"); if (trig_num < 0) { printf("Failed to find the trigger %s\n", trigger_name); - ret = -ENODEV; + ret = trig_num; goto error_free_triggername; } printf("iio trigger number being used is %d\n", trig_num); @@ -255,7 +309,7 @@ int main(int argc, char **argv) "%siio:device%d/buffer", iio_dir, dev_num); if (ret < 0) { ret = -ENOMEM; - goto error_free_triggername; + goto error_free_channels; } if (!notrigger) { @@ -296,8 +350,8 @@ int main(int argc, char **argv) /* Attempt to open non blocking the access dev */ fp = open(buffer_access, O_RDONLY | O_NONBLOCK); if (fp == -1) { /* If it isn't there make the node */ - printf("Failed to open %s\n", buffer_access); ret = -errno; + printf("Failed to open %s\n", buffer_access); goto error_free_buffer_access; } @@ -309,7 +363,14 @@ int main(int argc, char **argv) .events = POLLIN, }; - poll(&pfd, 1, -1); + ret = poll(&pfd, 1, -1); + if (ret < 0) { + ret = -errno; + goto error_close_buffer_access; + } else if (ret == 0) { + continue; + } + toread = buf_len; } else { @@ -321,7 +382,7 @@ int main(int argc, char **argv) data, toread*scan_size); if (read_size < 0) { - if (errno == -EAGAIN) { + if (errno == EAGAIN) { printf("nothing available\n"); continue; } else @@ -340,20 +401,31 @@ int main(int argc, char **argv) if (!notrigger) /* Disconnect the trigger - just write a dummy name. */ - write_sysfs_string("trigger/current_trigger", - dev_dir_name, "NULL"); + ret = write_sysfs_string("trigger/current_trigger", + dev_dir_name, "NULL"); + if (ret < 0) + printf("Failed to write to %s\n", dev_dir_name); error_close_buffer_access: - close(fp); -error_free_data: - free(data); + if (close(fp) == -1) + perror("Failed to close buffer"); error_free_buffer_access: free(buffer_access); +error_free_data: + free(data); error_free_buf_dir_name: free(buf_dir_name); +error_free_channels: + for (i = num_channels - 1; i >= 0; i--) { + free(channels[i].name); + free(channels[i].generic_name); + } + free(channels); error_free_triggername: if (datardytrigger) free(trigger_name); -error_ret: +error_free_dev_dir_name: + free(dev_dir_name); + return ret; } diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index 427c271ac0d6..016760e769c0 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -213,23 +213,19 @@ static void print_event(struct iio_event_data *event) return; } - printf("Event: time: %lld, ", event->timestamp); + printf("Event: time: %lld, type: %s", event->timestamp, + iio_chan_type_name_spec[type]); - if (mod != IIO_NO_MOD) { - printf("type: %s(%s), ", - iio_chan_type_name_spec[type], - iio_modifier_names[mod]); - } else { - printf("type: %s, ", - iio_chan_type_name_spec[type]); - } + if (mod != IIO_NO_MOD) + printf("(%s)", iio_modifier_names[mod]); - if (diff && chan >= 0 && chan2 >= 0) - printf("channel: %d-%d, ", chan, chan2); - else if (chan >= 0) - printf("channel: %d, ", chan); + if (chan >= 0) { + printf(", channel: %d", chan); + if (diff && chan2 >= 0) + printf("-%d", chan2); + } - printf("evtype: %s", iio_ev_type_text[ev_type]); + printf(", evtype: %s", iio_ev_type_text[ev_type]); if (dir != IIO_EV_DIR_NONE) printf(", direction: %s", iio_ev_dir_text[dir]); @@ -258,28 +254,34 @@ int main(int argc, char **argv) device_name, dev_num); ret = asprintf(&chrdev_name, "/dev/iio:device%d", dev_num); if (ret < 0) { - ret = -ENOMEM; - goto error_ret; + return -ENOMEM; } } else { /* If we can't find a IIO device by name assume device_name is a IIO chrdev */ chrdev_name = strdup(device_name); + if (!chrdev_name) + return -ENOMEM; } fd = open(chrdev_name, 0); if (fd == -1) { - fprintf(stdout, "Failed to open %s\n", chrdev_name); ret = -errno; + fprintf(stdout, "Failed to open %s\n", chrdev_name); goto error_free_chrdev_name; } ret = ioctl(fd, IIO_GET_EVENT_FD_IOCTL, &event_fd); - - close(fd); - if (ret == -1 || event_fd == -1) { + ret = -errno; fprintf(stdout, "Failed to retrieve event fd\n"); + if (close(fd) == -1) + perror("Failed to close character device file"); + + goto error_free_chrdev_name; + } + + if (close(fd) == -1) { ret = -errno; goto error_free_chrdev_name; } @@ -291,8 +293,8 @@ int main(int argc, char **argv) printf("nothing available\n"); continue; } else { - perror("Failed to read event from device"); ret = -errno; + perror("Failed to read event from device"); break; } } @@ -300,9 +302,11 @@ int main(int argc, char **argv) print_event(&event); } - close(event_fd); + if (close(event_fd) == -1) + perror("Failed to close event file"); + error_free_chrdev_name: free(chrdev_name); -error_ret: + return ret; } diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index 6f6452167b67..ec9ab7f9ae4c 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -29,6 +29,8 @@ static char * const iio_direction[] = { * iioutils_break_up_name() - extract generic name from full channel name * @full_name: the full channel name * @generic_name: the output generic channel name + * + * Returns 0 on success, or a negative error code if string extraction failed. **/ int iioutils_break_up_name(const char *full_name, char **generic_name) @@ -36,7 +38,7 @@ int iioutils_break_up_name(const char *full_name, char *current; char *w, *r; char *working, *prefix = ""; - int i; + int i, ret; for (i = 0; i < sizeof(iio_direction) / sizeof(iio_direction[0]); i++) if (!strncmp(full_name, iio_direction[i], @@ -46,7 +48,14 @@ int iioutils_break_up_name(const char *full_name, } current = strdup(full_name + strlen(prefix) + 1); + if (!current) + return -ENOMEM; + working = strtok(current, "_\0"); + if (!working) { + free(current); + return -EINVAL; + } w = working; r = working; @@ -59,21 +68,25 @@ int iioutils_break_up_name(const char *full_name, r++; } *w = '\0'; - asprintf(generic_name, "%s_%s", prefix, working); + ret = asprintf(generic_name, "%s_%s", prefix, working); free(current); - return 0; + return (ret == -1) ? -ENOMEM : 0; } /** * iioutils_get_type() - find and process _type attribute data * @is_signed: output whether channel is signed * @bytes: output how many bytes the channel storage occupies + * @bits_used: output number of valid bits of data + * @shift: output amount of bits to shift right data before applying bit mask * @mask: output a bit mask for the raw data - * @be: big endian - * @device_dir: the iio device directory + * @be: output if data in big endian + * @device_dir: the IIO device directory * @name: the channel name * @generic_name: the channel type name + * + * Returns a value >= 0 on success, otherwise a negative error code. **/ int iioutils_get_type(unsigned *is_signed, unsigned *bytes, @@ -94,10 +107,9 @@ int iioutils_get_type(unsigned *is_signed, const struct dirent *ent; ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir); - if (ret < 0) { - ret = -ENOMEM; - goto error_ret; - } + if (ret < 0) + return -ENOMEM; + ret = asprintf(&builtname, FORMAT_TYPE_FILE, name); if (ret < 0) { ret = -ENOMEM; @@ -114,6 +126,7 @@ int iioutils_get_type(unsigned *is_signed, ret = -errno; goto error_free_builtname_generic; } + ret = -ENOENT; while (ent = readdir(dp), ent != NULL) /* * Do we allow devices to override a generic name with @@ -129,8 +142,8 @@ int iioutils_get_type(unsigned *is_signed, } sysfsfp = fopen(filename, "r"); if (sysfsfp == NULL) { - printf("failed to open %s\n", filename); ret = -errno; + printf("failed to open %s\n", filename); goto error_free_filename; } @@ -141,8 +154,12 @@ int iioutils_get_type(unsigned *is_signed, bits_used, &padint, shift); if (ret < 0) { - printf("failed to pass scan type description\n"); ret = -errno; + printf("failed to pass scan type description\n"); + goto error_close_sysfsfp; + } else if (ret != 5) { + ret = -EIO; + printf("scan type description didn't match\n"); goto error_close_sysfsfp; } *be = (endianchar == 'b'); @@ -151,34 +168,50 @@ int iioutils_get_type(unsigned *is_signed, *mask = ~0; else *mask = (1 << *bits_used) - 1; - if (signchar == 's') - *is_signed = 1; - else - *is_signed = 0; - fclose(sysfsfp); + *is_signed = (signchar == 's'); + if (fclose(sysfsfp)) { + ret = -errno; + printf("Failed to close %s\n", filename); + goto error_free_filename; + } + + sysfsfp = 0; free(filename); filename = 0; - sysfsfp = 0; } error_close_sysfsfp: if (sysfsfp) - fclose(sysfsfp); + if (fclose(sysfsfp)) + perror("iioutils_get_type(): Failed to close file"); + error_free_filename: if (filename) free(filename); error_closedir: - closedir(dp); + if (closedir(dp) == -1) + perror("iioutils_get_type(): Failed to close directory"); + error_free_builtname_generic: free(builtname_generic); error_free_builtname: free(builtname); error_free_scan_el_dir: free(scan_el_dir); -error_ret: + return ret; } +/** + * iioutils_get_param_float() - read a float value from a channel parameter + * @output: output the float value + * @param_name: the parameter name to read + * @device_dir: the IIO device directory in sysfs + * @name: the channel name + * @generic_name: the channel type name + * + * Returns a value >= 0 on success, otherwise a negative error code. + **/ int iioutils_get_param_float(float *output, const char *param_name, const char *device_dir, @@ -193,10 +226,9 @@ int iioutils_get_param_float(float *output, const struct dirent *ent; ret = asprintf(&builtname, "%s_%s", name, param_name); - if (ret < 0) { - ret = -ENOMEM; - goto error_ret; - } + if (ret < 0) + return -ENOMEM; + ret = asprintf(&builtname_generic, "%s_%s", generic_name, param_name); if (ret < 0) { @@ -208,6 +240,7 @@ int iioutils_get_param_float(float *output, ret = -errno; goto error_free_builtname_generic; } + ret = -ENOENT; while (ent = readdir(dp), ent != NULL) if ((strcmp(builtname, ent->d_name) == 0) || (strcmp(builtname_generic, ent->d_name) == 0)) { @@ -222,25 +255,31 @@ int iioutils_get_param_float(float *output, ret = -errno; goto error_free_filename; } - fscanf(sysfsfp, "%f", output); + errno = 0; + if (fscanf(sysfsfp, "%f", output) != 1) + ret = errno ? -errno : -ENODATA; + break; } error_free_filename: if (filename) free(filename); error_closedir: - closedir(dp); + if (closedir(dp) == -1) + perror("iioutils_get_param_float(): Failed to close directory"); + error_free_builtname_generic: free(builtname_generic); error_free_builtname: free(builtname); -error_ret: + return ret; } /** - * bsort_channel_array_by_index() - reorder so that the array is in index order - * + * bsort_channel_array_by_index() - sort the array in index order + * @ci_array: the iio_channel_info array to be sorted + * @cnt: the amount of array elements **/ void bsort_channel_array_by_index(struct iio_channel_info **ci_array, @@ -262,7 +301,10 @@ void bsort_channel_array_by_index(struct iio_channel_info **ci_array, /** * build_channel_array() - function to figure out what channels are present * @device_dir: the IIO device directory in sysfs - * @ + * @ci_array: output the resulting array of iio_channel_info + * @counter: output the amount of array elements + * + * Returns 0 on success, otherwise a negative error code. **/ int build_channel_array(const char *device_dir, struct iio_channel_info **ci_array, @@ -270,7 +312,7 @@ int build_channel_array(const char *device_dir, { DIR *dp; FILE *sysfsfp; - int count, i; + int count = 0, i; struct iio_channel_info *current; int ret; const struct dirent *ent; @@ -279,10 +321,9 @@ int build_channel_array(const char *device_dir, *counter = 0; ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir); - if (ret < 0) { - ret = -ENOMEM; - goto error_ret; - } + if (ret < 0) + return -ENOMEM; + dp = opendir(scan_el_dir); if (dp == NULL) { ret = -errno; @@ -303,10 +344,24 @@ int build_channel_array(const char *device_dir, free(filename); goto error_close_dir; } - fscanf(sysfsfp, "%i", &ret); + errno = 0; + if (fscanf(sysfsfp, "%i", &ret) != 1) { + ret = errno ? -errno : -ENODATA; + if (fclose(sysfsfp)) + perror("build_channel_array(): Failed to close file"); + + free(filename); + goto error_close_dir; + } + if (ret == 1) (*counter)++; - fclose(sysfsfp); + if (fclose(sysfsfp)) { + ret = -errno; + free(filename); + goto error_close_dir; + } + free(filename); } *ci_array = malloc(sizeof(**ci_array) * (*counter)); @@ -315,7 +370,6 @@ int build_channel_array(const char *device_dir, goto error_close_dir; } seekdir(dp, 0); - count = 0; while (ent = readdir(dp), ent != NULL) { if (strcmp(ent->d_name + strlen(ent->d_name) - strlen("_en"), "_en") == 0) { @@ -332,12 +386,25 @@ int build_channel_array(const char *device_dir, } sysfsfp = fopen(filename, "r"); if (sysfsfp == NULL) { + ret = -errno; free(filename); + count--; + goto error_cleanup_array; + } + errno = 0; + if (fscanf(sysfsfp, "%i", ¤t_enabled) != 1) { + ret = errno ? -errno : -ENODATA; + free(filename); + count--; + goto error_cleanup_array; + } + + if (fclose(sysfsfp)) { ret = -errno; + free(filename); + count--; goto error_cleanup_array; } - fscanf(sysfsfp, "%i", ¤t_enabled); - fclose(sysfsfp); if (!current_enabled) { free(filename); @@ -353,6 +420,7 @@ int build_channel_array(const char *device_dir, if (current->name == NULL) { free(filename); ret = -ENOMEM; + count--; goto error_cleanup_array; } /* Get the generic and specific name elements */ @@ -360,6 +428,8 @@ int build_channel_array(const char *device_dir, ¤t->generic_name); if (ret) { free(filename); + free(current->name); + count--; goto error_cleanup_array; } ret = asprintf(&filename, @@ -372,8 +442,29 @@ int build_channel_array(const char *device_dir, goto error_cleanup_array; } sysfsfp = fopen(filename, "r"); - fscanf(sysfsfp, "%u", ¤t->index); - fclose(sysfsfp); + if (sysfsfp == NULL) { + ret = -errno; + printf("failed to open %s\n", filename); + free(filename); + goto error_cleanup_array; + } + + errno = 0; + if (fscanf(sysfsfp, "%u", ¤t->index) != 1) { + ret = errno ? -errno : -ENODATA; + if (fclose(sysfsfp)) + perror("build_channel_array(): Failed to close file"); + + free(filename); + goto error_cleanup_array; + } + + if (fclose(sysfsfp)) { + ret = -errno; + free(filename); + goto error_cleanup_array; + } + free(filename); /* Find the scale */ ret = iioutils_get_param_float(¤t->scale, @@ -399,38 +490,64 @@ int build_channel_array(const char *device_dir, device_dir, current->name, current->generic_name); + if (ret < 0) + goto error_cleanup_array; } } - closedir(dp); + if (closedir(dp) == -1) { + ret = -errno; + goto error_cleanup_array; + } + + free(scan_el_dir); /* reorder so that the array is in index order */ bsort_channel_array_by_index(ci_array, *counter); return 0; error_cleanup_array: - for (i = count - 1; i >= 0; i--) + for (i = count - 1; i >= 0; i--) { free((*ci_array)[i].name); + free((*ci_array)[i].generic_name); + } free(*ci_array); error_close_dir: - closedir(dp); + if (dp) + if (closedir(dp) == -1) + perror("build_channel_array(): Failed to close dir"); + error_free_name: free(scan_el_dir); -error_ret: + return ret; } +int calc_digits(int num) +{ + int count = 0; + + while (num != 0) { + num /= 10; + count++; + } + + return count; +} + /** * find_type_by_name() - function to match top level types by name * @name: top level type instance name - * @type: the type of top level instance being sort + * @type: the type of top level instance being searched * + * Returns the device number of a matched IIO device on success, otherwise a + * negative error code. * Typical types this is used for are device and trigger. **/ int find_type_by_name(const char *name, const char *type) { const struct dirent *ent; - int number, numstrlen; + int number, numstrlen, ret; FILE *nameFile; DIR *dp; @@ -448,9 +565,19 @@ int find_type_by_name(const char *name, const char *type) strcmp(ent->d_name, "..") != 0 && strlen(ent->d_name) > strlen(type) && strncmp(ent->d_name, type, strlen(type)) == 0) { - numstrlen = sscanf(ent->d_name + strlen(type), - "%d", - &number); + errno = 0; + ret = sscanf(ent->d_name + strlen(type), "%d", &number); + if (ret < 0) { + ret = -errno; + printf("failed to read element number\n"); + goto error_close_dir; + } else if (ret != 1) { + ret = -EIO; + printf("failed to match element number\n"); + goto error_close_dir; + } + + numstrlen = calc_digits(number); /* verify the next character is not a colon */ if (strncmp(ent->d_name + strlen(type) + numstrlen, ":", @@ -460,33 +587,55 @@ int find_type_by_name(const char *name, const char *type) + numstrlen + 6); if (filename == NULL) { - closedir(dp); - return -ENOMEM; + ret = -ENOMEM; + goto error_close_dir; } - sprintf(filename, "%s%s%d/name", - iio_dir, - type, - number); + + ret = sprintf(filename, "%s%s%d/name", iio_dir, + type, number); + if (ret < 0) { + free(filename); + goto error_close_dir; + } + nameFile = fopen(filename, "r"); if (!nameFile) { free(filename); continue; } free(filename); - fscanf(nameFile, "%s", thisname); - fclose(nameFile); + errno = 0; + if (fscanf(nameFile, "%s", thisname) != 1) { + ret = errno ? -errno : -ENODATA; + goto error_close_dir; + } + + if (fclose(nameFile)) { + ret = -errno; + goto error_close_dir; + } + if (strcmp(name, thisname) == 0) { - closedir(dp); + if (closedir(dp) == -1) + return -errno; return number; } } } } - closedir(dp); + if (closedir(dp) == -1) + return -errno; + return -ENODEV; + +error_close_dir: + if (closedir(dp) == -1) + perror("find_type_by_name(): Failed to close directory"); + return ret; } -int _write_sysfs_int(char *filename, char *basedir, int val, int verify) +static int _write_sysfs_int(const char *filename, const char *basedir, int val, + int verify) { int ret = 0; FILE *sysfsfp; @@ -495,24 +644,49 @@ int _write_sysfs_int(char *filename, char *basedir, int val, int verify) if (temp == NULL) return -ENOMEM; - sprintf(temp, "%s/%s", basedir, filename); + ret = sprintf(temp, "%s/%s", basedir, filename); + if (ret < 0) + goto error_free; + sysfsfp = fopen(temp, "w"); if (sysfsfp == NULL) { + ret = -errno; printf("failed to open %s\n", temp); + goto error_free; + } + ret = fprintf(sysfsfp, "%d", val); + if (ret < 0) { + if (fclose(sysfsfp)) + perror("_write_sysfs_int(): Failed to close dir"); + + goto error_free; + } + + if (fclose(sysfsfp)) { ret = -errno; goto error_free; } - fprintf(sysfsfp, "%d", val); - fclose(sysfsfp); + if (verify) { sysfsfp = fopen(temp, "r"); if (sysfsfp == NULL) { + ret = -errno; printf("failed to open %s\n", temp); + goto error_free; + } + if (fscanf(sysfsfp, "%d", &test) != 1) { + ret = errno ? -errno : -ENODATA; + if (fclose(sysfsfp)) + perror("_write_sysfs_int(): Failed to close dir"); + + goto error_free; + } + + if (fclose(sysfsfp)) { ret = -errno; goto error_free; } - fscanf(sysfsfp, "%d", &test); - fclose(sysfsfp); + if (test != val) { printf("Possible failure in int write %d to %s%s\n", val, @@ -526,17 +700,36 @@ error_free: return ret; } -int write_sysfs_int(char *filename, char *basedir, int val) +/** + * write_sysfs_int() - write an integer value to a sysfs file + * @filename: name of the file to write to + * @basedir: the sysfs directory in which the file is to be found + * @val: integer value to write to file + * + * Returns a value >= 0 on success, otherwise a negative error code. + **/ +int write_sysfs_int(const char *filename, const char *basedir, int val) { return _write_sysfs_int(filename, basedir, val, 0); } -int write_sysfs_int_and_verify(char *filename, char *basedir, int val) +/** + * write_sysfs_int_and_verify() - write an integer value to a sysfs file + * and verify + * @filename: name of the file to write to + * @basedir: the sysfs directory in which the file is to be found + * @val: integer value to write to file + * + * Returns a value >= 0 on success, otherwise a negative error code. + **/ +int write_sysfs_int_and_verify(const char *filename, const char *basedir, + int val) { return _write_sysfs_int(filename, basedir, val, 1); } -int _write_sysfs_string(char *filename, char *basedir, char *val, int verify) +static int _write_sysfs_string(const char *filename, const char *basedir, + const char *val, int verify) { int ret = 0; FILE *sysfsfp; @@ -546,24 +739,49 @@ int _write_sysfs_string(char *filename, char *basedir, char *val, int verify) printf("Memory allocation failed\n"); return -ENOMEM; } - sprintf(temp, "%s/%s", basedir, filename); + ret = sprintf(temp, "%s/%s", basedir, filename); + if (ret < 0) + goto error_free; + sysfsfp = fopen(temp, "w"); if (sysfsfp == NULL) { + ret = -errno; printf("Could not open %s\n", temp); + goto error_free; + } + ret = fprintf(sysfsfp, "%s", val); + if (ret < 0) { + if (fclose(sysfsfp)) + perror("_write_sysfs_string(): Failed to close dir"); + + goto error_free; + } + + if (fclose(sysfsfp)) { ret = -errno; goto error_free; } - fprintf(sysfsfp, "%s", val); - fclose(sysfsfp); + if (verify) { sysfsfp = fopen(temp, "r"); if (sysfsfp == NULL) { + ret = -errno; printf("could not open file to verify\n"); + goto error_free; + } + if (fscanf(sysfsfp, "%s", temp) != 1) { + ret = errno ? -errno : -ENODATA; + if (fclose(sysfsfp)) + perror("_write_sysfs_string(): Failed to close dir"); + + goto error_free; + } + + if (fclose(sysfsfp)) { ret = -errno; goto error_free; } - fscanf(sysfsfp, "%s", temp); - fclose(sysfsfp); + if (strcmp(temp, val) != 0) { printf("Possible failure in string write of %s " "Should be %s " @@ -586,18 +804,38 @@ error_free: * @filename: name of file to write to * @basedir: the sysfs directory in which the file is to be found * @val: the string to write + * + * Returns a value >= 0 on success, otherwise a negative error code. **/ -int write_sysfs_string_and_verify(char *filename, char *basedir, char *val) +int write_sysfs_string_and_verify(const char *filename, const char *basedir, + const char *val) { return _write_sysfs_string(filename, basedir, val, 1); } -int write_sysfs_string(char *filename, char *basedir, char *val) +/** + * write_sysfs_string() - write string to a sysfs file + * @filename: name of file to write to + * @basedir: the sysfs directory in which the file is to be found + * @val: the string to write + * + * Returns a value >= 0 on success, otherwise a negative error code. + **/ +int write_sysfs_string(const char *filename, const char *basedir, + const char *val) { return _write_sysfs_string(filename, basedir, val, 0); } -int read_sysfs_posint(char *filename, char *basedir) +/** + * read_sysfs_posint() - read an integer value from file + * @filename: name of file to read from + * @basedir: the sysfs directory in which the file is to be found + * + * Returns the read integer value >= 0 on success, otherwise a negative error + * code. + **/ +int read_sysfs_posint(const char *filename, const char *basedir) { int ret; FILE *sysfsfp; @@ -607,20 +845,41 @@ int read_sysfs_posint(char *filename, char *basedir) printf("Memory allocation failed"); return -ENOMEM; } - sprintf(temp, "%s/%s", basedir, filename); + ret = sprintf(temp, "%s/%s", basedir, filename); + if (ret < 0) + goto error_free; + sysfsfp = fopen(temp, "r"); if (sysfsfp == NULL) { ret = -errno; goto error_free; } - fscanf(sysfsfp, "%d\n", &ret); - fclose(sysfsfp); + errno = 0; + if (fscanf(sysfsfp, "%d\n", &ret) != 1) { + ret = errno ? -errno : -ENODATA; + if (fclose(sysfsfp)) + perror("read_sysfs_posint(): Failed to close dir"); + + goto error_free; + } + + if (fclose(sysfsfp)) + ret = -errno; + error_free: free(temp); return ret; } -int read_sysfs_float(char *filename, char *basedir, float *val) +/** + * read_sysfs_float() - read a float value from file + * @filename: name of file to read from + * @basedir: the sysfs directory in which the file is to be found + * @val: output the read float value + * + * Returns a value >= 0 on success, otherwise a negative error code. + **/ +int read_sysfs_float(const char *filename, const char *basedir, float *val) { int ret = 0; FILE *sysfsfp; @@ -630,19 +889,40 @@ int read_sysfs_float(char *filename, char *basedir, float *val) printf("Memory allocation failed"); return -ENOMEM; } - sprintf(temp, "%s/%s", basedir, filename); + ret = sprintf(temp, "%s/%s", basedir, filename); + if (ret < 0) + goto error_free; + sysfsfp = fopen(temp, "r"); if (sysfsfp == NULL) { ret = -errno; goto error_free; } - fscanf(sysfsfp, "%f\n", val); - fclose(sysfsfp); + errno = 0; + if (fscanf(sysfsfp, "%f\n", val) != 1) { + ret = errno ? -errno : -ENODATA; + if (fclose(sysfsfp)) + perror("read_sysfs_float(): Failed to close dir"); + + goto error_free; + } + + if (fclose(sysfsfp)) + ret = -errno; + error_free: free(temp); return ret; } +/** + * read_sysfs_string() - read a string from file + * @filename: name of file to read from + * @basedir: the sysfs directory in which the file is to be found + * @str: output the read string + * + * Returns a value >= 0 on success, otherwise a negative error code. + **/ int read_sysfs_string(const char *filename, const char *basedir, char *str) { int ret = 0; @@ -653,14 +933,27 @@ int read_sysfs_string(const char *filename, const char *basedir, char *str) printf("Memory allocation failed"); return -ENOMEM; } - sprintf(temp, "%s/%s", basedir, filename); + ret = sprintf(temp, "%s/%s", basedir, filename); + if (ret < 0) + goto error_free; + sysfsfp = fopen(temp, "r"); if (sysfsfp == NULL) { ret = -errno; goto error_free; } - fscanf(sysfsfp, "%s\n", str); - fclose(sysfsfp); + errno = 0; + if (fscanf(sysfsfp, "%s\n", str) != 1) { + ret = errno ? -errno : -ENODATA; + if (fclose(sysfsfp)) + perror("read_sysfs_string(): Failed to close dir"); + + goto error_free; + } + + if (fclose(sysfsfp)) + ret = -errno; + error_free: free(temp); return ret; diff --git a/tools/iio/iio_utils.h b/tools/iio/iio_utils.h index 1bc837b2d769..379eed9deaea 100644 --- a/tools/iio/iio_utils.h +++ b/tools/iio/iio_utils.h @@ -28,9 +28,12 @@ extern const char *iio_dir; * @offset: offset to be applied for conversion to si units * @index: the channel index in the buffer output * @bytes: number of bytes occupied in buffer output + * @bits_used: number of valid bits of data + * @shift: amount of bits to shift right data before applying bit mask * @mask: a bit mask for the raw output + * @be: flag if data is big endian * @is_signed: is the raw value stored signed - * @enabled: is this channel enabled + * @location: data offset for this channel inside the buffer (in bytes) **/ struct iio_channel_info { char *name; @@ -60,12 +63,15 @@ void bsort_channel_array_by_index(struct iio_channel_info **ci_array, int cnt); int build_channel_array(const char *device_dir, struct iio_channel_info **ci_array, int *counter); int find_type_by_name(const char *name, const char *type); -int write_sysfs_int(char *filename, char *basedir, int val); -int write_sysfs_int_and_verify(char *filename, char *basedir, int val); -int write_sysfs_string_and_verify(char *filename, char *basedir, char *val); -int write_sysfs_string(char *filename, char *basedir, char *val); -int read_sysfs_posint(char *filename, char *basedir); -int read_sysfs_float(char *filename, char *basedir, float *val); +int write_sysfs_int(const char *filename, const char *basedir, int val); +int write_sysfs_int_and_verify(const char *filename, const char *basedir, + int val); +int write_sysfs_string_and_verify(const char *filename, const char *basedir, + const char *val); +int write_sysfs_string(const char *filename, const char *basedir, + const char *val); +int read_sysfs_posint(const char *filename, const char *basedir); +int read_sysfs_float(const char *filename, const char *basedir, float *val); int read_sysfs_string(const char *filename, const char *basedir, char *str); #endif /* _IIO_UTILS_H_ */ diff --git a/tools/iio/lsiio.c b/tools/iio/lsiio.c index c585440f864e..b59ee1733924 100644 --- a/tools/iio/lsiio.c +++ b/tools/iio/lsiio.c @@ -56,7 +56,7 @@ static int dump_channels(const char *dev_dir_name) printf(" %-10s\n", ent->d_name); } - return 0; + return (closedir(dp) == -1) ? -errno : 0; } static int dump_one_device(const char *dev_dir_name) @@ -69,7 +69,10 @@ static int dump_one_device(const char *dev_dir_name) "%i", &dev_idx); if (retval != 1) return -EINVAL; - read_sysfs_string("name", dev_dir_name, name); + retval = read_sysfs_string("name", dev_dir_name, name); + if (retval) + return retval; + printf("Device %03d: %s\n", dev_idx, name); if (verblevel >= VERBLEVEL_SENSORS) @@ -87,28 +90,42 @@ static int dump_one_trigger(const char *dev_dir_name) "%i", &dev_idx); if (retval != 1) return -EINVAL; - read_sysfs_string("name", dev_dir_name, name); + retval = read_sysfs_string("name", dev_dir_name, name); + if (retval) + return retval; + printf("Trigger %03d: %s\n", dev_idx, name); return 0; } -static void dump_devices(void) +static int dump_devices(void) { const struct dirent *ent; + int ret; DIR *dp; dp = opendir(iio_dir); if (dp == NULL) { printf("No industrial I/O devices available\n"); - return; + return -ENODEV; } while (ent = readdir(dp), ent != NULL) { if (check_prefix(ent->d_name, type_device)) { char *dev_dir_name; - asprintf(&dev_dir_name, "%s%s", iio_dir, ent->d_name); - dump_one_device(dev_dir_name); + if (asprintf(&dev_dir_name, "%s%s", iio_dir, + ent->d_name) < 0) { + ret = -ENOMEM; + goto error_close_dir; + } + + ret = dump_one_device(dev_dir_name); + if (ret) { + free(dev_dir_name); + goto error_close_dir; + } + free(dev_dir_name); if (verblevel >= VERBLEVEL_SENSORS) printf("\n"); @@ -119,19 +136,35 @@ static void dump_devices(void) if (check_prefix(ent->d_name, type_trigger)) { char *dev_dir_name; - asprintf(&dev_dir_name, "%s%s", iio_dir, ent->d_name); - dump_one_trigger(dev_dir_name); + if (asprintf(&dev_dir_name, "%s%s", iio_dir, + ent->d_name) < 0) { + ret = -ENOMEM; + goto error_close_dir; + } + + ret = dump_one_trigger(dev_dir_name); + if (ret) { + free(dev_dir_name); + goto error_close_dir; + } + free(dev_dir_name); } } - closedir(dp); + return (closedir(dp) == -1) ? -errno : 0; + +error_close_dir: + if (closedir(dp) == -1) + perror("dump_devices(): Failed to close directory"); + + return ret; } int main(int argc, char **argv) { int c, err = 0; - while ((c = getopt(argc, argv, "d:D:v")) != EOF) { + while ((c = getopt(argc, argv, "v")) != EOF) { switch (c) { case 'v': verblevel++; @@ -146,13 +179,9 @@ int main(int argc, char **argv) if (err || argc > optind) { fprintf(stderr, "Usage: lsiio [options]...\n" "List industrial I/O devices\n" - " -v, --verbose\n" - " Increase verbosity (may be given multiple times)\n" - ); + " -v Increase verbosity (may be given multiple times)\n"); exit(1); } - dump_devices(); - - return 0; + return dump_devices(); } |