Skip to content
This repository has been archived by the owner on Jun 27, 2019. It is now read-only.

flow:iio: Add Proximity sensor Category support into iio node #1986

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions src/modules/flow/iio/iio.json
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,95 @@
}
],
"private_data_type": "light_data"
},
{
"category": "input/hw",
"description": "IIO based proximity input node. As any IIO device, it can use a buffer to get the readings. To use a buffer, define a size > 0 on buffer size. Readings are sent to the buffer via a trigger mechanism. Set iio_trigger_name to a valid iio trigger name in order to use buffer. If buffer is enabled but no iio_trigger_name was set, it will attempt to create a default trigger that will be activated when sending packets to TICK port.",
"in_ports": [
{
"data_type": "any",
"description": "Packets sent in here will trigger a proximity reading and produce packets on each of the the output ports. If buffer is enabled, current trigger must accept manual activation (default one, sysfs trigger, does).",
"name": "TICK",
"methods": {
"process": "proximity_tick"
}
}
],
"methods": {
"close": "proximity_close",
"open": "proximity_open"
},
"name": "iio/proximity",
"options": {
"members": [
{
"data_type": "string",
"description": "IIO device identifier. It's a space separated list of commands. For commands, if it's an integer value, will be interpreted as IIO device id. If it's a string starting with '/', will be interpreted as absolute path of IIO device on sysfs. If it's on the form 'i2c/X-YYYY', will evaluate to an i2c device on sysfs, where X is the bus number and YYYY is the device number, eg, 7-0069, for device 0x69 on bus 7. If it's on the form 'create,i2c,<rel_path>,<devnumber>,<devname>', where rel_path is the path of bus relative to '/sys/devices', them it will attempt to create an IIO device on that i2c bus and use it.",
"name": "iio_device"
},
{
"data_type": "int",
"description": "IIO buffer size. If -1, buffering is disabled. If 0, will use default buffer size. If enabled (> 0), a trigger is necessary to perform readings. Set it using 'iio_trigger_name'; if no trigger name is set, it will attempt to create a default one, which is activated via TICK port.",
"default": 0,
"name": "buffer_size"
},
{
"data_type": "string",
"description": "IIO trigger name. Name of IIO trigger that should be associated to this device for buffered readings. If not set and buffer enabled, will try to use device current trigger, if any. If none, will attempt to create a sysfs trigger",
"name": "iio_trigger_name",
"default": null
},
{
"data_type": "boolean",
"default": true,
"description": "If should use device own default scale. If false, it will attempt to use scale option.",
"name": "use_device_default_scale"
},
{
"data_type": "float",
"default": 0,
"description": "Scale to applied to device raw readings",
"name": "scale"
},
{
"data_type": "boolean",
"default": true,
"description": "If should use device own default offset. If false, it will attempt to use offset_x, offset_y and offset_z options.",
"name": "use_device_default_offset"
},
{
"data_type": "float",
"default": 0,
"description": "Offset to be added to device raw readings",
"name": "offset"
},
{
"data_type": "int",
"default": -1,
"description": "Sampling frequency of the sensor. If -1, use device default",
"name": "sampling_frequency"
},
{
"data_type": "drange-spec",
"default": {
"max": "DBL_MAX",
"min": "-DBL_MAX",
"step": "DBL_MIN"
},
"description": "Range of output packet. Usually, is the output range of used sensor. It'll be the 'min' and 'max' fields of drange packets sent on OUT port.",
"name": "out_range"
}
],
"version": 1
},
"out_ports": [
{
"data_type": "float",
"description": "proximity data read.",
"name": "OUT"
}
],
"private_data_type": "proximity_data"
}
]
}
154 changes: 154 additions & 0 deletions src/modules/flow/iio/nodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1429,4 +1429,158 @@ light_tick(struct sol_flow_node *node, void *data, uint16_t port, uint16_t conn_
return -EIO;
}

struct proximity_data {
struct sol_iio_config config;
struct sol_drange_spec out_range;
double scale;
double offset;
struct sol_iio_device *device;
struct sol_iio_channel *channel_val;
bool buffer_enabled : 1;
bool use_device_default_scale : 1;
bool use_device_default_offset : 1;
};

static void
proximity_reader_cb(void *data, struct sol_iio_device *device)
{
static const char *errmsg = "Could not read channel buffer values";
struct sol_flow_node *node = data;
struct proximity_data *mdata = sol_flow_node_get_private_data(node);
struct sol_drange out = {
.min = mdata->out_range.min,
.max = mdata->out_range.max,
.step = mdata->out_range.step
};
bool b;

b = sol_iio_read_channel_value(mdata->channel_val, &out.val);
if (!b) goto error;

sol_flow_send_drange_value_packet(node,
SOL_FLOW_NODE_TYPE_IIO_PROXIMITY__OUT__OUT, out.val);

return;

error:
sol_flow_send_error_packet_str(node, EIO, errmsg);
SOL_WRN("%s", errmsg);
}

