Add support for the GPIOLINE_SET_VALUES_IOCTL. Signed-off-by: Kent Gibson <warthog618@xxxxxxxxx> --- drivers/gpio/gpiolib-cdev.c | 66 +++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 4822cb10aa40..0482a16388a0 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -785,6 +785,70 @@ static long line_get_values(struct line *line, void __user *ip) return 0; } +static long line_set_values_locked(struct line *line, + struct gpioline_set_values *lsv) +{ + unsigned long *vals = (unsigned long *)lsv->bits; + unsigned long *mask = (unsigned long *)lsv->mask; + struct gpio_desc **descs; + int ret, i, didx, num_set = 0; + + for (i = 0; i < line->num_descs; i++) { + if (test_bit(i, mask)) { + if (!test_bit(FLAG_IS_OUT, &line->descs[i]->flags)) + return -EPERM; + num_set++; + } + } + if (num_set == 0) + return -EINVAL; + + if (num_set == line->num_descs) + /* Reuse the array setting function */ + return gpiod_set_array_value_complex(false, + true, + line->num_descs, + line->descs, + NULL, + vals); + + /* build compacted desc array and values */ + descs = kmalloc_array(num_set, sizeof(*descs), GFP_KERNEL); + for (didx = 0, i = 0; i < line->num_descs; i++) { + if (test_bit(i, mask)) { + descs[didx] = line->descs[i]; + assign_bit(didx, vals, test_bit(i, vals)); + didx++; + } + } + ret = gpiod_set_array_value_complex(false, + true, + num_set, + descs, + NULL, + vals); + + kfree(descs); + return ret; +} + +static long line_set_values(struct line *line, void __user *ip) +{ + struct gpioline_set_values lsv; + int ret; + + if (copy_from_user(&lsv, ip, sizeof(lsv))) + return -EFAULT; + + mutex_lock(&line->config_mutex); + + ret = line_set_values_locked(line, &lsv); + + mutex_unlock(&line->config_mutex); + + return ret; +} + static long line_set_config_locked(struct line *line, struct gpioline_config *lc) { @@ -853,6 +917,8 @@ static long line_ioctl(struct file *file, unsigned int cmd, if (cmd == GPIOLINE_GET_VALUES_IOCTL) return line_get_values(line, ip); + else if (cmd == GPIOLINE_SET_VALUES_IOCTL) + return line_set_values(line, ip); else if (cmd == GPIOLINE_SET_CONFIG_IOCTL) return line_set_config(line, ip); -- 2.27.0