From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.0 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 63A6EC433DF for ; Sat, 25 Jul 2020 04:21:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 2DCFD206EB for ; Sat, 25 Jul 2020 04:21:38 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="E0W6oETx" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726636AbgGYEVh (ORCPT ); Sat, 25 Jul 2020 00:21:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35646 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725766AbgGYEVh (ORCPT ); Sat, 25 Jul 2020 00:21:37 -0400 Received: from mail-pg1-x544.google.com (mail-pg1-x544.google.com [IPv6:2607:f8b0:4864:20::544]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3D0D7C0619D3; Fri, 24 Jul 2020 21:21:37 -0700 (PDT) Received: by mail-pg1-x544.google.com with SMTP id n5so6495670pgf.7; Fri, 24 Jul 2020 21:21:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=RlfKxgMK8kyfoZ4IqhKS6wRML8OI3BN7ZAL2rq6coLQ=; b=E0W6oETxuGMT/Anf4SQMOy5p/spurLsGZWhdvzh8J3JvrDhEUhQSmMUtOv2qjye+yB 0qulKv0Q6EsjX0K/sF1bThOkNJYVjewmrcOG8/vmuLGbsyaEQLQrPVe9EghgKzNlSbz6 Xe1ILASPhltRc+OJ+hDqicokU49ZoupMT3KH4hGOtY0GeD7eQnD6j7CsK98o0GSVHgq7 H865RQus4iQ5rsNN8uAJXTp5YhItLJan3Pz0xmYe4kpv6Vb5R35M2n1SmQGu7CVttjGi 4+8VX7roqQNxVzqglncK4QbtnB/Nzr5WMIL/th9HaMtpOQ0dCmDqLQ9LCgFSk59MeTYz qyIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=RlfKxgMK8kyfoZ4IqhKS6wRML8OI3BN7ZAL2rq6coLQ=; b=clCDDm8OR+P6/bIJzg9TW7242VEXJM+uKvyMfFkahqVmohd5SPVO4hz7pN5cJRF0Gm 4J4RpGYFXNmnFQWN4KUe1cCjDdw1LCUvpeAkBYqgxTHLCcgG9YZk43CwrdV2+p09SCMl waGlJhmVql5fpg4eeo7VMeM8EUBqYtPbk8hD9DGRurNNbbnfcvi8J0DiwUtgPS8/llB9 yyCdqvikvFRjIge5AIk3bq8trUOKnrY9TAwtuwi3FtKbUIxEtu7KsN5A5kHWGHbMZJOl 9oS9FF9AX8xNaYsJDiSM17HVtd22CvQDW1APUjNMBmmXQaIg3JzRUktWKIFGTzP3c/4P yxeA== X-Gm-Message-State: AOAM532w293EkVJ4UhHCp6IJPhBiV1hCcWC3EnvKZPcIyCZOhfBnbjTh VjAZcsC1fpH3Q0LeZc4EDi1RQ3Pf X-Google-Smtp-Source: ABdhPJx06GBPf8PvJqc/Deb0HM0oRXawtTs8UrDG1Tf7wode02E7yLZAUIYYt5WQVMD+b+z4HnupQQ== X-Received: by 2002:a63:e617:: with SMTP id g23mr10932795pgh.102.1595650895983; Fri, 24 Jul 2020 21:21:35 -0700 (PDT) Received: from sol.lan (106-69-185-93.dyn.iinet.net.au. [106.69.185.93]) by smtp.gmail.com with ESMTPSA id p1sm885320pjp.10.2020.07.24.21.21.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Jul 2020 21:21:35 -0700 (PDT) From: Kent Gibson To: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, bgolaszewski@baylibre.com, linus.walleij@linaro.org Cc: Kent Gibson Subject: [PATCH v2 06/18] gpiolib: cdev: support GPIO_GET_LINEINFO_V2_IOCTL and GPIO_GET_LINEINFO_WATCH_V2_IOCTL Date: Sat, 25 Jul 2020 12:19:43 +0800 Message-Id: <20200725041955.9985-7-warthog618@gmail.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20200725041955.9985-1-warthog618@gmail.com> References: <20200725041955.9985-1-warthog618@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org Add support for GPIO_GET_LINEINFO_V2_IOCTL and GPIO_GET_LINEINFO_WATCH_V2_IOCTL. Signed-off-by: Kent Gibson --- The core of this change is the event kfifo switching to contain struct gpioline_info_changed_v2, instead of V1 as V2 is richer. The two uAPI versions are mostly independent - other than where they both provide line info changes via reads on the chip fd. As the info change structs differ between V1 and V2, the infowatch implementation tracks which version of the infowatch ioctl, either GPIO_GET_LINEINFO_WATCH_IOCTL or GPIO_GET_LINEINFO_WATCH_V2_IOCTL, initiates the initial watch and returns the corresponding info change struct to the read. The version supported on that fd locks to that version on the first watch request, so subsequent watches from that process must use the same uAPI version. drivers/gpio/gpiolib-cdev.c | 187 +++++++++++++++++++++++++++++++----- 1 file changed, 164 insertions(+), 23 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 0908ae117b7d..1f282207fb70 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -1108,10 +1108,52 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) return ret; } +static void gpioline_info_v2_to_v1(struct gpioline_info_v2 *info_v2, + struct gpioline_info *info_v1) +{ + int flagsv2 = info_v2->flags; + + strncpy(info_v1->name, info_v2->name, sizeof(info_v1->name)); + strncpy(info_v1->consumer, info_v2->consumer, + sizeof(info_v1->consumer)); + info_v1->line_offset = info_v2->offset; + info_v1->flags = 0; + + if (flagsv2 & GPIOLINE_FLAG_V2_USED) + info_v1->flags |= GPIOLINE_FLAG_KERNEL; + + if (flagsv2 & GPIOLINE_FLAG_V2_OUTPUT) + info_v1->flags |= GPIOLINE_FLAG_IS_OUT; + + if (flagsv2 & GPIOLINE_FLAG_V2_ACTIVE_LOW) + info_v1->flags |= GPIOLINE_FLAG_ACTIVE_LOW; + + if (flagsv2 & GPIOLINE_FLAG_V2_OPEN_DRAIN) + info_v1->flags |= GPIOLINE_FLAG_OPEN_DRAIN; + if (flagsv2 & GPIOLINE_FLAG_V2_OPEN_SOURCE) + info_v1->flags |= GPIOLINE_FLAG_OPEN_SOURCE; + + if (flagsv2 & GPIOLINE_FLAG_V2_BIAS_PULL_UP) + info_v1->flags |= GPIOLINE_FLAG_BIAS_PULL_UP; + if (flagsv2 & GPIOLINE_FLAG_V2_BIAS_PULL_DOWN) + info_v1->flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN; + if (flagsv2 & GPIOLINE_FLAG_V2_BIAS_DISABLED) + info_v1->flags |= GPIOLINE_FLAG_BIAS_DISABLE; +} + +static void gpioline_info_changed_v2_to_v1( + struct gpioline_info_changed_v2 *lic_v2, + struct gpioline_info_changed *lic_v1) +{ + gpioline_info_v2_to_v1(&lic_v2->info, &lic_v1->info); + lic_v1->timestamp = lic_v2->timestamp; + lic_v1->event_type = lic_v2->event_type; +} + #endif /* CONFIG_GPIO_CDEV_V1 */ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, - struct gpioline_info *info) + struct gpioline_info_v2 *info) { struct gpio_chip *gc = desc->gdev->chip; bool ok_for_pinctrl; @@ -1125,7 +1167,7 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, * lock common to both frameworks? */ ok_for_pinctrl = - pinctrl_gpio_can_use_line(gc->base + info->line_offset); + pinctrl_gpio_can_use_line(gc->base + info->offset); spin_lock_irqsave(&gpio_lock, flags); @@ -1154,23 +1196,27 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, test_bit(FLAG_EXPORT, &desc->flags) || test_bit(FLAG_SYSFS, &desc->flags) || !ok_for_pinctrl) - info->flags |= GPIOLINE_FLAG_KERNEL; + info->flags |= GPIOLINE_FLAG_V2_USED; + if (test_bit(FLAG_IS_OUT, &desc->flags)) - info->flags |= GPIOLINE_FLAG_IS_OUT; + info->flags |= GPIOLINE_FLAG_V2_OUTPUT; + else + info->flags |= GPIOLINE_FLAG_V2_INPUT; + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) - info->flags |= GPIOLINE_FLAG_ACTIVE_LOW; + info->flags |= GPIOLINE_FLAG_V2_ACTIVE_LOW; + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) - info->flags |= (GPIOLINE_FLAG_OPEN_DRAIN | - GPIOLINE_FLAG_IS_OUT); + info->flags |= GPIOLINE_FLAG_V2_OPEN_DRAIN; if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) - info->flags |= (GPIOLINE_FLAG_OPEN_SOURCE | - GPIOLINE_FLAG_IS_OUT); + info->flags |= GPIOLINE_FLAG_V2_OPEN_SOURCE; + if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) - info->flags |= GPIOLINE_FLAG_BIAS_DISABLE; + info->flags |= GPIOLINE_FLAG_V2_BIAS_DISABLED; if (test_bit(FLAG_PULL_DOWN, &desc->flags)) - info->flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN; + info->flags |= GPIOLINE_FLAG_V2_BIAS_PULL_DOWN; if (test_bit(FLAG_PULL_UP, &desc->flags)) - info->flags |= GPIOLINE_FLAG_BIAS_PULL_UP; + info->flags |= GPIOLINE_FLAG_V2_BIAS_PULL_UP; spin_unlock_irqrestore(&gpio_lock, flags); } @@ -1178,11 +1224,65 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, struct gpio_chardev_data { struct gpio_device *gdev; wait_queue_head_t wait; - DECLARE_KFIFO(events, struct gpioline_info_changed, 32); + DECLARE_KFIFO(events, struct gpioline_info_changed_v2, 32); struct notifier_block lineinfo_changed_nb; unsigned long *watched_lines; +#ifdef CONFIG_GPIO_CDEV_V1 + atomic_t watch_abi_version; +#endif }; +#ifdef CONFIG_GPIO_CDEV_V1 +static int lineinfo_ensure_abi_version(struct gpio_chardev_data *cdata, + unsigned int version) +{ + int abiv = atomic_read(&cdata->watch_abi_version); + + if (abiv == 0) { + atomic_cmpxchg(&cdata->watch_abi_version, 0, version); + abiv = atomic_read(&cdata->watch_abi_version); + } + if (abiv != version) + return -EPERM; + return 0; +} +#endif + +static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip, + unsigned int cmd) +{ + struct gpio_desc *desc; + struct gpioline_info_v2 lineinfo; + + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + + if (padding_not_zeroed(lineinfo.padding, ARRAY_SIZE(lineinfo.padding))) + return -EINVAL; + + desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + if (cmd == GPIO_GET_LINEINFO_WATCH_V2_IOCTL) { +#ifdef CONFIG_GPIO_CDEV_V1 + if (lineinfo_ensure_abi_version(cdev, 2)) + return -EPERM; +#endif + if (test_and_set_bit(lineinfo.offset, cdev->watched_lines)) + return -EBUSY; + } + gpio_desc_to_lineinfo(desc, &lineinfo); + + if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) { + if (cmd == GPIO_GET_LINEINFO_WATCH_V2_IOCTL) + clear_bit(lineinfo.offset, cdev->watched_lines); + return -EFAULT; + } + + return 0; +} + /* * gpio_ioctl() - ioctl handler for the GPIO chardev */ @@ -1192,7 +1292,6 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct gpio_device *gdev = cdev->gdev; struct gpio_chip *gc = gdev->chip; void __user *ip = (void __user *)arg; - struct gpio_desc *desc; __u32 offset; /* We fail any subsequent ioctl():s when the chip is gone */ @@ -1217,7 +1316,9 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return 0; #ifdef CONFIG_GPIO_CDEV_V1 } else if (cmd == GPIO_GET_LINEINFO_IOCTL) { + struct gpio_desc *desc; struct gpioline_info lineinfo; + struct gpioline_info_v2 lineinfo_v2; if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) return -EFAULT; @@ -1227,7 +1328,9 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (IS_ERR(desc)) return PTR_ERR(desc); - gpio_desc_to_lineinfo(desc, &lineinfo); + gpio_desc_to_lineinfo(desc, &lineinfo_v2); + lineinfo_v2.offset = lineinfo.line_offset; + gpioline_info_v2_to_v1(&lineinfo_v2, &lineinfo); if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) return -EFAULT; @@ -1237,7 +1340,9 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) { return lineevent_create(gdev, ip); } else if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) { + struct gpio_desc *desc; struct gpioline_info lineinfo; + struct gpioline_info_v2 lineinfo_v2; if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) return -EFAULT; @@ -1247,10 +1352,15 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (IS_ERR(desc)) return PTR_ERR(desc); + if (lineinfo_ensure_abi_version(cdev, 1)) + return -EPERM; + if (test_and_set_bit(lineinfo.line_offset, cdev->watched_lines)) return -EBUSY; - gpio_desc_to_lineinfo(desc, &lineinfo); + gpio_desc_to_lineinfo(desc, &lineinfo_v2); + lineinfo_v2.offset = lineinfo.line_offset; + gpioline_info_v2_to_v1(&lineinfo_v2, &lineinfo); if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) { clear_bit(lineinfo.line_offset, cdev->watched_lines); @@ -1259,6 +1369,9 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return 0; #endif /* CONFIG_GPIO_CDEV_V1 */ + } else if (cmd == GPIO_GET_LINEINFO_V2_IOCTL || + cmd == GPIO_GET_LINEINFO_WATCH_V2_IOCTL) { + return lineinfo_get(cdev, ip, cmd); } else if (cmd == GPIO_GET_LINE_IOCTL) { return line_create(gdev, ip); } else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) { @@ -1294,7 +1407,7 @@ static int lineinfo_changed_notify(struct notifier_block *nb, unsigned long action, void *data) { struct gpio_chardev_data *cdev = to_gpio_chardev_data(nb); - struct gpioline_info_changed chg; + struct gpioline_info_changed_v2 chg; struct gpio_desc *desc = data; int ret; @@ -1302,7 +1415,7 @@ static int lineinfo_changed_notify(struct notifier_block *nb, return NOTIFY_DONE; memset(&chg, 0, sizeof(chg)); - chg.info.line_offset = gpio_chip_hwgpio(desc); + chg.info.offset = gpio_chip_hwgpio(desc); chg.event_type = action; chg.timestamp = ktime_get_ns(); gpio_desc_to_lineinfo(desc, &chg.info); @@ -1335,12 +1448,16 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, size_t count, loff_t *off) { struct gpio_chardev_data *cdev = file->private_data; - struct gpioline_info_changed event; + struct gpioline_info_changed_v2 event; ssize_t bytes_read = 0; int ret; + size_t event_size; - if (count < sizeof(event)) +#ifndef CONFIG_GPIO_CDEV_V1 + event_size = sizeof(struct gpioline_info_changed_v2); + if (count < event_size) return -EINVAL; +#endif do { spin_lock(&cdev->wait.lock); @@ -1362,7 +1479,17 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, return ret; } } - +#ifdef CONFIG_GPIO_CDEV_V1 + /* must be after kfifo check so watch_abi_version is set */ + if (atomic_read(&cdev->watch_abi_version) == 2) + event_size = sizeof(struct gpioline_info_changed_v2); + else + event_size = sizeof(struct gpioline_info_changed); + if (count < event_size) { + spin_unlock(&cdev->wait.lock); + return -EINVAL; + } +#endif ret = kfifo_out(&cdev->events, &event, 1); spin_unlock(&cdev->wait.lock); if (ret != 1) { @@ -1371,9 +1498,23 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, /* We should never get here. See lineevent_read(). */ } - if (copy_to_user(buf + bytes_read, &event, sizeof(event))) +#ifdef CONFIG_GPIO_CDEV_V1 + if (event_size == sizeof(struct gpioline_info_changed_v2)) { + if (copy_to_user(buf + bytes_read, &event, event_size)) + return -EFAULT; + } else { + struct gpioline_info_changed event_v1; + + gpioline_info_changed_v2_to_v1(&event, &event_v1); + if (copy_to_user(buf + bytes_read, &event_v1, + event_size)) + return -EFAULT; + } +#else + if (copy_to_user(buf + bytes_read, &event, event_size)) return -EFAULT; - bytes_read += sizeof(event); +#endif + bytes_read += event_size; } while (count >= bytes_read + sizeof(event)); return bytes_read; -- 2.27.0