static bool
proximity_create_channels(struct proximity_data *mdata, int device_id)
{
struct sol_iio_channel_config channel_config = SOL_IIO_CHANNEL_CONFIG_INIT;

mdata->device = sol_iio_open(device_id, &mdata->config);
SOL_NULL_CHECK(mdata->device, false);

#define ADD_CHANNEL(_val) \
if (!mdata->use_device_default_scale) \
channel_config.scale = mdata->scale; \
if (!mdata->use_device_default_offset) \
channel_config.offset = mdata->offset; \
mdata->channel_ ## _val = sol_iio_add_channel(mdata->device, "in_proximity", &channel_config); \
if (!mdata->channel_ ## _val) \
mdata->channel_ ## _val = sol_iio_add_channel(mdata->device, "in_proximity2", &channel_config); \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are adding two channels, but only saves one. So, you only read in_proximity2 in fact, as it's the last one you saved. If a device happens to not have in_proximity2, you'll get NULL here and no readings after.
Nevertheless, I still would like to understand the differences between these channels. I suspect that we could model all channels on only one final information.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh... disregard first comment, only now I've seen the if (!mdata->channel_ ## _val)... sorry.
But I'm kinda puzzled by the in_proximity2... AFAICU from sx9500 datasheet, there's no differentiation among channels...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@edersondisouza As discussed in issue #1971, only channel 2 in sx9500 is needed.
APDS9960/APDS9930 only have the in_proximity_raw, and the SX9500 has in_proximity0_raw to in_proximity3_raw, if we only use in_proximity2_raw, this PR can support all these three sensors. But if we want to add multi-channel support, we need a new implement

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I'll merge this PR, but we need to change nodes to address this multiple channel case.

SOL_NULL_CHECK_GOTO(mdata->channel_ ## _val, error);

ADD_CHANNEL(val);

#undef ADD_CHANNEL

sol_iio_device_start_buffer(mdata->device);

return true;

error:
SOL_WRN("Could not create iio/proximity node. Failed to open IIO device %d",
device_id);

sol_iio_close(mdata->device);
return false;
}

static int
proximity_open(struct sol_flow_node *node, void *data, const struct sol_flow_node_options *options)
{
struct proximity_data *mdata = data;
const struct sol_flow_node_type_iio_proximity_options *opts;
int device_id;

SOL_FLOW_NODE_OPTIONS_SUB_API_CHECK(options, SOL_FLOW_NODE_TYPE_IIO_PROXIMITY_OPTIONS_API_VERSION,
-EINVAL);
opts = (const struct sol_flow_node_type_iio_proximity_options *)options;

mdata->buffer_enabled = opts->buffer_size > -1;

SOL_SET_API_VERSION(mdata->config.api_version = SOL_IIO_CONFIG_API_VERSION; )

if (opts->iio_trigger_name) {
mdata->config.trigger_name = strdup(opts->iio_trigger_name);
SOL_NULL_CHECK(mdata->config.trigger_name, -ENOMEM);
}

mdata->config.buffer_size = opts->buffer_size;
mdata->config.sampling_frequency = opts->sampling_frequency;
if (mdata->buffer_enabled) {
mdata->config.sol_iio_reader_cb = proximity_reader_cb;
mdata->config.data = node;
}
mdata->use_device_default_scale = opts->use_device_default_scale;
mdata->use_device_default_offset = opts->use_device_default_offset;
mdata->scale = opts->scale;
mdata->offset = opts->offset;
mdata->out_range = opts->out_range;

device_id = sol_iio_address_device(opts->iio_device);
if (device_id < 0) {
SOL_WRN("Could not create iio/proximity node. Failed to open IIO device %s",
opts->iio_device);
goto err;
}

if (!proximity_create_channels(mdata, device_id))
goto err;

return 0;

err:
free((char *)mdata->config.trigger_name);
return -EINVAL;

}

static void
proximity_close(struct sol_flow_node *node, void *data)
{
struct proximity_data *mdata = data;

free((char *)mdata->config.trigger_name);
if (mdata->device)
sol_iio_close(mdata->device);
}

static int
proximity_tick(struct sol_flow_node *node, void *data, uint16_t port, uint16_t conn_id, const struct sol_flow_packet *packet)
{
static const char *errmsg = "Could not read channel values";
struct proximity_data *mdata = data;

if (mdata->buffer_enabled) {
if (!sol_iio_device_trigger_now(mdata->device))
goto error;
} else {
proximity_reader_cb(node, mdata->device);
}

return 0;

error:
sol_flow_send_error_packet(node, EIO, "%s", errmsg);
SOL_WRN("%s", errmsg);

return -EIO;
}

#include "iio-gen.c"