gpio: add new SET_CONFIG ioctl() to gpio chardev
authorKent Gibson <warthog618@gmail.com>
Tue, 5 Nov 2019 02:04:29 +0000 (10:04 +0800)
committerBartosz Golaszewski <bgolaszewski@baylibre.com>
Tue, 12 Nov 2019 15:30:31 +0000 (16:30 +0100)
Add the GPIOHANDLE_SET_CONFIG_IOCTL to the gpio chardev.
The ioctl allows some of the configuration of a requested handle to be
changed without having to release the line.
The primary use case is the changing of direction for bi-directional
lines.

Based on initial work by Bartosz Golaszewski <bgolaszewski@baylibre.com>

Signed-off-by: Kent Gibson <warthog618@gmail.com>
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
drivers/gpio/gpiolib.c
include/uapi/linux/gpio.h

index a9826627f3475c7af0fbed149839fa49d7a0dc10..dba5f08f308c47ecf53c07cd3101aa8dad65212e 100644 (file)
@@ -476,6 +476,73 @@ static int linehandle_validate_flags(u32 flags)
        return 0;
 }
 
+static void linehandle_configure_flag(unsigned long *flagsp,
+                                     u32 bit, bool active)
+{
+       if (active)
+               set_bit(bit, flagsp);
+       else
+               clear_bit(bit, flagsp);
+}
+
+static long linehandle_set_config(struct linehandle_state *lh,
+                                 void __user *ip)
+{
+       struct gpiohandle_config gcnf;
+       struct gpio_desc *desc;
+       int i, ret;
+       u32 lflags;
+       unsigned long *flagsp;
+
+       if (copy_from_user(&gcnf, ip, sizeof(gcnf)))
+               return -EFAULT;
+
+       lflags = gcnf.flags;
+       ret = linehandle_validate_flags(lflags);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < lh->numdescs; i++) {
+               desc = lh->descs[i];
+               flagsp = &desc->flags;
+
+               linehandle_configure_flag(flagsp, FLAG_ACTIVE_LOW,
+                       lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW);
+
+               linehandle_configure_flag(flagsp, FLAG_OPEN_DRAIN,
+                       lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN);
+
+               linehandle_configure_flag(flagsp, FLAG_OPEN_SOURCE,
+                       lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE);
+
+               linehandle_configure_flag(flagsp, FLAG_PULL_UP,
+                       lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP);
+
+               linehandle_configure_flag(flagsp, FLAG_PULL_DOWN,
+                       lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN);
+
+               linehandle_configure_flag(flagsp, FLAG_BIAS_DISABLE,
+                       lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE);
+
+               /*
+                * Lines have to be requested explicitly for input
+                * or output, else the line will be treated "as is".
+                */
+               if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
+                       int val = !!gcnf.default_values[i];
+
+                       ret = gpiod_direction_output(desc, val);
+                       if (ret)
+                               return ret;
+               } else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
+                       ret = gpiod_direction_input(desc);
+                       if (ret)
+                               return ret;
+               }
+       }
+       return 0;
+}
+
 static long linehandle_ioctl(struct file *filep, unsigned int cmd,
                             unsigned long arg)
 {
@@ -526,6 +593,8 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
                                              lh->descs,
                                              NULL,
                                              vals);
+       } else if (cmd == GPIOHANDLE_SET_CONFIG_IOCTL) {
+               return linehandle_set_config(lh, ip);
        }
        return -EINVAL;
 }
index 7cc21c3b083977c8ed53b33c509c6ae2c6248368..799cf823d493a3b358089bcaafb60f725f468be3 100644 (file)
@@ -100,6 +100,24 @@ struct gpiohandle_request {
        int fd;
 };
 
+/**
+ * struct gpiohandle_config - Configuration for a GPIO handle request
+ * @flags: updated flags for the requested GPIO lines, such as
+ * GPIOHANDLE_REQUEST_OUTPUT, GPIOHANDLE_REQUEST_ACTIVE_LOW etc, OR:ed
+ * together
+ * @default_values: if the GPIOHANDLE_REQUEST_OUTPUT is set in flags,
+ * this specifies the default output value, should be 0 (low) or
+ * 1 (high), anything else than 0 or 1 will be interpreted as 1 (high)
+ * @padding: reserved for future use and should be zero filled
+ */
+struct gpiohandle_config {
+       __u32 flags;
+       __u8 default_values[GPIOHANDLES_MAX];
+       __u32 padding[4]; /* padding for future use */
+};
+
+#define GPIOHANDLE_SET_CONFIG_IOCTL _IOWR(0xB4, 0x0a, struct gpiohandle_config)
+
 /**
  * struct gpiohandle_data - Information of values on a GPIO handle
  * @values: when getting the state of lines this contains the current