1 // SPDX-License-Identifier: GPL-2.0
3 #include <linux/delay.h>
4 #include <linux/leds.h>
5 #include <linux/module.h>
6 #include <linux/slab.h>
8 #include <uapi/linux/serial.h>
10 #define LEDTRIG_TTY_INTERVAL 50
12 struct ledtrig_tty_data {
13 struct led_classdev *led_cdev;
14 struct delayed_work dwork;
17 struct tty_struct *tty;
21 static void ledtrig_tty_restart(struct ledtrig_tty_data *trigger_data)
23 schedule_delayed_work(&trigger_data->dwork, 0);
26 static ssize_t ttyname_show(struct device *dev,
27 struct device_attribute *attr, char *buf)
29 struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev);
32 mutex_lock(&trigger_data->mutex);
34 if (trigger_data->ttyname)
35 len = sprintf(buf, "%s\n", trigger_data->ttyname);
37 mutex_unlock(&trigger_data->mutex);
42 static ssize_t ttyname_store(struct device *dev,
43 struct device_attribute *attr, const char *buf,
46 struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev);
51 if (size > 0 && buf[size - 1] == '\n')
55 ttyname = kmemdup_nul(buf, size, GFP_KERNEL);
62 mutex_lock(&trigger_data->mutex);
64 running = trigger_data->ttyname != NULL;
66 kfree(trigger_data->ttyname);
67 tty_kref_put(trigger_data->tty);
68 trigger_data->tty = NULL;
70 trigger_data->ttyname = ttyname;
72 mutex_unlock(&trigger_data->mutex);
74 if (ttyname && !running)
75 ledtrig_tty_restart(trigger_data);
79 static DEVICE_ATTR_RW(ttyname);
81 static void ledtrig_tty_work(struct work_struct *work)
83 struct ledtrig_tty_data *trigger_data =
84 container_of(work, struct ledtrig_tty_data, dwork.work);
85 struct serial_icounter_struct icount;
88 mutex_lock(&trigger_data->mutex);
90 if (!trigger_data->ttyname) {
91 /* exit without rescheduling */
92 mutex_unlock(&trigger_data->mutex);
96 /* try to get the tty corresponding to $ttyname */
97 if (!trigger_data->tty) {
99 struct tty_struct *tty;
102 ret = tty_dev_name_to_number(trigger_data->ttyname, &devno);
105 * A device with this name might appear later, so keep
110 tty = tty_kopen_shared(devno);
111 if (IS_ERR(tty) || !tty)
112 /* What to do? retry or abort */
115 trigger_data->tty = tty;
118 ret = tty_get_icount(trigger_data->tty, &icount);
120 dev_info(trigger_data->tty->dev, "Failed to get icount, stopped polling\n");
121 mutex_unlock(&trigger_data->mutex);
125 if (icount.rx != trigger_data->rx ||
126 icount.tx != trigger_data->tx) {
127 unsigned long interval = LEDTRIG_TTY_INTERVAL;
129 led_blink_set_oneshot(trigger_data->led_cdev, &interval,
132 trigger_data->rx = icount.rx;
133 trigger_data->tx = icount.tx;
137 mutex_unlock(&trigger_data->mutex);
138 schedule_delayed_work(&trigger_data->dwork,
139 msecs_to_jiffies(LEDTRIG_TTY_INTERVAL * 2));
142 static struct attribute *ledtrig_tty_attrs[] = {
143 &dev_attr_ttyname.attr,
146 ATTRIBUTE_GROUPS(ledtrig_tty);
148 static int ledtrig_tty_activate(struct led_classdev *led_cdev)
150 struct ledtrig_tty_data *trigger_data;
152 trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL);
156 led_set_trigger_data(led_cdev, trigger_data);
158 INIT_DELAYED_WORK(&trigger_data->dwork, ledtrig_tty_work);
159 trigger_data->led_cdev = led_cdev;
160 mutex_init(&trigger_data->mutex);
165 static void ledtrig_tty_deactivate(struct led_classdev *led_cdev)
167 struct ledtrig_tty_data *trigger_data = led_get_trigger_data(led_cdev);
169 cancel_delayed_work_sync(&trigger_data->dwork);
171 kfree(trigger_data->ttyname);
172 tty_kref_put(trigger_data->tty);
173 trigger_data->tty = NULL;
178 static struct led_trigger ledtrig_tty = {
180 .activate = ledtrig_tty_activate,
181 .deactivate = ledtrig_tty_deactivate,
182 .groups = ledtrig_tty_groups,
184 module_led_trigger(ledtrig_tty);
186 MODULE_AUTHOR("Uwe Kleine-König <u.kleine-koenig@pengutronix.de>");
187 MODULE_DESCRIPTION("UART LED trigger");
188 MODULE_LICENSE("GPL v2");