Re: [PATCH 1/2] iio: chemical: sgpxx: Support Sensirion SGPxx sensors

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, 10 Mar 2018 23:02:17 +0100
Andreas Brauchli <a.brauchli@xxxxxxxxxxxxxxx> wrote:

> Dear Peter, Jonathan,
> 
> Thanks for the thourough and speedy review and apologies for the delayed reply.
> 
> Many of your comments are integrated in the v2 patch series - details below.
> 
...
> > >   
> > > > +
> > > > +2.2. Indoor Air Quality (IAQ) concentrations
> > > > +
> > > > +* tVOC (in_concentration_voc_input) at ppb precision (1e-9)
> > > > +* CO2eq (in_concentration_co2_input) at ppm precision (1e-6) -- SGP30 only
> > > > +
> > > > +2.2.1. IAQ Initialization
> > > > +Before Indoor Air Quality (IAQ) values can be read, the IAQ mode must be
> > > > +initialized by writing a non-empty value to out_iaq_init:
> > > > +
> > > > +    $ echo init > out_iaq_init    
> > > 
> > > can't this be done on probe()?  
> 
> Yes, v2 now uses this approach and removes iaq_init entirely.
> A semi-replacement is provided in the form of a set_iaq_preheat_seconds
> interface that sets the pre-heat duration, on the low-power SGPC3. All uses of
> iaq_init are now purely internal and called as needed E.g. running the selftest
> can mess up the state on the SGP30. So we're also re-initializing after a
> selftest and after set_baseline.
> 
> > It very much needs to be - userspace code for this sort of sensor
> > should be generic and hence not need to know about how to initialize
> > your particular sensor.  It will assume if the driver is there, the
> > device is on - or will be dynamically enabled when it wants to talk
> > to it.  
> > > 
> > > in any case, private API should be documented under
> > > Documentation/ABI/testing/sysfs-bus-iio-*  
> 
> amended in v2, please check if the format is as expected.
> 
> > >   
> > > > +After initializing IAQ, at least one IAQ signal must be read out every second
> > > > +(SGP30) / every two seconds (SGPC3) for the sensor to correctly maintain its
> > > > +internal baseline:    
> > > 
> > > shouldn't the driver do this?  
> > 
> > Absolutely!
> > 
> > As I understand it, the requirement is also to prevent the hardware being read
> > more often than this so it needs to be under control of the driver.  
> 
> fixed with a kernel thread in v2 - the IAQ thread is now the only accessor of
> the chip's iaq_{init,measure,set_baseline} functions. A separate dedicated
> IAQ-values buffer is used for caching.
> 
> > >   
> > > > +
> > > > +    SGP30:
> > > > +    $ watch -n1 cat in_concentration_voc_input
> > > > +
> > > > +    SGPC3:
> > > > +    $ watch -n2 cat in_concentration_voc_input
> > > > +
> > > > +For the first 15s of operation after writing to out_iaq_init, default values are
> > > > +retured by the sensor.    
> > 
> > In which case it should return -EBUSY from the read function and not garbage
> > data (userspace in general has no way of knowing these chip specific
> > things).  
> 
> fixed in v2. For the low-power/pulsed SGPC3, the time is dependent on the
> pre-heating duration and whether the initialization is performed with a restored
> baseline. This is reflected in the code (sgpc3_iaq_init). A query of default
> values now results in -EBUSY.
> 
> Consequently, in_iaq_baseline now also returns -EBUSY instead of "0000" which is
> considered invalid.
> 
> > > typo: returned  
> 
> fixed in v2
> 
> > > > +
> > > > +2.2.2. Pausing and Resuming IAQ
> > > > +
> > > > +For best performance and faster startup times, the baseline should be saved
> > > > +once every hour, after 12h of operation. The baseline is restored by writing a
> > > > +non-empty value to out_iaq_init, followed by writing an unmodified retrieved
> > > > +baseline value from in_iaq_baseline to out_iaq_baseline.    
> > > 
> > > the out_ prefix seems inappropriate here, the sensors doesn't output CO2 :)  
> 
> renamed to set_iaq_baseline in v2. Should in_iaq_baseline also be renamed to
> get_iaq_baseline?
Nope, just iaq_baseline for both.

