From 3c3b5a3f7c7865c8fd8bc3290219e9da900656f0 Mon Sep 17 00:00:00 2001 From: Yong Li Date: Fri, 6 May 2016 14:07:26 +0800 Subject: [PATCH] flow:iio: Add Proximity sensor Category support into iio node Signed-off-by: Yong Li --- src/modules/flow/iio/iio.json | 89 ++++++++++++++++++++ src/modules/flow/iio/nodes.c | 154 ++++++++++++++++++++++++++++++++++ 2 files changed, 243 insertions(+) diff --git a/src/modules/flow/iio/iio.json b/src/modules/flow/iio/iio.json index 1de8254a9..3611a4ba8 100644 --- a/src/modules/flow/iio/iio.json +++ b/src/modules/flow/iio/iio.json @@ -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,,,', 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" } ] } diff --git a/src/modules/flow/iio/nodes.c b/src/modules/flow/iio/nodes.c index ce5da1239..dedd7b7d1 100644 --- a/src/modules/flow/iio/nodes.c +++ b/src/modules/flow/iio/nodes.c @@ -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); \ + 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"