From a71bfb4a6aabfe5e6f145883020153103c7fdfba Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:39:41 +0200 Subject: [PATCH 01/52] tools:iio:generic_buffer: fix order of freeing data data gets allocated before buffer_access, so it should be freed in reverse order. Otherwise, if allocating buffer_access fails, an attempt to free it would be taken, which should not happen. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/generic_buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index f805493be3eb..15f2a40c9bf7 100644 --- a/tools/iio/generic_buffer.c +++ b/tools/iio/generic_buffer.c @@ -345,10 +345,10 @@ int main(int argc, char **argv) error_close_buffer_access: close(fp); -error_free_data: - free(data); error_free_buffer_access: free(buffer_access); +error_free_data: + free(data); error_free_buf_dir_name: free(buf_dir_name); error_free_triggername: From d3ccfc41f971105404694e8478b5e60625e46967 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:39:42 +0200 Subject: [PATCH 02/52] tools:iio:generic_buffer: free dev_dir_name on exit Make sure to free dev_dir_name in case of an error or regular exit. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/generic_buffer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index 15f2a40c9bf7..7859ee9a46b2 100644 --- a/tools/iio/generic_buffer.c +++ b/tools/iio/generic_buffer.c @@ -220,7 +220,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; } } @@ -354,6 +354,8 @@ error_free_buf_dir_name: error_free_triggername: if (datardytrigger) free(trigger_name); +error_free_dev_dir_name: + free(dev_dir_name); error_ret: return ret; } From 66dd08fde06e5ad6f0f86c7a780d60973e9d9cf0 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:39:43 +0200 Subject: [PATCH 03/52] tools:iio:iio_utils: free scan_el_dir on exit In the error path, the string scan_el_dir got freed, while it was missing when build_channel_array() finished without errors. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_utils.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index 6f6452167b67..f879ad7b88bc 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -403,6 +403,7 @@ int build_channel_array(const char *device_dir, } closedir(dp); + free(scan_el_dir); /* reorder so that the array is in index order */ bsort_channel_array_by_index(ci_array, *counter); From 63f05c855f3825b89b92cd21df0415e6e32af3dd Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:39:44 +0200 Subject: [PATCH 04/52] tools:iio: free channel-array completely In iio_utils.c build_channel_array() dynamically allocates the string generic_name in the current iio_channel_info, which doesn't got freed in case of an error. This dynamically allocated channel-array is used by generic_buffer, and needs to be freed on the error/exit path. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/generic_buffer.c | 8 +++++++- tools/iio/iio_utils.c | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index 7859ee9a46b2..0410948e23c2 100644 --- a/tools/iio/generic_buffer.c +++ b/tools/iio/generic_buffer.c @@ -255,7 +255,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) { @@ -351,6 +351,12 @@ 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); diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index f879ad7b88bc..6daf98fdde1a 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -410,8 +410,10 @@ int build_channel_array(const char *device_dir, 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); From 121b5e505fe86ea52603c946865d658a5fe8130b Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:39:45 +0200 Subject: [PATCH 05/52] tools:iio:iio_utils: fix allocation handling In build_channel_array(), count needs to be decreased in more places since current->name and current->generic_name would be freed on the error path, although they have not been allocated, yet. This also requires to free current->name, when it is allocated, but current->generic_name is not yet allocated. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_utils.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index 6daf98fdde1a..1c0ca2f89e18 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -334,6 +334,7 @@ int build_channel_array(const char *device_dir, if (sysfsfp == NULL) { free(filename); ret = -errno; + count--; goto error_cleanup_array; } fscanf(sysfsfp, "%i", ¤t_enabled); @@ -353,6 +354,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 +362,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, From f96d055e4b38c64123f211f0521f834d649cd01c Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:39:47 +0200 Subject: [PATCH 06/52] tools:iio:lsiio: add closedir before exit In dump_channels() the DIR *dp was left open on exit. Close it and check for errors. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/lsiio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/iio/lsiio.c b/tools/iio/lsiio.c index c585440f864e..65a2385e2a78 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) From 2b6a6e67af6f8f644a48f75efc1f44544c0d74f6 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:39:48 +0200 Subject: [PATCH 07/52] tools:iio: save errno first The man-page of errno states, that errno should be saved before doing any library call, as that call may have changed the value of errno. So, when encountering any error, save errno first. This patch affects generic_buffer.c, iio_event_monitor.c and iio_utils.c. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/generic_buffer.c | 2 +- tools/iio/iio_event_monitor.c | 6 +++--- tools/iio/iio_utils.c | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index 0410948e23c2..7635aeb93dc8 100644 --- a/tools/iio/generic_buffer.c +++ b/tools/iio/generic_buffer.c @@ -296,8 +296,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; } diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index 427c271ac0d6..f6fdab48d340 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -269,8 +269,8 @@ int main(int argc, char **argv) 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; } @@ -279,8 +279,8 @@ int main(int argc, char **argv) close(fd); if (ret == -1 || event_fd == -1) { - fprintf(stdout, "Failed to retrieve event fd\n"); ret = -errno; + fprintf(stdout, "Failed to retrieve event fd\n"); goto error_free_chrdev_name; } @@ -291,8 +291,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; } } diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index 1c0ca2f89e18..f12bc2e81c80 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -129,8 +129,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 +141,8 @@ 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; } *be = (endianchar == 'b'); @@ -332,8 +332,8 @@ int build_channel_array(const char *device_dir, } sysfsfp = fopen(filename, "r"); if (sysfsfp == NULL) { - free(filename); ret = -errno; + free(filename); count--; goto error_cleanup_array; } @@ -505,8 +505,8 @@ int _write_sysfs_int(char *filename, char *basedir, int val, int verify) sprintf(temp, "%s/%s", basedir, filename); sysfsfp = fopen(temp, "w"); if (sysfsfp == NULL) { - printf("failed to open %s\n", temp); ret = -errno; + printf("failed to open %s\n", temp); goto error_free; } fprintf(sysfsfp, "%d", val); @@ -514,8 +514,8 @@ int _write_sysfs_int(char *filename, char *basedir, int val, int verify) if (verify) { sysfsfp = fopen(temp, "r"); if (sysfsfp == NULL) { - printf("failed to open %s\n", temp); ret = -errno; + printf("failed to open %s\n", temp); goto error_free; } fscanf(sysfsfp, "%d", &test); @@ -556,8 +556,8 @@ int _write_sysfs_string(char *filename, char *basedir, char *val, int verify) sprintf(temp, "%s/%s", basedir, filename); sysfsfp = fopen(temp, "w"); if (sysfsfp == NULL) { - printf("Could not open %s\n", temp); ret = -errno; + printf("Could not open %s\n", temp); goto error_free; } fprintf(sysfsfp, "%s", val); @@ -565,8 +565,8 @@ int _write_sysfs_string(char *filename, char *basedir, char *val, int verify) if (verify) { sysfsfp = fopen(temp, "r"); if (sysfsfp == NULL) { - printf("could not open file to verify\n"); ret = -errno; + printf("could not open file to verify\n"); goto error_free; } fscanf(sysfsfp, "%s", temp); From 5fdb8c6127c3c594360fb5f2959a724809c0032e Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:39:49 +0200 Subject: [PATCH 08/52] tools:iio:iio_event_monitor: save right errno Move up error handling code to preserve the errno coming from ioctl(), before it may be changed by close(). Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_event_monitor.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index f6fdab48d340..578390db1bec 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -275,15 +275,17 @@ int main(int argc, char **argv) } 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; } + close(fd); + while (true) { ret = read(event_fd, &event, sizeof(event)); if (ret == -1) { From 8749948a1bc0aff24e493f89de40e2ff95a83baf Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:39:56 +0200 Subject: [PATCH 09/52] tools:iio:generic_buffer: fix check of errno Since errno contains the value of any of the defined error names, a negation will not lead to the desired match. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/generic_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index 7635aeb93dc8..e01c80ec5a88 100644 --- a/tools/iio/generic_buffer.c +++ b/tools/iio/generic_buffer.c @@ -321,7 +321,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 From e83a47cf6a5bdbd3d5677db13ae4df22f5e24b08 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:39:57 +0200 Subject: [PATCH 10/52] tools:iio:generic_buffer: pass up right error code find_type_by_name() returns a valid error code in case of an error. Pass this code up instead of an artificial one. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/generic_buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index e01c80ec5a88..419e22736c3a 100644 --- a/tools/iio/generic_buffer.c +++ b/tools/iio/generic_buffer.c @@ -202,7 +202,7 @@ int main(int argc, char **argv) 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; + ret = dev_num; goto error_ret; } printf("iio device number being used is %d\n", dev_num); @@ -228,7 +228,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); From 8e926134ef15267f65ddfc7389c8078234610295 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:39:58 +0200 Subject: [PATCH 11/52] tools:iio:generic_buffer: sign-extend and shift data Refactor process_scan() to handle signed and unsigned data, respect shifts and the data mask for 2, 4 and 8 byte sized scan elements. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/generic_buffer.c | 101 +++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 37 deletions(-) diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index 419e22736c3a..4cd246426b57 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; - - 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); + int16_t val = (int16_t)(input << (16 - info->bits_used)) >> + (16 - info->bits_used); + printf("%05f ", ((float)val + info->offset) * info->scale); } else { - uint16_t val = input; - - val &= (1 << info->bits_used) - 1; - printf("%05f ", ((float)val + info->offset)*info->scale); + 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); + + /* + * 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 { + printf("%05f ", ((float)input + 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; From dc8b5d6e633f8e54f70594d0be87aaf401ea0559 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:39:59 +0200 Subject: [PATCH 12/52] tools:iio:iio_utils: check amount of matches fscanf() usually returns the number of input items successfully matched and assigned, which can be fewer than provided (or even zero). Add a check in iioutils_get_type() to make sure all items are matched. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_utils.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index f12bc2e81c80..c5b4136e648a 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -144,6 +144,10 @@ int iioutils_get_type(unsigned *is_signed, 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'); *bytes = padint / 8; From 096f9b862e605fe08bb30e4f7a381684a8ff82ed Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:00 +0200 Subject: [PATCH 13/52] tools:iio:iio_utils: implement digit calculation Previously, the return value of sscanf() was treated as an indication of the digits it would have read. Yet, sscanf() only returns the amount of valid matches. Therefore, introduce a function to calculate the decimal digits of the read number and use this one to commence a colon search, as originally intended. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_utils.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index c5b4136e648a..60e5ec4165b7 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -431,6 +431,18 @@ 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 @@ -441,7 +453,7 @@ error_ret: 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; @@ -459,9 +471,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, ":", @@ -495,6 +517,11 @@ int find_type_by_name(const char *name, const char *type) } closedir(dp); 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) From 2156b179993e3d5b422976181ba17d91153313e1 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:01 +0200 Subject: [PATCH 14/52] tools:iio:iio_utils: mark private function static Functions _write_sysfs_int() and _write_sysfs_string() are supposed to be called only by public wrappers, so make them static. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_utils.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index 60e5ec4165b7..812153ff18c3 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -524,7 +524,7 @@ error_close_dir: return ret; } -int _write_sysfs_int(char *filename, char *basedir, int val, int verify) +static int _write_sysfs_int(char *filename, char *basedir, int val, int verify) { int ret = 0; FILE *sysfsfp; @@ -574,7 +574,8 @@ int write_sysfs_int_and_verify(char *filename, 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(char *filename, char *basedir, char *val, + int verify) { int ret = 0; FILE *sysfsfp; From e9e45b43b8f06273d9b78f187042dff0bf5be0a5 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:02 +0200 Subject: [PATCH 15/52] tools:iio: catch errors in string allocation This patch catches errors in string allocation in generic_buffer.c, iio_event_monitor.c, iio_utils.c and lsiio.c. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/generic_buffer.c | 4 +++- tools/iio/iio_event_monitor.c | 2 ++ tools/iio/iio_utils.c | 9 ++++++--- tools/iio/lsiio.c | 15 +++++++++++++-- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index 4cd246426b57..eb89bc2ca5d0 100644 --- a/tools/iio/generic_buffer.c +++ b/tools/iio/generic_buffer.c @@ -234,7 +234,9 @@ int main(int argc, char **argv) } 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) { diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index 578390db1bec..1374374208ee 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -265,6 +265,8 @@ int main(int argc, char **argv) /* 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); diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index 812153ff18c3..f0896f46847f 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -36,7 +36,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,6 +46,9 @@ int iioutils_break_up_name(const char *full_name, } current = strdup(full_name + strlen(prefix) + 1); + if (!current) + return -ENOMEM; + working = strtok(current, "_\0"); w = working; @@ -59,10 +62,10 @@ 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; } /** diff --git a/tools/iio/lsiio.c b/tools/iio/lsiio.c index 65a2385e2a78..daa6c5312d66 100644 --- a/tools/iio/lsiio.c +++ b/tools/iio/lsiio.c @@ -107,7 +107,12 @@ static void dump_devices(void) if (check_prefix(ent->d_name, type_device)) { char *dev_dir_name; - asprintf(&dev_dir_name, "%s%s", iio_dir, ent->d_name); + if (asprintf(&dev_dir_name, "%s%s", iio_dir, + ent->d_name) < 0) { + printf("Memory allocation failed\n"); + goto error_close_dir; + } + dump_one_device(dev_dir_name); free(dev_dir_name); if (verblevel >= VERBLEVEL_SENSORS) @@ -119,11 +124,17 @@ 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); + if (asprintf(&dev_dir_name, "%s%s", iio_dir, + ent->d_name) < 0) { + printf("Memory allocation failed\n"); + goto error_close_dir; + } + dump_one_trigger(dev_dir_name); free(dev_dir_name); } } +error_close_dir: closedir(dp); } From c8ce9903cba202936999c2c9463fbb370ee145ba Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:03 +0200 Subject: [PATCH 16/52] tools:iio:generic_buffer: catch errors for arguments conversion Add handler to catch errors on conversion of numerical arguments. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/generic_buffer.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index eb89bc2ca5d0..93ac93f74549 100644 --- a/tools/iio/generic_buffer.c +++ b/tools/iio/generic_buffer.c @@ -206,13 +206,22 @@ int main(int argc, char **argv) noevents = 1; break; case 'c': + errno = 0; num_loops = strtoul(optarg, &dummy, 10); + if (errno) + return -errno; break; case 'w': + errno = 0; timedelay = strtoul(optarg, &dummy, 10); + if (errno) + return -errno; break; case 'l': + errno = 0; buf_len = strtoul(optarg, &dummy, 10); + if (errno) + return -errno; break; case 'g': notrigger = 1; From 6bb7cac8551e2d60edbd25a6d046cc45932c4c3e Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:04 +0200 Subject: [PATCH 17/52] tools:iio:generic_buffer: add error handling Add error handling to calls which can indicate a major problem by returning an error code. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/generic_buffer.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index 93ac93f74549..cf9a4120204f 100644 --- a/tools/iio/generic_buffer.c +++ b/tools/iio/generic_buffer.c @@ -347,7 +347,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 { @@ -378,11 +385,14 @@ 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); + if (close(fp) == -1) + perror("Failed to close buffer"); error_free_buffer_access: free(buffer_access); error_free_data: From 963f54cef23b7e8c91bbe60b978b5f4a3e990f2c Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:05 +0200 Subject: [PATCH 18/52] tools:iio:iio_event_monitor: add error handling Add error handling to calls which can indicate a major problem by returning an error code. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_event_monitor.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index 1374374208ee..1316527f3c3c 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -286,7 +286,10 @@ int main(int argc, char **argv) goto error_free_chrdev_name; } - close(fd); + if (close(fd) == -1) { + ret = -errno; + goto error_free_chrdev_name; + } while (true) { ret = read(event_fd, &event, sizeof(event)); @@ -304,7 +307,9 @@ 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: From 53118557b6a9c263e4a80825da367b2116529541 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:14 +0200 Subject: [PATCH 19/52] tools:iio:iio_utils: add error handling Add error handling to calls which can indicate a major problem by returning an error code. This also sets ret to -ENOENT in iioutils_get_type() and iioutils_get_param_float() to indicate if no matching directory entry was found. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_utils.c | 265 +++++++++++++++++++++++++++++++++++------- 1 file changed, 223 insertions(+), 42 deletions(-) diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index f0896f46847f..e1828d0d8a38 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -50,6 +50,10 @@ int iioutils_break_up_name(const char *full_name, return -ENOMEM; working = strtok(current, "_\0"); + if (!working) { + free(current); + return -EINVAL; + } w = working; r = working; @@ -117,6 +121,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 @@ -162,7 +167,12 @@ int iioutils_get_type(unsigned *is_signed, *is_signed = 1; else *is_signed = 0; - fclose(sysfsfp); + if (fclose(sysfsfp)) { + ret = -errno; + printf("Failed to close %s\n", filename); + goto error_free_filename; + } + free(filename); filename = 0; @@ -170,12 +180,16 @@ int iioutils_get_type(unsigned *is_signed, } 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: @@ -215,6 +229,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)) { @@ -229,14 +244,19 @@ 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: @@ -310,10 +330,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)); @@ -344,8 +378,20 @@ int build_channel_array(const char *device_dir, count--; goto error_cleanup_array; } - fscanf(sysfsfp, "%i", ¤t_enabled); - fclose(sysfsfp); + 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; + } if (!current_enabled) { free(filename); @@ -383,8 +429,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, @@ -410,10 +477,16 @@ 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); @@ -427,7 +500,10 @@ error_cleanup_array: } 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: @@ -496,29 +572,45 @@ 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: @@ -536,15 +628,29 @@ static 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; } - fprintf(sysfsfp, "%d", val); - fclose(sysfsfp); + 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; + } + if (verify) { sysfsfp = fopen(temp, "r"); if (sysfsfp == NULL) { @@ -552,8 +658,19 @@ static int _write_sysfs_int(char *filename, char *basedir, int val, int verify) printf("failed to open %s\n", temp); goto error_free; } - fscanf(sysfsfp, "%d", &test); - fclose(sysfsfp); + 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; + } + if (test != val) { printf("Possible failure in int write %d to %s%s\n", val, @@ -588,15 +705,29 @@ static int _write_sysfs_string(char *filename, char *basedir, char *val, 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; } - fprintf(sysfsfp, "%s", val); - fclose(sysfsfp); + 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; + } + if (verify) { sysfsfp = fopen(temp, "r"); if (sysfsfp == NULL) { @@ -604,8 +735,19 @@ static int _write_sysfs_string(char *filename, char *basedir, char *val, printf("could not open file to verify\n"); goto error_free; } - fscanf(sysfsfp, "%s", temp); - fclose(sysfsfp); + 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; + } + if (strcmp(temp, val) != 0) { printf("Possible failure in string write of %s " "Should be %s " @@ -649,14 +791,27 @@ 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; @@ -672,14 +827,27 @@ 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; @@ -695,14 +863,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; From acf50b3586f8d8a7530b905e111dda41876d38f4 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:15 +0200 Subject: [PATCH 20/52] tools:iio:lsiio: add error handling Add error handling to calls which can indicate a major problem by returning an error code. This also involves to change the type of dump_devices() from void to int. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/lsiio.c | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/tools/iio/lsiio.c b/tools/iio/lsiio.c index daa6c5312d66..b1089adb7d3a 100644 --- a/tools/iio/lsiio.c +++ b/tools/iio/lsiio.c @@ -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,20 +90,24 @@ 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) { @@ -109,11 +116,16 @@ static void dump_devices(void) if (asprintf(&dev_dir_name, "%s%s", iio_dir, ent->d_name) < 0) { - printf("Memory allocation failed\n"); + ret = -ENOMEM; + goto error_close_dir; + } + + ret = dump_one_device(dev_dir_name); + if (ret) { + free(dev_dir_name); goto error_close_dir; } - dump_one_device(dev_dir_name); free(dev_dir_name); if (verblevel >= VERBLEVEL_SENSORS) printf("\n"); @@ -126,16 +138,26 @@ static void dump_devices(void) if (asprintf(&dev_dir_name, "%s%s", iio_dir, ent->d_name) < 0) { - printf("Memory allocation failed\n"); + ret = -ENOMEM; + goto error_close_dir; + } + + ret = dump_one_trigger(dev_dir_name); + if (ret) { + free(dev_dir_name); goto error_close_dir; } - dump_one_trigger(dev_dir_name); free(dev_dir_name); } } + return (closedir(dp) == -1) ? -errno : 0; + error_close_dir: - closedir(dp); + if (closedir(dp) == -1) + perror("dump_devices(): Failed to close directory"); + + return ret; } int main(int argc, char **argv) @@ -163,7 +185,5 @@ int main(int argc, char **argv) exit(1); } - dump_devices(); - - return 0; + return dump_devices(); } From 5dc65d791ddafc9f6d944391e379708b6056fc48 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:16 +0200 Subject: [PATCH 21/52] tools:iio:iio_utils: add missing documentation Fully document public functions and elements. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_utils.c | 85 ++++++++++++++++++++++++++++++++++++++++--- tools/iio/iio_utils.h | 5 ++- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index e1828d0d8a38..5d5831d7dfd0 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) @@ -76,11 +78,15 @@ int iioutils_break_up_name(const char *full_name, * 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, @@ -200,6 +206,16 @@ 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, @@ -266,8 +282,9 @@ error_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, @@ -289,7 +306,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, @@ -525,8 +545,10 @@ int calc_digits(int num) /** * 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) @@ -684,11 +706,28 @@ error_free: return ret; } +/** + * 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(char *filename, char *basedir, int val) { return _write_sysfs_int(filename, basedir, val, 0); } +/** + * 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(char *filename, char *basedir, int val) { return _write_sysfs_int(filename, basedir, val, 1); @@ -770,17 +809,35 @@ 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) { return _write_sysfs_string(filename, basedir, val, 1); } +/** + * 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(char *filename, char *basedir, char *val) { return _write_sysfs_string(filename, basedir, val, 0); } +/** + * 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(char *filename, char *basedir) { int ret; @@ -817,6 +874,14 @@ error_free: return ret; } +/** + * 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(char *filename, char *basedir, float *val) { int ret = 0; @@ -853,6 +918,14 @@ error_free: 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; diff --git a/tools/iio/iio_utils.h b/tools/iio/iio_utils.h index 1bc837b2d769..e5eb46ade0a3 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; From 0e799878175aa7d08f5882b6a391de4724c52e9e Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:17 +0200 Subject: [PATCH 22/52] tools:iio: return values directly Return directly, if no common cleanup is required. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/generic_buffer.c | 5 ++--- tools/iio/iio_event_monitor.c | 5 ++--- tools/iio/iio_utils.c | 27 ++++++++++++--------------- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index cf9a4120204f..62ec398922a0 100644 --- a/tools/iio/generic_buffer.c +++ b/tools/iio/generic_buffer.c @@ -238,8 +238,7 @@ int main(int argc, char **argv) dev_num = find_type_by_name(device_name, "iio:device"); if (dev_num < 0) { printf("Failed to find the %s\n", device_name); - ret = dev_num; - goto error_ret; + return dev_num; } printf("iio device number being used is %d\n", dev_num); @@ -410,6 +409,6 @@ error_free_triggername: free(trigger_name); error_free_dev_dir_name: free(dev_dir_name); -error_ret: + return ret; } diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index 1316527f3c3c..7f56238b1d66 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -258,8 +258,7 @@ 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 @@ -312,6 +311,6 @@ int main(int argc, char **argv) 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 5d5831d7dfd0..0524725279bb 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -107,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; @@ -202,7 +201,7 @@ error_free_builtname: free(builtname); error_free_scan_el_dir: free(scan_el_dir); -error_ret: + return ret; } @@ -230,10 +229,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) { @@ -277,7 +275,7 @@ error_free_builtname_generic: free(builtname_generic); error_free_builtname: free(builtname); -error_ret: + return ret; } @@ -326,10 +324,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; @@ -526,7 +523,7 @@ error_close_dir: error_free_name: free(scan_el_dir); -error_ret: + return ret; } From 916e89e4b7272e1eda9eba057cf25197129bcf79 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:18 +0200 Subject: [PATCH 23/52] tools:iio:iio_event_monitor: refactor events output Refactor the code in print_event() to reduce code duplication and better reflect that the type is output unconditionally, as well as cascade the dependency of the diff-channel. Saves a few lines of code, as well. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_event_monitor.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index 7f56238b1d66..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 (chan >= 0) { + printf(", channel: %d", chan); + if (diff && chan2 >= 0) + printf("-%d", chan2); } - if (diff && chan >= 0 && chan2 >= 0) - printf("channel: %d-%d, ", chan, chan2); - else if (chan >= 0) - printf("channel: %d, ", chan); - - 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]); From 33ebcb21a67f257faf1128f08a6d7c0299cb0da2 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:19 +0200 Subject: [PATCH 24/52] tools:iio:iio_utils: refactor assignment of is_signed Change the assignment of *is_signed in iioutils_get_type() to a one-liner, as already done with *be. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_utils.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index 0524725279bb..c83f4dfe71d6 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -168,10 +168,7 @@ 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; + *is_signed = (signchar == 's'); if (fclose(sysfsfp)) { ret = -errno; printf("Failed to close %s\n", filename); From ace76e42bcd5d67e5be303997a4dc325d44366ce Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:20 +0200 Subject: [PATCH 25/52] tools:iio:iio_utils: move up reset of sysfsfp In iioutils_get_type() it is logically better fitting to have sysfsfp assigned zero right after closing it. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index c83f4dfe71d6..7c0abb306f5a 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -175,10 +175,10 @@ int iioutils_get_type(unsigned *is_signed, goto error_free_filename; } + sysfsfp = 0; free(filename); filename = 0; - sysfsfp = 0; } error_close_sysfsfp: if (sysfsfp) From 1e7c34788de3c0e5b18b6f27c42c191da5811c74 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:21 +0200 Subject: [PATCH 26/52] tools:iio:iio_utils: initialize count during declaration In build_channel_array(), count can be initialized already during variable declaration. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_utils.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index 7c0abb306f5a..dfee1a3f3f63 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -312,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; @@ -370,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) { From e06e3d7112f2ec5494d2d934a8641a53885003ee Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:22 +0200 Subject: [PATCH 27/52] tools:iio: rework program parameters In generic_buffer.c: sort program parameters alphabetically and provide usage information In lsiio.c: drop unused parameters Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/generic_buffer.c | 55 +++++++++++++++++++++++++------------- tools/iio/lsiio.c | 6 ++--- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/tools/iio/generic_buffer.c b/tools/iio/generic_buffer.c index 62ec398922a0..4eebb6616e5c 100644 --- a/tools/iio/generic_buffer.c +++ b/tools/iio/generic_buffer.c @@ -168,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 Do n conversions\n" + " -e Disable wait for event (new data)\n" + " -g Use trigger-less mode\n" + " -l Set buffer length to n samples\n" + " -n Set device name (mandatory)\n" + " -t Set trigger name\n" + " -w Set delay between reads in us (event-less mode)\n"); +} + int main(int argc, char **argv) { unsigned long num_loops = 2; @@ -193,29 +206,19 @@ 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 'n': - device_name = optarg; - break; - case 't': - trigger_name = optarg; - datardytrigger = 0; - break; - case 'e': - noevents = 1; - break; case 'c': errno = 0; num_loops = strtoul(optarg, &dummy, 10); if (errno) return -errno; break; - case 'w': - errno = 0; - timedelay = strtoul(optarg, &dummy, 10); - if (errno) - return -errno; + case 'e': + noevents = 1; + break; + case 'g': + notrigger = 1; break; case 'l': errno = 0; @@ -223,16 +226,30 @@ int main(int argc, char **argv) if (errno) return -errno; break; - case 'g': - notrigger = 1; + case 'n': + device_name = optarg; + break; + case 't': + trigger_name = optarg; + datardytrigger = 0; + break; + case 'w': + errno = 0; + timedelay = strtoul(optarg, &dummy, 10); + 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"); diff --git a/tools/iio/lsiio.c b/tools/iio/lsiio.c index b1089adb7d3a..b59ee1733924 100644 --- a/tools/iio/lsiio.c +++ b/tools/iio/lsiio.c @@ -164,7 +164,7 @@ 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++; @@ -179,9 +179,7 @@ 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); } From 9d4752544d17a10a40be5fc5ef68edcae5363599 Mon Sep 17 00:00:00 2001 From: Hartmut Knaack Date: Sun, 31 May 2015 14:40:36 +0200 Subject: [PATCH 28/52] tools:iio:iio_utils: pass strings as const Mark strings, which are not supposed to be changed (basedir, filename, value), as const in function parameters. Signed-off-by: Hartmut Knaack Signed-off-by: Jonathan Cameron --- tools/iio/iio_utils.c | 22 +++++++++++++--------- tools/iio/iio_utils.h | 15 +++++++++------ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index dfee1a3f3f63..ec9ab7f9ae4c 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -634,7 +634,8 @@ error_close_dir: return ret; } -static 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; @@ -707,7 +708,7 @@ error_free: * * Returns a value >= 0 on success, otherwise a negative error code. **/ -int write_sysfs_int(char *filename, char *basedir, int val) +int write_sysfs_int(const char *filename, const char *basedir, int val) { return _write_sysfs_int(filename, basedir, val, 0); } @@ -721,13 +722,14 @@ int write_sysfs_int(char *filename, char *basedir, int val) * * Returns a value >= 0 on success, otherwise a negative error code. **/ -int write_sysfs_int_and_verify(char *filename, char *basedir, int val) +int write_sysfs_int_and_verify(const char *filename, const char *basedir, + int val) { return _write_sysfs_int(filename, basedir, val, 1); } -static 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; @@ -805,7 +807,8 @@ error_free: * * 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); } @@ -818,7 +821,8 @@ int write_sysfs_string_and_verify(char *filename, char *basedir, char *val) * * Returns a value >= 0 on success, otherwise a negative error code. **/ -int write_sysfs_string(char *filename, char *basedir, char *val) +int write_sysfs_string(const char *filename, const char *basedir, + const char *val) { return _write_sysfs_string(filename, basedir, val, 0); } @@ -831,7 +835,7 @@ int write_sysfs_string(char *filename, char *basedir, char *val) * Returns the read integer value >= 0 on success, otherwise a negative error * code. **/ -int read_sysfs_posint(char *filename, char *basedir) +int read_sysfs_posint(const char *filename, const char *basedir) { int ret; FILE *sysfsfp; @@ -875,7 +879,7 @@ error_free: * * Returns a value >= 0 on success, otherwise a negative error code. **/ -int read_sysfs_float(char *filename, char *basedir, float *val) +int read_sysfs_float(const char *filename, const char *basedir, float *val) { int ret = 0; FILE *sysfsfp; diff --git a/tools/iio/iio_utils.h b/tools/iio/iio_utils.h index e5eb46ade0a3..379eed9deaea 100644 --- a/tools/iio/iio_utils.h +++ b/tools/iio/iio_utils.h @@ -63,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_ */ From ff7d4f5981a8a139ead70adef3c1d0ed574bca01 Mon Sep 17 00:00:00 2001 From: Laurent Navet Date: Sat, 30 May 2015 22:35:36 +0200 Subject: [PATCH 29/52] iio: buffer: remove unneeded test The same code is executed regardless ret value, so this test can be removed. Also fix coverity scan CID 1268786. Signed-off-by: Laurent Navet Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 11291259b7b9..dad61ab9b36f 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -888,8 +888,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; From b3f6af36e52e1e61cf7571c77dba0e905c38297a Mon Sep 17 00:00:00 2001 From: Vladimirs Ambrosovs Date: Sat, 30 May 2015 11:20:15 +0300 Subject: [PATCH 30/52] staging: iio_simple_dummy: fix init function This patch fixes the init function for the iio_simple_dummy driver. The main issues were absence of kfree for the allocated array, and no devices being removed in case the probe function fails, running in a loop. Signed-off-by: Vladimirs Ambrosovs Signed-off-by: Jonathan Cameron --- drivers/staging/iio/iio_simple_dummy.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c index b47bf9fb64be..d0a99637007c 100644 --- a/drivers/staging/iio/iio_simple_dummy.c +++ b/drivers/staging/iio/iio_simple_dummy.c @@ -722,9 +722,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); From 62a90da69df1255ba43d3b6595e71e639efd80a8 Mon Sep 17 00:00:00 2001 From: Vladimirs Ambrosovs Date: Sat, 30 May 2015 11:20:16 +0300 Subject: [PATCH 31/52] staging: iio_simple_dummy: fix return types The functions iio_dummy_remove(), iio_simple_dummy_events_unregister() and iio_dummy_evgen_release_irq() were changed to return void instead of int. Signed-off-by: Vladimirs Ambrosovs Signed-off-by: Jonathan Cameron --- drivers/staging/iio/iio_dummy_evgen.c | 4 +--- drivers/staging/iio/iio_dummy_evgen.h | 2 +- drivers/staging/iio/iio_simple_dummy.c | 10 ++-------- drivers/staging/iio/iio_simple_dummy.h | 8 +++----- drivers/staging/iio/iio_simple_dummy_events.c | 4 +--- 5 files changed, 8 insertions(+), 20 deletions(-) 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 d0a99637007c..dc9482c7569c 100644 --- a/drivers/staging/iio/iio_simple_dummy.c +++ b/drivers/staging/iio/iio_simple_dummy.c @@ -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; } /** 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; } From 4dcaa5f75473271ea83545eb5a95db8d7a152362 Mon Sep 17 00:00:00 2001 From: Vladimirs Ambrosovs Date: Sat, 30 May 2015 11:20:17 +0300 Subject: [PATCH 32/52] staging: iio_simple_dummy: fix module_param type Fix the module_param "instances" type to uint, since the variable type holding the value is unsigned. Signed-off-by: Vladimirs Ambrosovs Signed-off-by: Jonathan Cameron --- drivers/staging/iio/iio_simple_dummy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c index dc9482c7569c..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; From 629bc02331f7aae6ef775fb4c15e6d8aa58722f1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 29 May 2015 18:14:20 +0200 Subject: [PATCH 33/52] iio: Always compute masklength Even if no userspace consumer buffer is attached to the IIO device at registration we still need to compute the masklength, since it is possible that a in-kernel consumer buffer is going to get attached to the device at a later point. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index dad61ab9b36f..209c7ad793c5 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -966,6 +966,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; @@ -1009,12 +1018,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) From 225d59adf1c899176cce0fc80e42b1d1c12f109f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 29 May 2015 18:14:21 +0200 Subject: [PATCH 34/52] iio: Specify supported modes for buffers For each buffer type specify the supported device modes for this buffer. This allows us for devices which support multiple different operating modes to pick the correct operating mode based on the modes supported by the attached buffers. It also prevents that buffers with conflicting modes are attached to a device at the same time or that a buffer with a non-supported mode is attached to a device (e.g. in-kernel callback buffer to a device only supporting hardware mode). Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/buffer_cb.c | 2 ++ drivers/iio/industrialio-buffer.c | 18 +++++++++++++++--- drivers/iio/kfifo_buf.c | 2 ++ drivers/staging/iio/accel/sca3000_ring.c | 2 ++ include/linux/iio/buffer.h | 3 +++ 5 files changed, 24 insertions(+), 3 deletions(-) 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/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 209c7ad793c5..0a14bd825fe0 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -604,6 +604,7 @@ static int iio_verify_update(struct iio_dev *indio_dev, const unsigned long *scan_mask; struct iio_buffer *buffer; bool scan_timestamp; + unsigned int modes; memset(config, 0, sizeof(*config)); @@ -615,12 +616,23 @@ 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) { config->mode = INDIO_BUFFER_HARDWARE; - } else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) { + } else if (modes & INDIO_BUFFER_SOFTWARE) { config->mode = INDIO_BUFFER_SOFTWARE; } else { /* Can only occur on first buffer */ diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c index 847ca561afe0..db15684a4731 100644 --- a/drivers/iio/kfifo_buf.c +++ b/drivers/iio/kfifo_buf.c @@ -135,6 +135,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/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/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; }; /** From 1e1ec2861e0d4307267096c3f74c17298c1cde98 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 29 May 2015 18:14:22 +0200 Subject: [PATCH 35/52] iio: Require strict scan mask matching in hardware mode In hardware mode we can not use the software demuxer, this means that the selected scan mask needs to match one of the available scan masks exactly. It also means that all attached buffers need to use the same scan mask. Given that when operating in hardware mode there is typically only a single buffer attached to the device this not an issue. Add a sanity check to make sure that only a single buffer is attached in hardware mode nevertheless. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 0a14bd825fe0..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,6 +608,7 @@ 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; @@ -631,7 +638,14 @@ static int iio_verify_update(struct iio_dev *indio_dev, if ((modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) { config->mode = INDIO_BUFFER_TRIGGERED; } 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; + strict_scanmask = true; } else if (modes & INDIO_BUFFER_SOFTWARE) { config->mode = INDIO_BUFFER_SOFTWARE; } else { @@ -666,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; From 5517547bf44731621338c1a4047196a49c790719 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 27 May 2015 11:20:52 +0300 Subject: [PATCH 36/52] iio: magnetometer: correct a harmless off by one check The line before limits i to 0-3 so the existing code works fine but the check is still off by one and >= is intended instead of >. Signed-off-by: Dan Carpenter Reviewed-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index aa6e25d3bfc3..c71392cf1370 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -308,7 +308,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]; From ed6e75c7dc5627c7251002936577052ae73ac2db Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 27 May 2015 11:24:25 +0300 Subject: [PATCH 37/52] iio: light: signedness bug in stk3310_write_raw() "index" needs to be signed for the error handling to work. Fixes: be9e6229d676 ('iio: light: Add support for Sensortek STK3310') Signed-off-by: Dan Carpenter Signed-off-by: Jonathan Cameron --- drivers/iio/light/stk3310.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) { From a52ffebcf19afea27588abe75828f9d300e08f3d Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 5 Jun 2015 14:03:10 +0300 Subject: [PATCH 38/52] iio: magnetometer: mmc35240: i2c device name should be lower case This is the standard convention for i2c device name and also this is the name used in some Intel platforms DT files. Fixes: abeb6b1e7b ("iio: magnetometer: Add support for MEMSIC MMC35240") Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index c71392cf1370..d8cf5a6f9291 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -490,7 +490,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); From bd35a214f56158d11e46c4a9d8436139f58e7099 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 5 Jun 2015 14:03:11 +0300 Subject: [PATCH 39/52] iio: magnetometer: mmc35240: NULL terminate attribute array This avoid nasty crashes when registering the IIO device. Fixes: abeb6b1e7b ("iio: magnetometer: Add support for MEMSIC MMC35240") Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index d8cf5a6f9291..78da0d15c8eb 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -125,6 +125,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 = { From c2890547a035e019df646be5d56adc0ee1b0a327 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 5 Jun 2015 14:03:12 +0300 Subject: [PATCH 40/52] iio: magnetometer: mmc35240: Fix broken processed value The current computation for fractional part of the magnetic field is broken. This patch fixes it by taking a different approach. We expose the raw reading in milli Gauss (to avoid rounding errors) with a scale of 0.001. Thus the final computation is done in userspace where floating point operation are more relaxed. Fixes: abeb6b1e7b ("iio: magnetometer: Add support for MEMSIC MMC35240") Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 43 ++++++++++++++++++----------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index 78da0d15c8eb..146ae6613537 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -113,8 +113,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[] = { @@ -241,9 +242,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; @@ -261,18 +272,15 @@ 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; @@ -290,16 +298,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); From 787f55c4d1842e5fb037a81bca9bd9d9fdfd46fe Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 5 Jun 2015 14:03:13 +0300 Subject: [PATCH 41/52] iio: magnetometer: mmc35240: Use a smaller sleep value According to datasheet, Page 8, minimum wait time to complete measurement is 10ms. Adjusting this value will increase the userspace polling rate. Fixes: abeb6b1e7b ("iio: magnetometer: Add support for MEMSIC MMC35240") Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index 146ae6613537..7fdf906b94c9 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -219,7 +219,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) { From 6b90da4b58b39a80f490479953aa2563c3c41a6d Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 5 Jun 2015 14:03:14 +0300 Subject: [PATCH 42/52] iio: magnetometer: mmc35240: Fix sensitivity on z-axis Datasheet says (Page 2) that typical value for sensitivity for 16 bits mode on Z-axis is 770. Anyhow, looking at the input driver provided by Memsic the value for MMC35240 is 1024. Also, testing shows that using 1024 for Z-axis senzitivity offers better results. Fixes: abeb6b1e7b ("iio: magnetometer: Add support for MEMSIC MMC35240") Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index 7fdf906b94c9..b2ab58ea15f0 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -77,7 +77,7 @@ static const struct { } mmc35240_props_table[] = { /* 16 bits, 100Hz ODR */ { - {1024, 1024, 770}, + {1024, 1024, 1024}, 32768, }, /* 16 bits, 200Hz ODR */ From 4892688d7004c004765a51976087a8f51ce3586d Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 5 Jun 2015 14:03:15 +0300 Subject: [PATCH 43/52] iio: magnetometer: mmc35240: Add compensation for raw values This patch adds compensation formula to raw readings, borrowed from Memsic's input driver. Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 62 +++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index b2ab58ea15f0..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 */ @@ -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}; @@ -172,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) { @@ -197,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) @@ -286,6 +337,9 @@ static int mmc35240_raw_to_mgauss(struct mmc35240_data *data, int index, default: return -EINVAL; } + /* apply OTP compensation */ + *val = (*val) * data->axis_coef[index] / data->axis_scale[index]; + return 0; } From 28e3427824ccc864b2866905eb10278c584b461d Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Mon, 1 Jun 2015 15:39:52 +0200 Subject: [PATCH 44/52] iio: mma8452: Basic support for transient events. The event is triggered when the highpass filtered absolute acceleration exceeds the threshold. Signed-off-by: Martin Fuzzey Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 212 +++++++++++++++++++++++++++++++++++- 1 file changed, 211 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 877ce2954196..f30eb9b7a99c 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 @@ -19,20 +19,33 @@ #include #include #include +#include #include #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_TRANSIENT_CFG 0x1d +#define MMA8452_TRANSIENT_CFG_ELE BIT(4) +#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1) +#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_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 @@ -48,6 +61,8 @@ #define MMA8452_DATA_CFG_FS_4G 1 #define MMA8452_DATA_CFG_FS_8G 2 +#define MMA8452_INT_TRANS BIT(5) + #define MMA8452_DEVICE_ID 0x2a struct mma8452_data { @@ -274,6 +289,126 @@ static int mma8452_write_raw(struct iio_dev *indio_dev, } } +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; + + 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; +} + +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); + + return mma8452_change_config(data, MMA8452_TRANSIENT_THS, + val & MMA8452_TRANSIENT_THS_MASK); +} + +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 src; + + src = i2c_smbus_read_byte_data(data->client, MMA8452_INT_SRC); + if (src < 0) + return IRQ_NONE; + + if (src & MMA8452_INT_TRANS) { + mma8452_transient_interrupt(indio_dev); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + static irqreturn_t mma8452_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -316,6 +451,31 @@ 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) + }, +}; + +/* + * 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, \ @@ -332,6 +492,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[] = { @@ -355,6 +517,11 @@ 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, }; @@ -425,6 +592,38 @@ 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_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, + supported_interrupts); + 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, @@ -437,9 +636,20 @@ static int mma8452_probe(struct i2c_client *client, if (ret < 0) return ret; + 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: From 5dbbd19f11846574e9143ae96da8603017cf823b Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Mon, 1 Jun 2015 15:39:54 +0200 Subject: [PATCH 45/52] iio: mma8452: Add support for transient event debouncing Allow the debouce counter for transient events to be configured using the sysfs attribute events/in_accel_thresh_rising_period Signed-off-by: Martin Fuzzey Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 76 +++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index f30eb9b7a99c..7429df3a191e 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -38,6 +38,7 @@ #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 @@ -124,6 +125,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} @@ -139,6 +146,18 @@ 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 +}; + static ssize_t mma8452_show_samp_freq_avail(struct device *dev, struct device_attribute *attr, char *buf) { @@ -198,8 +217,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; @@ -297,15 +315,33 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev, int *val, int *val2) { struct mma8452_data *data = iio_priv(indio_dev); - int ret; + int ret, us; - ret = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_THS); - if (ret < 0) - return ret; + 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; + *val = ret & MMA8452_TRANSIENT_THS_MASK; + return IIO_VAL_INT; - 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; + + default: + return -EINVAL; + } } static int mma8452_write_thresh(struct iio_dev *indio_dev, @@ -316,9 +352,26 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev, int val, int val2) { struct mma8452_data *data = iio_priv(indio_dev); + int steps; - return mma8452_change_config(data, MMA8452_TRANSIENT_THS, - val & MMA8452_TRANSIENT_THS_MASK); + 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); + default: + return -EINVAL; + } } static int mma8452_read_event_config(struct iio_dev *indio_dev, @@ -456,7 +509,8 @@ 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) + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD) }, }; From 1e79841a00e46506da887c718dd36a86b24b63e5 Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Mon, 1 Jun 2015 15:39:56 +0200 Subject: [PATCH 46/52] iio: mma8452: Add highpass filter configuration. Allow the cutoff frequency of the high pass filter to be configured. Signed-off-by: Martin Fuzzey Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 140 ++++++++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 4 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 7429df3a191e..51ae751e64a7 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -29,9 +29,12 @@ #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) @@ -61,6 +64,7 @@ #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_TRANS BIT(5) @@ -158,6 +162,18 @@ static const int mma8452_transient_time_step_us[8] = { 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) { @@ -172,9 +188,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) @@ -190,6 +220,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) @@ -228,6 +283,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; } @@ -269,12 +334,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; @@ -302,6 +386,19 @@ 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; } @@ -339,6 +436,22 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev, *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; } @@ -352,7 +465,7 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev, int val, int val2) { struct mma8452_data *data = iio_priv(indio_dev); - int steps; + int ret, reg, steps; switch (info) { case IIO_EV_INFO_VALUE: @@ -369,6 +482,22 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev, 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; } @@ -510,7 +639,8 @@ static const struct iio_event_spec mma8452_transient_event[] = { .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_PERIOD) | + BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB) }, }; @@ -537,7 +667,8 @@ static struct attribute_group mma8452_event_attribute_group = { .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', \ @@ -560,6 +691,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 }; From ae6d9ce05691bf79694074db7c7da980080548af Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Mon, 1 Jun 2015 15:39:58 +0200 Subject: [PATCH 47/52] iio: mma8452: Add support for interrupt driven triggers. Implement interrupt driven trigger for data ready. This allows more efficient access to the sample data. Signed-off-by: Martin Fuzzey Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 101 +++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 8 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 51ae751e64a7..e8e2077c7244 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -66,6 +68,7 @@ #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 @@ -577,18 +580,24 @@ 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_TRANS) { - mma8452_transient_interrupt(indio_dev); - return IRQ_HANDLED; + if (src & MMA8452_INT_DRDY) { + iio_trigger_poll_chained(indio_dev->trig); + ret = IRQ_HANDLED; } - return IRQ_NONE; + 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) @@ -714,6 +723,72 @@ static const struct iio_info mma8452_info = { 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; @@ -794,7 +869,8 @@ static int mma8452_probe(struct i2c_client *client, * enabled until userspace asks for it by * mma8452_write_event_config() */ - int supported_interrupts = MMA8452_INT_TRANS; + 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, @@ -805,7 +881,11 @@ static int mma8452_probe(struct i2c_client *client, ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG4, - supported_interrupts); + enabled_interrupts); + if (ret < 0) + return ret; + + ret = mma8452_trigger_setup(indio_dev); if (ret < 0) return ret; } @@ -815,12 +895,12 @@ static int mma8452_probe(struct i2c_client *client, 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, @@ -840,6 +920,10 @@ static int mma8452_probe(struct i2c_client *client, buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); + +trigger_cleanup: + mma8452_trigger_cleanup(indio_dev); + return ret; } @@ -849,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; From 0cbb39f1432096cf00eca21f2d10f6d2057c26ba Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Thu, 28 May 2015 21:50:18 +0200 Subject: [PATCH 48/52] iio: adc: twl4030_madc: Fix calculation of the temperature sense current The bit mask to read the setting of the constant current source for measuring the NTC voltage was the wrong one. Since default value is initialized to the lowest level (000 = 10uA) the difference was probably never noticed in practice. Signed-off-by: H. Nikolaus Schaller Signed-off-by: Marek Belisko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/twl4030-madc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index 94c5f05b4bc1..6d2d429a48e0 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 */ From 994bda83dc6facc3fdd2130b0162b2abf09b2100 Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Thu, 28 May 2015 21:50:19 +0200 Subject: [PATCH 49/52] iio: adc: twl4030_madc: Fix description of twl4030_madc_set_current_generator() The @chan parameter can be 0 or 1 and not a bit mask. Fix wrong description. Signed-off-by: H. Nikolaus Schaller Signed-off-by: Marek Belisko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/twl4030-madc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index 6d2d429a48e0..06f4792240f0 100644 --- a/drivers/iio/adc/twl4030-madc.c +++ b/drivers/iio/adc/twl4030-madc.c @@ -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 From bf04c1a367e3d52b2e071cc7c7047c27dc1c3c5f Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 27 May 2015 14:47:51 +0200 Subject: [PATCH 50/52] iio: adc: vf610: implement configurable conversion modes Support configurable conversion mode through sysfs. So far, the mode used was low-power, which is enabled by default now. Beside that, the modes normal and high-speed are selectable as well. Use the new device tree property which specifies the maximum ADC conversion clock frequencies. Depending on the mode used, the available resulting conversion frequency are calculated dynamically. Acked-by: Fugang Duan Signed-off-by: Stefan Agner Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio-vf610 | 7 + .../devicetree/bindings/iio/adc/vf610-adc.txt | 9 ++ drivers/iio/adc/vf610_adc.c | 146 +++++++++++++----- 3 files changed, 120 insertions(+), 42 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-vf610 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/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); From 4861a007bfd71a9fc0a83cc7fad41dda9bf8b5b7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 May 2015 15:37:02 +0200 Subject: [PATCH 51/52] iio: st_accel: support 8bit channel data Some sensors like the LIS331DL only support 8bit data by a single register per axis. These utilize the MSB byte. Make it possible to register these apropriately. A oneliner change is needed in the ST sensors core to handle 8bit reads as this is the first supported 8bit sensor. Signed-off-by: Linus Walleij Acked-by: Denis Ciocca Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 16 ++++++++++++++++ drivers/iio/common/st_sensors/st_sensors_core.c | 4 +++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 58d1d13d552a..ad19fb4304df 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -153,6 +153,22 @@ #define ST_ACCEL_4_IG1_EN_MASK 0x08 #define ST_ACCEL_4_MULTIREAD_BIT true +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), diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 1255b157c71c..bf4b13f9defc 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -433,7 +433,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); From bbf5f037fad47e4affef6696aaf88a40b261e639 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 May 2015 15:37:18 +0200 Subject: [PATCH 52/52] iio: st_accel: support the LIS331DL sensor This adds support for the LIS331DL sensor version. This is a simple 8bit-only accelerometer. Signed-off-by: Linus Walleij Acked-by: Denis Ciocca Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/st-sensors.txt | 1 + drivers/iio/accel/st_accel.h | 1 + drivers/iio/accel/st_accel_core.c | 70 +++++++++++++++++++ drivers/iio/accel/st_accel_i2c.c | 4 ++ 4 files changed, 76 insertions(+) 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/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 ad19fb4304df..970e9f904dde 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -153,6 +153,28 @@ #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), @@ -470,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 @@ -48,6 +48,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lsm330dlc-accel", .data = LSM330DLC_ACCEL_DEV_NAME, }, + { + .compatible = "st,lis331dl-accel", + .data = LIS331DL_ACCEL_DEV_NAME, + }, { .compatible = "st,lis331dlh-accel", .data = LIS331DLH_ACCEL_DEV_NAME,