On Mon, Mar 06, 2023 at 11:17:15AM +0200, Matti Vaittinen wrote:
Some light sensors can adjust both the HW-gain and integration time.
There are cases where adjusting the integration time has similar impact
to the scale of the reported values as gain setting has.
IIO users do typically expect to handle scale by a single writable 'scale'
entry. Driver should then adjust the gain/time accordingly.
It however is difficult for a driver to know whether it should change
gain or integration time to meet the requested scale. Usually it is
preferred to have longer integration time which usually improves
accuracy, but there may be use-cases where long measurement times can be
an issue. Thus it can be preferable to allow also changing the
integration time - but mitigate the scale impact by also changing the gain
underneath. Eg, if integration time change doubles the measured values,
the driver can reduce the HW-gain to half.
The theory of the computations of gain-time-scale is simple. However,
some people (undersigned) got that implemented wrong for more than once.
Add some gain-time-scale helpers in order to not dublicate errors in all
drivers needing these computations.
...
+/*
If it's deliberately not a kernel doc, why to bother to have it looking as one?
It's really a provocative to some people who will come with a patches to "fix"
this...
+ * iio_gts_get_gain - Convert scale to total gain
+ *
+ * Internal helper for converting scale to total gain.
+ *
+ * @max: Maximum linearized scale. As an example, when scale is created
+ * in magnitude of NANOs and max scale is 64.1 - The linearized
+ * scale is 64 100 000 000.
+ * @scale: Linearized scale to compte the gain for.
+ *
+ * Return: (floored) gain corresponding to the scale. -EINVAL if scale
+ * is invalid.
+ */
+static int iio_gts_get_gain(const u64 max, const u64 scale)
+{
+ int tmp = 1;
+
+ if (scale > max || !scale)
+ return -EINVAL;
+
+ if (U64_MAX - max < scale) {
+ /* Risk of overflow */
+ if (max - scale < scale)
+ return 1;
+ while (max - scale > scale * (u64)tmp)
+ tmp++;
+
+ return tmp + 1;
Can you wait for the comments to appear a bit longer, please?
I have answered to your query in the previous discussion.
+ }
+
+ while (max > scale * (u64) tmp)
No space for castings?
+ tmp++;
+
+ return tmp;
+}
...
+ /*
+ * Expect scale to be (mostly) NANO or MICRO. Divide divider instead of
+ * multiplication followed by division to avoid overflow
Missing period.
+ */
+ if (scaler > NANO || !scaler)
+ return -EINVAL;
Shouldn't be OVERFLOW for the first one?
...
+ *lin_scale = (u64) scale_whole * (u64)scaler +
No space for casting?
...
+ ret = iio_gts_linearize(max_scale_int, max_scale_nano, NANO,
+ >s->max_scale);
+ if (ret)
+ return ret;
+
+ gts->hwgain_table = gain_tbl;
+ gts->num_hwgain = num_gain;
+ gts->itime_table = tim_tbl;
+ gts->num_itime = num_times;
+ gts->per_time_avail_scale_tables = NULL;
+ gts->avail_time_tables = NULL;
+ gts->avail_all_scales_table = NULL;
+ gts->num_avail_all_scales = 0;
Just wondering why we can't simply
memset(0)
beforehand and drop all these 0 assignments?
...
+ /*
+ * Sort the tables for nice output and for easier finding of
+ * unique values
Missing period. Please, check the style of multi-line comments. I believe it's
even mentioned in the documentation.
+ */
...
+ sort(gains[i], gts->num_hwgain, sizeof(int), iio_gts_gain_cmp,
+ NULL);
One line reads better?
...
+ if (ret && gts->avail_all_scales_table)
In one case you commented that free(NULL) is okay, in the other, you add
a duplicative check. Why?
+ kfree(gts->avail_all_scales_table);
...
+ per_time_gains = kcalloc(gts->num_itime, sizeof(int *), GFP_KERNEL);
sizeof(type) is error prone in comparison to sizeof(*var).
+ if (!per_time_gains)
+ return ret;
+
+ per_time_scales = kcalloc(gts->num_itime, sizeof(int *), GFP_KERNEL);
Ditto.
+ if (!per_time_scales)
+ goto free_gains;
...
+err_free_out:
+ while (i) {
+ /*
+ * It does not matter if i'th alloc was not succesfull as
+ * kfree(NULL) is safe.
+ */
Instead, can be just a free of the known allocated i:th member first followed
by traditional pattern. In that case comment will become redundant.
+ kfree(per_time_scales[i]);
+ kfree(per_time_gains[i]);
+
+ i--;
+ }
...
+ for (i = gts->num_itime - 1; i >= 0; i--) {
while (i--) {
makes it easier to parse.
No. I think I have never run the kernel doc - probably time to do so :) Thanks.
+/**
+ * iio_gts_all_avail_scales - helper for listing all available scales
+ * @gts: Gain time scale descriptor
+ * @vals: Returned array of supported scales
+ * @type: Type of returned scale values
+ * @length: Amount of returned values in array
+ *
+ * Returns a value suitable to be returned from read_avail or a negative error
Missing a return section. Have you run kernel doc to validate this?
Missing period.
Seems these problems occur in many function descriptions.
+ */
...
+ /*
+ * Using this function prior building the tables is a driver-error
+ * which should be fixed when the driver is tested for a first time
Missing period.
+ */
+ if (WARN_ON(!gts->num_avail_all_scales))
Does this justify panic? Note, that any WARN() can become an Oops followed by
panic and reboot.
+ return -EINVAL;
...
+ for (i = 0; i < gts->num_hwgain; i++) {
+ /*
+ * It is not expected this function is called for an exactly
+ * matching gain.
+ */
+ if (unlikely(gain == gts->hwgain_table[i].gain)) {
+ *in_range = true;
+ return gain;
+ }
+ if (!min)
+ min = gts->hwgain_table[i].gain;
+ else
+ min = min(min, gts->hwgain_table[i].gain);
I was staring at this and have got no clue why it's not a dead code.
+ if (gain > gts->hwgain_table[i].gain) {
+ if (!diff) {
+ diff = gain - gts->hwgain_table[i].gain;
+ best = i;
+ } else {
+ int tmp = gain - gts->hwgain_table[i].gain;
+
+ if (tmp < diff) {
+ diff = tmp;
+ best = i;
+ }
+ }
int tmp = gain - gts->hwgain_table[i].gain;
if (!diff || tmp < diff) {
diff = tmp;
best = i;
}
?
Or did you miss using 'min'? (And I'm wondering how variable name and min()
macro are not conflicting with each other.
+ } else {
+ /*
+ * We found valid hwgain which is greater than
+ * reference. So, unless we return a failure below we
+ * will have found an in-range gain
+ */
+ *in_range = true;
+ }
+ }
+ /* The requested gain was smaller than anything we support */
+ if (!diff) {
+ *in_range = false;
+
+ return -EINVAL;
+ }
+
+ return gts->hwgain_table[best].gain;
...
+ ret = iio_gts_get_scale_linear(gts, old_gain, itime_old->time_us,
+ &scale);
Still can be one line.
+ if (ret)
+ return ret;
+
+ ret = gain_get_scale_fraction(gts->max_scale, scale, itime_new->mul,
+ new_gain);
Ditto.
+ if (ret)
+ return -EINVAL;
...
+++ b/drivers/iio/light/iio-gts-helper.h
Is it _only_ for a Light type of sensors?
...
+#ifndef __IIO_GTS_HELPER__
+#define __IIO_GTS_HELPER__
If yes, perhaps adding LIGHT here?