> 
> > > 
> > > handling calibration data in a generic way is difficult  
> > 
> > We can do better than this though.  Init is automatic - if you are updating the
> > baseline and it needs to call it again, then do that in the write of the baseline
> > not a separate init write.  
> 
> Yes, in v2, iaq_init is now always performed automatically by the driver.
> 
> > > > +
> > > > +    Saving the baseline:
> > > > +    $ baseline=$(cat in_iaq_baseline)
> > > > +
> > > > +    Restoring the baseline:
> > > > +    $ echo init > out_iaq_init
> > > > +    $ echo -n $baseline > out_iaq_baseline
> > > > +
> > > > +2.3. Gas Concentration Signals
> > > > +
> > > > +* Ethanol (in_concentration_ethanol_raw)    
> > > 
> > > we'd need a IIO_MOD_ETHANOL?  
> > 
> > Cool a new sensor type ;)  
> 
> Added in v2 - On a side note, I can imagine quite a few more gases to be added
> down the road.. maybe half a dozen if you don't ask a chemist.
> 
Sure, it's fine to add them as they come up.
 
> > These are really calibration values being input to the device.
> > The ABI needs to reflect that rather than putting them through as output channels.
> >   
> > > 
> > > absolute humidity is new, IIO has relative humidity so far (jus saying)  
> 
> Absolute humidity is in fact a concentration but I see the added value in
> knowing what specific concentration it is. I didn't add the IIO_HUMIDITYABSOLUTE
> channel to the type list since it's not used as channel and would therefore
> remain unused.
> 
> > > relative humidity is in milli percent in IIO
> > > temperature is in milli degree Celsius
> > >   
> > > > +converting the relative humidity and temperature. The following units are used:
> > > > +AH in mg/m^3, RH in percent (0..100), T in degrees Celsius, and exp() being the
> > > > +base-e exponential function.
> > > > +
> > > > +                  RH            exp(17.62 * T)
> > > > +                ----- * 6.112 * --------------
> > > > +                100.0            243.12 + T
> > > > +  AH = 216.7 * ------------------------------- * 1000
> > > > +                        273.15 + T
> > > > +
> > > > +Writing a value of 0 to out_absolute_humidity disables the humidity    
> > > 
> > > out_absolute_humidity is the same as out_concentration_ah_raw?  
> 
> Yes, my bad, out_absolute_humidity does not exist, in v2 it's replaced with
> set_absolute_humidity.
I don't like that one much either I'm afraid, we need to be clear this is
a control related to calibration/compensation rather than anything else.

> 
> > >   
> > > > +compensation.
> > > > +
> > > > +2.5. On-chip self test
> > > > +
> > > > +    $ cat in_selftest
> > > > +
> > > > +in_selftest returns OK or FAILED.
> > > > +
> > > > +The self test interferes with IAQ operations. If needed, first save the current
> > > > +baseline, then restore it after the self test:
> > > > +
> > > > +    $ baseline=$(cat in_iaq_baseline)
> > > > +    $ cat in_selftest
> > > > +    $ echo init > out_iaq_init
> > > > +    $ echo -n $baseline > out_iaq_baseline
> > > > +
> > > > +If the sensor's current operating duration is less than 12h the baseline should
> > > > +not be restored by skipping the last step.  
> > 
> > Talk me through the usecase - is power up often enough or are there reasons
> > to do it more often?  
> 
> The self test is more of a convenience than a necessity. It can be useful to
> confirm a broken device (e.g. physically broken hotplate) if the signal remains
> constantly high. I'd expect user space to perform it since they'll have to
> report the failure. Some failures are also non-fatal.
Is there any way of telling if it is fatal?
If not it isn't all that much use...
> 
> > > > +
> > > > +3. Sensor Interface
> > > > +
> > > > +    $ cat in_feature_set_version
> > > > +
> > > > +The SGP sensors' minor interface (feature set) version guarantees interface
> > > > +stability: a sensor with feature set 1.1 works with a driver for feature set 1.0    
> > > 
> > > really needed?  
> > 
> > Shouldn't be exposed to userspace.  Worth checking in kernel perhaps and refusing
> > to probe if we don't support the newer hardware.  
> 
> The driver already refuses to probe newer major version interfaces.
> 
> Different feature sets have different features, new in v2 is support for the
> SGPC3 FS0.6 with an ultra low power mode and humidity compensation. Trying to
> set either will result in an -EINVAL when unsupported, so user-space code might
> be interested in it.
>
Don't provide the userspace interface if it doesn't do anything.
 
