From patchwork Fri Dec 18 10:42:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= X-Patchwork-Id: 345842 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=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham 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 740F0C2BBCF for ; Fri, 18 Dec 2020 10:44:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 483E523A5B for ; Fri, 18 Dec 2020 10:44:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389175AbgLRKoM (ORCPT ); Fri, 18 Dec 2020 05:44:12 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53546 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2389170AbgLRKoM (ORCPT ); Fri, 18 Dec 2020 05:44:12 -0500 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 41F15C061248 for ; Fri, 18 Dec 2020 02:42:55 -0800 (PST) Received: from ptx.hi.pengutronix.de ([2001:67c:670:100:1d::c0]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kqDDk-00006w-RO; Fri, 18 Dec 2020 11:42:48 +0100 Received: from ukl by ptx.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1kqDDj-0004hQ-Pd; Fri, 18 Dec 2020 11:42:47 +0100 From: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= To: Jacek Anaszewski , Pavel Machek , Dan Murphy , Greg Kroah-Hartman , Jiri Slaby Cc: linux-serial@vger.kernel.org, linux-leds@vger.kernel.org, linux-kernel@vger.kernel.org, kernel@pengutronix.de, Johan Hovold Subject: [PATCH v10 1/3] tty: rename tty_kopen() and add new function tty_kopen_shared() Date: Fri, 18 Dec 2020 11:42:44 +0100 Message-Id: <20201218104246.591315-2-u.kleine-koenig@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20201218104246.591315-1-u.kleine-koenig@pengutronix.de> References: <20201218104246.591315-1-u.kleine-koenig@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::c0 X-SA-Exim-Mail-From: ukl@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-serial@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-serial@vger.kernel.org Introduce a new function tty_kopen_shared() that yields a struct tty_struct. The semantic difference to tty_kopen() is that the tty is expected to be used already. So rename tty_kopen() to tty_kopen_exclusive() for clearness, adapt the single user and put the common code in a new static helper function. tty_kopen_shared is to be used to implement an LED trigger for tty devices in one of the next patches. Signed-off-by: Uwe Kleine-König --- drivers/accessibility/speakup/spk_ttyio.c | 2 +- drivers/tty/tty_io.c | 56 +++++++++++++++-------- include/linux/tty.h | 5 +- 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/drivers/accessibility/speakup/spk_ttyio.c b/drivers/accessibility/speakup/spk_ttyio.c index 6284aff434a1..835d17455fcd 100644 --- a/drivers/accessibility/speakup/spk_ttyio.c +++ b/drivers/accessibility/speakup/spk_ttyio.c @@ -152,7 +152,7 @@ static int spk_ttyio_initialise_ldisc(struct spk_synth *synth) if (ret) return ret; - tty = tty_kopen(dev); + tty = tty_kopen_exclusive(dev); if (IS_ERR(tty)) return PTR_ERR(tty); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 56ade99ef99f..d0c1f97adc17 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1873,22 +1873,7 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, return driver; } -/** - * tty_kopen - open a tty device for kernel - * @device: dev_t of device to open - * - * Opens tty exclusively for kernel. Performs the driver lookup, - * makes sure it's not already opened and performs the first-time - * tty initialization. - * - * Returns the locked initialized &tty_struct - * - * Claims the global tty_mutex to serialize: - * - concurrent first-time tty initialization - * - concurrent tty driver removal w/ lookup - * - concurrent tty removal from driver table - */ -struct tty_struct *tty_kopen(dev_t device) +static struct tty_struct *tty_kopen(dev_t device, int shared) { struct tty_struct *tty; struct tty_driver *driver; @@ -1903,7 +1888,7 @@ struct tty_struct *tty_kopen(dev_t device) /* check whether we're reopening an existing tty */ tty = tty_driver_lookup_tty(driver, NULL, index); - if (IS_ERR(tty)) + if (IS_ERR(tty) || shared) goto out; if (tty) { @@ -1921,7 +1906,42 @@ struct tty_struct *tty_kopen(dev_t device) tty_driver_kref_put(driver); return tty; } -EXPORT_SYMBOL_GPL(tty_kopen); + +/** + * tty_kopen_exclusive - open a tty device for kernel + * @device: dev_t of device to open + * + * Opens tty exclusively for kernel. Performs the driver lookup, + * makes sure it's not already opened and performs the first-time + * tty initialization. + * + * Returns the locked initialized &tty_struct + * + * Claims the global tty_mutex to serialize: + * - concurrent first-time tty initialization + * - concurrent tty driver removal w/ lookup + * - concurrent tty removal from driver table + */ +struct tty_struct *tty_kopen_exclusive(dev_t device) +{ + return tty_kopen(device, 0); +} +EXPORT_SYMBOL_GPL(tty_kopen_exclusive); + +/** + * tty_kopen_shared - open a tty device for shared in-kernel use + * @device: dev_t of device to open + * + * Opens an already existing tty for in-kernel use. Compared to + * tty_kopen_exclusive() above it doesn't ensure to be the only user. + * + * Locking is identical to tty_kopen() above. + */ +struct tty_struct *tty_kopen_shared(dev_t device) +{ + return tty_kopen(device, 1); +} +EXPORT_SYMBOL_GPL(tty_kopen_shared); /** * tty_open_by_driver - open a tty device diff --git a/include/linux/tty.h b/include/linux/tty.h index eb33d948788c..7c3b5f156f03 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -417,7 +417,8 @@ extern struct tty_struct *get_current_tty(void); /* tty_io.c */ extern int __init tty_init(void); extern const char *tty_name(const struct tty_struct *tty); -extern struct tty_struct *tty_kopen(dev_t device); +extern struct tty_struct *tty_kopen_exclusive(dev_t device); +extern struct tty_struct *tty_kopen_shared(dev_t device); extern void tty_kclose(struct tty_struct *tty); extern int tty_dev_name_to_number(const char *name, dev_t *number); extern int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout); @@ -442,7 +443,7 @@ static inline int __init tty_init(void) { return 0; } static inline const char *tty_name(const struct tty_struct *tty) { return "(none)"; } -static inline struct tty_struct *tty_kopen(dev_t device) +static inline struct tty_struct *tty_kopen_exclusive(dev_t device) { return ERR_PTR(-ENODEV); } static inline void tty_kclose(struct tty_struct *tty) { } From patchwork Fri Dec 18 10:42:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= X-Patchwork-Id: 346154 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=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham 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 02C58C4361B for ; Fri, 18 Dec 2020 10:44:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CFEA023A7B for ; Fri, 18 Dec 2020 10:44:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389170AbgLRKoN (ORCPT ); Fri, 18 Dec 2020 05:44:13 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53552 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2389171AbgLRKoM (ORCPT ); Fri, 18 Dec 2020 05:44:12 -0500 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 48054C061257 for ; Fri, 18 Dec 2020 02:42:55 -0800 (PST) Received: from ptx.hi.pengutronix.de ([2001:67c:670:100:1d::c0]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kqDDk-00006x-RO; Fri, 18 Dec 2020 11:42:48 +0100 Received: from ukl by ptx.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1kqDDk-0004hT-21; Fri, 18 Dec 2020 11:42:48 +0100 From: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= To: Jacek Anaszewski , Pavel Machek , Dan Murphy , Greg Kroah-Hartman , Jiri Slaby Cc: linux-serial@vger.kernel.org, linux-leds@vger.kernel.org, linux-kernel@vger.kernel.org, kernel@pengutronix.de, Johan Hovold Subject: [PATCH v10 2/3] tty: new helper function tty_get_icount() Date: Fri, 18 Dec 2020 11:42:45 +0100 Message-Id: <20201218104246.591315-3-u.kleine-koenig@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20201218104246.591315-1-u.kleine-koenig@pengutronix.de> References: <20201218104246.591315-1-u.kleine-koenig@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::c0 X-SA-Exim-Mail-From: ukl@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-serial@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-serial@vger.kernel.org For a given struct tty_struct this yields the corresponding statistics about sent and received characters (and some more) which is needed to implement an LED trigger for tty devices. The new function is then used to simplify tty_tiocgicount(). Reviewed-by: Pavel Machek Signed-off-by: Uwe Kleine-König --- drivers/tty/tty_io.c | 29 +++++++++++++++++++++++++---- include/linux/tty.h | 2 ++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index d0c1f97adc17..61b179f2ae42 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2486,15 +2486,36 @@ static int tty_tiocmset(struct tty_struct *tty, unsigned int cmd, return tty->ops->tiocmset(tty, set, clear); } +/** + * tty_get_icount - get tty statistics + * @tty: tty device + * @icount: output parameter + * + * Gets a copy of the tty's icount statistics. + * + * Locking: none (up to the driver) + */ +int tty_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + memset(icount, 0, sizeof(*icount)); + + if (tty->ops->get_icount) + return tty->ops->get_icount(tty, icount); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(tty_get_icount); + static int tty_tiocgicount(struct tty_struct *tty, void __user *arg) { - int retval = -EINVAL; struct serial_icounter_struct icount; - memset(&icount, 0, sizeof(icount)); - if (tty->ops->get_icount) - retval = tty->ops->get_icount(tty, &icount); + int retval; + + retval = tty_get_icount(tty, &icount); if (retval != 0) return retval; + if (copy_to_user(arg, &icount, sizeof(icount))) return -EFAULT; return 0; diff --git a/include/linux/tty.h b/include/linux/tty.h index 7c3b5f156f03..3c689e9cd494 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -501,6 +501,8 @@ extern void tty_unthrottle(struct tty_struct *tty); extern int tty_throttle_safe(struct tty_struct *tty); extern int tty_unthrottle_safe(struct tty_struct *tty); extern int tty_do_resize(struct tty_struct *tty, struct winsize *ws); +extern int tty_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount); extern int is_current_pgrp_orphaned(void); extern void tty_hangup(struct tty_struct *tty); extern void tty_vhangup(struct tty_struct *tty); From patchwork Fri Dec 18 10:42:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= X-Patchwork-Id: 346155 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=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, 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 D2BA5C2D0E4 for ; Fri, 18 Dec 2020 10:43:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A2BAB23AA7 for ; Fri, 18 Dec 2020 10:43:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389129AbgLRKnh (ORCPT ); Fri, 18 Dec 2020 05:43:37 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53448 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2389126AbgLRKng (ORCPT ); Fri, 18 Dec 2020 05:43:36 -0500 Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [IPv6:2001:67c:670:201:290:27ff:fe1d:cc33]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 34587C061282 for ; Fri, 18 Dec 2020 02:42:55 -0800 (PST) Received: from ptx.hi.pengutronix.de ([2001:67c:670:100:1d::c0]) by metis.ext.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kqDDk-00006y-RO; Fri, 18 Dec 2020 11:42:48 +0100 Received: from ukl by ptx.hi.pengutronix.de with local (Exim 4.92) (envelope-from ) id 1kqDDk-0004hW-8X; Fri, 18 Dec 2020 11:42:48 +0100 From: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= To: Jacek Anaszewski , Pavel Machek , Dan Murphy , Greg Kroah-Hartman , Jiri Slaby Cc: linux-serial@vger.kernel.org, linux-leds@vger.kernel.org, linux-kernel@vger.kernel.org, kernel@pengutronix.de, Johan Hovold Subject: [PATCH v10 3/3] leds: trigger: implement a tty trigger Date: Fri, 18 Dec 2020 11:42:46 +0100 Message-Id: <20201218104246.591315-4-u.kleine-koenig@pengutronix.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20201218104246.591315-1-u.kleine-koenig@pengutronix.de> References: <20201218104246.591315-1-u.kleine-koenig@pengutronix.de> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 2001:67c:670:100:1d::c0 X-SA-Exim-Mail-From: ukl@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-serial@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-serial@vger.kernel.org Usage is as follows: myled=ledname tty=ttyS0 echo tty > /sys/class/leds/$myled/trigger echo $tty > /sys/class/leds/$myled/ttyname . When this new trigger is active it periodically checks the tty's statistics and when it changed since the last check the led is flashed once. Signed-off-by: Uwe Kleine-König --- .../ABI/testing/sysfs-class-led-trigger-tty | 6 + drivers/leds/trigger/Kconfig | 9 + drivers/leds/trigger/Makefile | 1 + drivers/leds/trigger/ledtrig-tty.c | 188 ++++++++++++++++++ 4 files changed, 204 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-led-trigger-tty create mode 100644 drivers/leds/trigger/ledtrig-tty.c diff --git a/Documentation/ABI/testing/sysfs-class-led-trigger-tty b/Documentation/ABI/testing/sysfs-class-led-trigger-tty new file mode 100644 index 000000000000..2bf6b24e781b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-led-trigger-tty @@ -0,0 +1,6 @@ +What: /sys/class/leds//ttyname +Date: Dec 2020 +KernelVersion: 5.10 +Contact: linux-leds@vger.kernel.org +Description: + Specifies the tty device name of the triggering tty diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index ce9429ca6dde..b77a01bd27f4 100644 --- a/drivers/leds/trigger/Kconfig +++ b/drivers/leds/trigger/Kconfig @@ -144,4 +144,13 @@ config LEDS_TRIGGER_AUDIO the audio mute and mic-mute changes. If unsure, say N +config LEDS_TRIGGER_TTY + tristate "LED Trigger for TTY devices" + depends on TTY + help + This allows LEDs to be controlled by activity on ttys which includes + serial devices like /dev/ttyS0. + + When build as a module this driver will be called ledtrig-tty. + endif # LEDS_TRIGGERS diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile index 733a83e2a718..25c4db97cdd4 100644 --- a/drivers/leds/trigger/Makefile +++ b/drivers/leds/trigger/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o obj-$(CONFIG_LEDS_TRIGGER_PATTERN) += ledtrig-pattern.o obj-$(CONFIG_LEDS_TRIGGER_AUDIO) += ledtrig-audio.o +obj-$(CONFIG_LEDS_TRIGGER_TTY) += ledtrig-tty.o diff --git a/drivers/leds/trigger/ledtrig-tty.c b/drivers/leds/trigger/ledtrig-tty.c new file mode 100644 index 000000000000..c1e87c0d23c3 --- /dev/null +++ b/drivers/leds/trigger/ledtrig-tty.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include + +struct ledtrig_tty_data { + struct led_classdev *led_cdev; + struct delayed_work dwork; + struct mutex mutex; + const char *ttyname; + struct tty_struct *tty; + int rx, tx; +}; + +static void ledtrig_tty_halt(struct ledtrig_tty_data *trigger_data) +{ + cancel_delayed_work_sync(&trigger_data->dwork); +} + +static void ledtrig_tty_restart(struct ledtrig_tty_data *trigger_data) +{ + schedule_delayed_work(&trigger_data->dwork, 0); +} + +static ssize_t ttyname_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); + ssize_t len = 0; + + mutex_lock(&trigger_data->mutex); + + if (trigger_data->ttyname) + len = sprintf(buf, "%s\n", trigger_data->ttyname); + + mutex_unlock(&trigger_data->mutex); + + return len; +} + +static ssize_t ttyname_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); + char *ttyname; + ssize_t ret = size; + bool running; + + if (size > 0 && buf[size - 1] == '\n') + size -= 1; + + if (size) { + ttyname = kmemdup_nul(buf, size, GFP_KERNEL); + if (!ttyname) { + ret = -ENOMEM; + goto out_unlock; + } + } else { + ttyname = NULL; + } + + mutex_lock(&trigger_data->mutex); + + running = trigger_data->ttyname != NULL; + + kfree(trigger_data->ttyname); + tty_kref_put(trigger_data->tty); + trigger_data->tty = NULL; + + trigger_data->ttyname = ttyname; + +out_unlock: + mutex_unlock(&trigger_data->mutex); + + if (ttyname && !running) + ledtrig_tty_restart(trigger_data); + + return ret; +} +static DEVICE_ATTR_RW(ttyname); + +static void ledtrig_tty_work(struct work_struct *work) +{ + struct ledtrig_tty_data *trigger_data = + container_of(work, struct ledtrig_tty_data, dwork.work); + struct serial_icounter_struct icount; + int ret; + + mutex_lock(&trigger_data->mutex); + + if (!trigger_data->ttyname) { + /* exit without rescheduling */ + mutex_unlock(&trigger_data->mutex); + return; + } + + /* try to get the tty corresponding to $ttyname */ + if (!trigger_data->tty) { + dev_t devno; + struct tty_struct *tty; + int ret; + + ret = tty_dev_name_to_number(trigger_data->ttyname, &devno); + if (ret < 0) + /* + * A device with this name might appear later, so keep + * retrying. + */ + goto out; + + tty = tty_kopen_shared(devno); + if (IS_ERR(tty) || !tty) + /* What to do? retry or abort */ + goto out; + + trigger_data->tty = tty; + } + + ret = tty_get_icount(trigger_data->tty, &icount); + if (ret) { + dev_info(trigger_data->tty->dev, "Failed to get icount, stopped polling\n"); + mutex_unlock(&trigger_data->mutex); + return; + } + + if (icount.rx != trigger_data->rx || + icount.tx != trigger_data->tx) { + led_set_brightness(trigger_data->led_cdev, LED_ON); + + trigger_data->rx = icount.rx; + trigger_data->tx = icount.tx; + } else { + led_set_brightness(trigger_data->led_cdev, LED_OFF); + } + +out: + mutex_unlock(&trigger_data->mutex); + schedule_delayed_work(&trigger_data->dwork, msecs_to_jiffies(100)); +} + +static struct attribute *ledtrig_tty_attrs[] = { + &dev_attr_ttyname.attr, + NULL +}; +ATTRIBUTE_GROUPS(ledtrig_tty); + +static int ledtrig_tty_activate(struct led_classdev *led_cdev) +{ + struct ledtrig_tty_data *trigger_data; + + trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL); + if (!trigger_data) + return -ENOMEM; + + led_set_trigger_data(led_cdev, trigger_data); + + INIT_DELAYED_WORK(&trigger_data->dwork, ledtrig_tty_work); + trigger_data->led_cdev = led_cdev; + mutex_init(&trigger_data->mutex); + + return 0; +} + +static void ledtrig_tty_deactivate(struct led_classdev *led_cdev) +{ + struct ledtrig_tty_data *trigger_data = led_get_trigger_data(led_cdev); + + cancel_delayed_work_sync(&trigger_data->dwork); + + kfree(trigger_data); +} + +static struct led_trigger ledtrig_tty = { + .name = "tty", + .activate = ledtrig_tty_activate, + .deactivate = ledtrig_tty_deactivate, + .groups = ledtrig_tty_groups, +}; +module_led_trigger(ledtrig_tty); + +MODULE_AUTHOR("Uwe Kleine-König "); +MODULE_DESCRIPTION("UART LED trigger"); +MODULE_LICENSE("GPL v2");