From patchwork Tue Apr 26 08:54:25 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 66672 Delivered-To: patch@linaro.org Received: by 10.140.93.198 with SMTP id d64csp1487020qge; Tue, 26 Apr 2016 01:54:47 -0700 (PDT) X-Received: by 10.66.66.234 with SMTP id i10mr1917336pat.114.1461660887035; Tue, 26 Apr 2016 01:54:47 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id zz12si2316493pab.3.2016.04.26.01.54.46; Tue, 26 Apr 2016 01:54:47 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-gpio-owner@vger.kernel.org; dmarc=fail (p=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752338AbcDZIyq (ORCPT + 4 others); Tue, 26 Apr 2016 04:54:46 -0400 Received: from mail-lf0-f47.google.com ([209.85.215.47]:33487 "EHLO mail-lf0-f47.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752235AbcDZIym (ORCPT ); Tue, 26 Apr 2016 04:54:42 -0400 Received: by mail-lf0-f47.google.com with SMTP id y84so8500643lfc.0 for ; Tue, 26 Apr 2016 01:54:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id; bh=34nsqOHmI9o5M6j/voJlJkKx0bBKc/VYUueUqKTnuR0=; b=Q2va4I5b0e3Pan2Z5j0/SWC26Qo/EoGZgSweBAyMbnAMtsmx11ayTFZ35nfLgxu7IC M5zLMLssglwfHVMcHPNRE5N+IIUOgnS965KTfK/Cl1Z4w9WjipvBXUaAqBv86acXVonD p5A/AHCNMmeBLhs1v4pRvwVNbfWo83i00zb48= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=34nsqOHmI9o5M6j/voJlJkKx0bBKc/VYUueUqKTnuR0=; b=G0LJ9nSfaZbUGxSurbUrEhYgiHd1ckih1l4t4jgXBXGRCEU/xmL7oQFD96X5GO/WKA CbB1gZ10vfs9iOqzf6gCTlqynUJGVgR8xj/wTUtM3rjt1C1rbZFYeIlGIwjTEwukMJ/C W806Y0xokpcXzfxerBX2plQ2LP2NluvjzDNgbvwo44NfOT7HT+ywNogAjEs5Xzcmv1dI K/bCafekWv+BmH81/IhcO+vIH0JUMEl8187aW9EcEGIkGjB6uR/wX8rx6YOEMH3IGGKx JOGptquXlMYFbkTMPNFTY8GrVjsVh9O0h0OyTuduc9VPaX/sKMiKOXrX/jwe31d/LRsl FqVA== X-Gm-Message-State: AOPr4FWyHDnIn1fjKqLcTc+6O4SVxZY/ypibZikLUVWF8incUANsVcQbJixT0TicS9rn1NNO X-Received: by 10.25.169.144 with SMTP id s138mr800401lfe.108.1461660880706; Tue, 26 Apr 2016 01:54:40 -0700 (PDT) Received: from localhost.localdomain ([85.235.10.227]) by smtp.gmail.com with ESMTPSA id z21sm283972lff.0.2016.04.26.01.54.39 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 26 Apr 2016 01:54:39 -0700 (PDT) From: Linus Walleij To: linux-gpio@vger.kernel.org, Alexandre Courbot , Michael Welling , Markus Pargmann , Lee Campbell Cc: Bamvor Jian Zhang , Grant Likely , Arnd Bergmann , Mark Brown , Dmitry Torokhov , Johan Hovold , Linus Walleij Subject: [PATCH 1/2] gpio: userspace ABI for reading/writing GPIO lines Date: Tue, 26 Apr 2016 10:54:25 +0200 Message-Id: <1461660866-18683-1-git-send-email-linus.walleij@linaro.org> X-Mailer: git-send-email 2.4.11 Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This adds a userspace ABI for reading and writing GPIO lines. The mechanism returns an anonymous file handle to a request to read/write n offsets from a gpiochip. This file handle in turn accepts two ioctl()s: one that reads and one that writes values to the selected lines. - Handles can be requested as input/output, active low, open drain, open source, however when you issue a request for n lines with GPIO_GET_LINEHANDLE_IOCTL, they must all have the same flags, i.e. all inputs or all outputs, all open drain etc. If a granular control of the flags for each line is desired, they need to be requested individually, not in a batch. - The GPIOHANDLE_GET_LINE_VALUES_IOCTL read ioctl() can be issued also to output lines to verify that the hardware is in the expected state. - It reads and writes up to GPIOHANDLES_MAX lines at once, utilizing the .set_multiple() call in the driver if possible, making the call efficient if several lines can be written with a single register update. The limitation of GPIOHANDLES_MAX to 64 lines is done under the assumption that we may expect hardware that can issue a transaction updating 64 bits at an instant but unlikely anything larger than that. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/gpio.h | 61 ++++++++++++++- 2 files changed, 248 insertions(+), 3 deletions(-) -- 2.4.11 -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index bb3195d5e3af..f72dfb6094a7 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "gpiolib.h" @@ -308,6 +309,193 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc) return 0; } + +/* + * GPIO line handle management + */ + +/** + * struct linehandle_state - contains the state of a userspace handle + * @gdev: the GPIO device the handle pertains to + * @label: consumer label used to tag descriptors + * @descs: the GPIO descriptors held by this handle + * @numdescs: the number of descriptors held in the descs array + */ +struct linehandle_state { + struct gpio_device *gdev; + char *label; + struct gpio_desc *descs[GPIOHANDLES_MAX]; + u32 numdescs; +}; + +static long linehandle_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct linehandle_state *lh = filep->private_data; + int __user *ip = (int __user *)arg; + struct gpiohandle_data ghd; + int i; + + if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { + int val; + + /* TODO: check if descriptors are really input */ + for (i = 0; i < lh->numdescs; i++) { + val = gpiod_get_value(lh->descs[i]); + if (val < 0) + return val; + ghd.values[i] = val; + } + + if (copy_to_user(ip, &ghd, sizeof(ghd))) + return -EFAULT; + + return 0; + } else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) { + int vals[GPIOHANDLES_MAX]; + + /* TODO: check if descriptors are really output */ + if (copy_from_user(&ghd, ip, sizeof(ghd))) + return -EFAULT; + + /* Clamp all values to [0,1] */ + for (i = 0; i < lh->numdescs; i++) + vals[i] = !!ghd.values[i]; + + /* Reuse the array setting function */ + gpiod_set_array_value_complex(false, + true, + lh->numdescs, + lh->descs, + vals); + return 0; + } + return -EINVAL; +} + +static int linehandle_release(struct inode *inode, struct file *filep) +{ + struct linehandle_state *lh = filep->private_data; + struct gpio_device *gdev = lh->gdev; + int i; + + for (i = 0; i < lh->numdescs; i++) + gpiod_free(lh->descs[i]); + devm_kfree(&gdev->dev, lh->label); + devm_kfree(&gdev->dev, lh); + put_device(&gdev->dev); + return 0; +} + +static const struct file_operations linehandle_fileops = { + .release = linehandle_release, + .owner = THIS_MODULE, + .llseek = noop_llseek, + .unlocked_ioctl = linehandle_ioctl, + .compat_ioctl = linehandle_ioctl, +}; + +static int linehandle_create(struct gpio_device *gdev, int __user *ip) +{ + struct gpiohandle_request handlereq; + struct linehandle_state *lh; + int fd, i, ret; + + if (copy_from_user(&handlereq, ip, sizeof(handlereq))) + return -EFAULT; + + /* + * Use devm_* calls with devm_*free() so we can request + * and free the memory for these while still be sure that + * they will eventually go away with the device if not + * before that. + */ + lh = devm_kzalloc(&gdev->dev, sizeof(*lh), GFP_KERNEL); + if (!lh) + return -ENOMEM; + lh->gdev = gdev; + get_device(&gdev->dev); + + fd = anon_inode_getfd("gpio-linehandle", + &linehandle_fileops, + lh, + O_RDONLY | O_CLOEXEC); + if (fd < 0) { + ret = fd; + goto out_free_lh; + } + + /* Make sure this is terminated */ + handlereq.consumer_label[sizeof(handlereq.consumer_label)-1] = '\0'; + if (strlen(handlereq.consumer_label)) { + lh->label = devm_kstrdup(&gdev->dev, + handlereq.consumer_label, + GFP_KERNEL); + if (!lh->label) { + ret = -ENOMEM; + goto out_free_lh; + } + } + + /* Request each GPIO */ + for (i = 0; i < handlereq.lines; i++) { + u32 offset = handlereq.lineoffsets[i]; + u32 lflags = handlereq.flags; + struct gpio_desc *desc; + + desc = &gdev->descs[offset]; + ret = gpiod_request(desc, lh->label); + if (ret) + goto out_free_descs; + lh->descs[i] = desc; + + if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) + set_bit(FLAG_ACTIVE_LOW, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) + set_bit(FLAG_OPEN_DRAIN, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) + set_bit(FLAG_OPEN_SOURCE, &desc->flags); + + /* + * 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 = !!handlereq.default_values[i]; + + ret = gpiod_direction_output(desc, val); + if (ret) + goto out_free_descs; + } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { + ret = gpiod_direction_input(desc); + if (ret) + goto out_free_descs; + } + dev_dbg(&gdev->dev, "registered chardev handle for pin %d\n", + offset); + } + lh->numdescs = handlereq.lines; + dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", + lh->numdescs); + + handlereq.fd = fd; + if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { + ret = -EFAULT; + goto out_free_descs; + } + + return 0; + +out_free_descs: + for (; i >= 0; i--) + gpiod_free(lh->descs[i]); + devm_kfree(&gdev->dev, lh->label); +out_free_lh: + devm_kfree(&gdev->dev, lh); + put_device(&gdev->dev); + return ret; +} + /** * gpio_ioctl() - ioctl handler for the GPIO chardev */ @@ -383,6 +571,8 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) return -EFAULT; return 0; + } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) { + return linehandle_create(gdev, ip); } return -EINVAL; } diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h index d0a3cac72250..8ffcde7b1c95 100644 --- a/include/uapi/linux/gpio.h +++ b/include/uapi/linux/gpio.h @@ -1,7 +1,7 @@ /* * - userspace ABI for the GPIO character devices * - * Copyright (C) 2015 Linus Walleij + * Copyright (C) 2016 Linus Walleij * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by @@ -26,8 +26,8 @@ struct gpiochip_info { __u32 lines; }; -/* Line is in use by the kernel */ -#define GPIOLINE_FLAG_KERNEL (1UL << 0) +/* Informational flags */ +#define GPIOLINE_FLAG_KERNEL (1UL << 0) /* Line used by the kernel */ #define GPIOLINE_FLAG_IS_OUT (1UL << 1) #define GPIOLINE_FLAG_ACTIVE_LOW (1UL << 2) #define GPIOLINE_FLAG_OPEN_DRAIN (1UL << 3) @@ -52,7 +52,62 @@ struct gpioline_info { char consumer[32]; }; +/* Maximum number of requested handles */ +#define GPIOHANDLES_MAX 64 + +/* Request flags */ +#define GPIOHANDLE_REQUEST_INPUT (1UL << 0) +#define GPIOHANDLE_REQUEST_OUTPUT (1UL << 1) +#define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2) +#define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3) +#define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4) + +/** + * struct gpiohandle_request - Information about a GPIO handle request + * @lineoffsets: an array desired lines, specified by offset index for the + * associated GPIO device + * @flags: desired flags for the desired GPIO lines, such as + * GPIOLINE_IS_OUT, GPIOLINE_FLAG_ACTIVE_LOW etc, OR:ed together. + * Note that even if multiple lines are requested, the same flags + * must be applicable to all of them, if you want lines with individual + * flags set, request them one by one. It is possible to select + * a batch of input or output lines, but they must all have the same + * characteristics, i.e. all inputs or all outputs, all active low etc + * @default_values: if the GPIOLINE_FLAG_IS_OUT is set for a requested + * line, 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) + * @consumer_label: a desired consumer label for the selected GPIO line(s) + * such as "my-bitbanged-relay" + * @lines: number of lines requested in this request, i.e. the number of + * valid fields in the above arrays, set to 1 to request a single line + * @fd: if successful this field will contain a valid anonymous file handle + * after a GPIO_GET_LINEHANDLE_IOCTL operation, zero or negative value + * means error + */ +struct gpiohandle_request { + __u32 lineoffsets[GPIOHANDLES_MAX]; + __u32 flags; + __u8 default_values[GPIOHANDLES_MAX]; + char consumer_label[32]; + __u32 lines; + int fd; +}; + #define GPIO_GET_CHIPINFO_IOCTL _IOR(0xB4, 0x01, struct gpiochip_info) #define GPIO_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x02, struct gpioline_info) +#define GPIO_GET_LINEHANDLE_IOCTL _IOWR(0xB4, 0x03, struct gpiohandle_request) + +/** + * struct gpiohandle_data - Information of values on a GPIO handle + * @values: when getting the state of lines this contains the current + * state of a line, when setting the state of lines these should contain + * the desired target state + */ +struct gpiohandle_data { + __u8 values[GPIOHANDLES_MAX]; +}; + +#define GPIOHANDLE_GET_LINE_VALUES_IOCTL _IOR(0xB4, 0x08, struct gpiohandle_data) +#define GPIOHANDLE_SET_LINE_VALUES_IOCTL _IOR(0xB4, 0x09, struct gpiohandle_data) #endif /* _UAPI_GPIO_H_ */