...
> > > > +static int sgp_write_raw(struct iio_dev *indio_dev,
> > > > +			 struct iio_chan_spec const *chan,
> > > > +			 int val, int val2, long mask)
> > > > +{
> > > > +	struct sgp_data *data = iio_priv(indio_dev);
> > > > +	int ret;
> > > > +
> > > > +	switch (chan->address) {
> > > > +	case SGP30_SET_AH_IDX:
> > > > +		ret = sgp_absolute_humidity_store(data, val, val2);  
> > 
> > This is fun - making the world wet ;)
> > Joking aside, we need to handle this differently as it
> > isn't an output device but rather a calibration parameter.  
> 
> Consider it my involuntary contribution against said reviewer grumpyness :D
> 
> Refactored from channel to attribute set_absolute_humidity in v2. Since it's not
> a channel I didn't add IIO_HUMIDITYABSOLUTE to the types, but if it were, the
> name would likely be in_humidityabsolute_raw - should the attribute also reflect
> this scheme (set_humidityabsolute)?
This needs some more thought.  It's really a calibration parameter so we
should indicate it in some way but I'm not totally sure how to do so cleanly.
I don't like the idea of set_* though as if we can write to a value then
it should be clear we are setting it.

Hopefully we'll get some other people's input on this question.

> 
> > 
> >    
...
> > > > +
> > > > +	return sprintf(buf, "%s\n",
> > > > +		       measure_test ^ SGP_SELFTEST_OK ? "FAILED" : "OK");
> > > > +  
> > 
> > How would userspace software know to do a self test?
> > Usual convention is to do it on module load and report the failure in the
> > kernel logs if there is one.  
> 
> Not successfully probing might be less desirable since userspace would have to
> check the logs to find out that the sensor is detected as broken, also the
> self-test is quite complete and an unsucessful self-test doesn't have to be a
> completely broken/useless sensor.
Hmm.  I'm still a little unconvinced but let's ignore that one for now as
bigger things to deal with.

Generic usespace is simply never going to run it however...

> 
> >   
> > > > +unlock_fail:
> > > > +	mutex_unlock(&data->data_lock);
> > > > +	return ret;
> > > > +}
> > > > +
> > > > +static ssize_t sgp_serial_id_show(struct device *dev,
> > > > +				  struct device_attribute *attr, char *buf)
> > > > +{
> > > > +	struct sgp_data *data = iio_priv(dev_to_iio_dev(dev));
> > > > +
> > > > +	return sprintf(buf, "%llu\n", data->serial_id);  
> > 
> > Not normally of much use except in perhaps debugging so we normally
> > only expose these as a kernel log message rather than cluttering the
> > sysfs abi.  
> 
> Let me know if the following use case is not good enough to keep it:
> The baseline is per-sensor and the sensor id is a good way of couple both
> together in case the system has multiple SGPs attached and i2c bus ids aren't
> stable or the sensors are reattached on a different machine/bus.
> 
Hmm. OK, I think that is a good enough reason...

...

> > > > +static IIO_DEVICE_ATTR(in_serial_id, 0444, sgp_serial_id_show, NULL, 0);
> > > > +static IIO_DEVICE_ATTR(in_feature_set_version, 0444,
> > > > +		       sgp_feature_set_version_show, NULL, 0);
> > > > +static IIO_DEVICE_ATTR(in_selftest, 0444, sgp_selftest_show, NULL, 0);
> > > > +static IIO_DEVICE_ATTR(out_iaq_init, 0220, NULL, sgp_iaq_init_store, 0);
> > > > +static IIO_DEVICE_ATTR(in_iaq_baseline, 0444, sgp_iaq_baseline_show, NULL, 0);
> > > > +static IIO_DEVICE_ATTR(out_iaq_baseline, 0220, NULL, sgp_iaq_baseline_store, 0);    
> > > 
> > > lot of private ABI; all needed?
> > > needs documentation...  
> 
> Better in v2?
Somewhat but still needs discussion.

> 
> > >   
> > > > +static struct attribute *sgp_attributes[] = {
> > > > +	&iio_dev_attr_in_serial_id.dev_attr.attr,
> > > > +	&iio_dev_attr_in_feature_set_version.dev_attr.attr,
> > > > +	&iio_dev_attr_in_selftest.dev_attr.attr,
> > > > +	&iio_dev_attr_out_iaq_init.dev_attr.attr,
> > > > +	&iio_dev_attr_in_iaq_baseline.dev_attr.attr,
> > > > +	&iio_dev_attr_out_iaq_baseline.dev_attr.attr,
> > > > +	NULL
> > > > +};
> > > > +
> > > > +static const struct attribute_group sgp_attr_group = {
> > > > +	.attrs = sgp_attributes,
> > > > +};
> > > > +
> > > > +static const struct iio_info sgp_info = {
> > > > +	.attrs		= &sgp_attr_group,
> > > > +	.read_raw	= sgp_read_raw,
> > > > +	.write_raw	= sgp_write_raw,
> > > > +};
> > > > +
> > > > +static const struct of_device_id sgp_dt_ids[] = {
> > > > +	{ .compatible = "sensirion,sgp30", .data = (void *)SGP30 },
> > > > +	{ .compatible = "sensirion,sgpc3", .data = (void *)SGPC3 },
> > > > +	{ }
> > > > +};
> > > > +
> > > > +static int sgp_probe(struct i2c_client *client,
> > > > +		     const struct i2c_device_id *id)
> > > > +{
> > > > +	struct iio_dev *indio_dev;
> > > > +	struct sgp_data *data;
> > > > +	struct sgp_device *chip;  
> > 
> > Not convinced this local variable adds anything to readability.  
> 
> Do you have a suggestion? I'm not sure if it's the name or the fact that's it's
> there at all.
There at all - just use the array directly in all cases.
 
> >   
> > > > +	const struct of_device_id *of_id;
> > > > +	unsigned long chip_id;
> > > > +	int ret;
> > > > +
> > > > +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> > > > +	if (!indio_dev)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	of_id = of_match_device(sgp_dt_ids, &client->dev);
> > > > +	if (!of_id)  
> > 
> > Seems a touch backward
> > if (of_id)
> > 	chip_id = (unsigned long)of_id->data;
> > else
> > 	chip_id = id->driver_data;  
> 
> fixed in v2
> 
> >   
> > > > +		chip_id = id->driver_data;
> > > > +	else
> > > > +		chip_id = (unsigned long)of_id->data;
> > > > +
> > > > +	chip = &sgp_devices[chip_id];
> > > > +	data = iio_priv(indio_dev);
> > > > +	i2c_set_clientdata(client, indio_dev);
> > > > +	data->client = client;
> > > > +	crc8_populate_msb(sgp_crc8_table, SGP_CRC8_POLYNOMIAL);
> > > > +	mutex_init(&data->data_lock);
> > > > +	mutex_init(&data->i2c_lock);
> > > > +
> > > > +	/* get serial id and write it to client data */
> > > > +	ret = sgp_get_serial_id(data);  
> > 
...
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux