1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <linux/delay.h> 4 #include <linux/leds.h> 5 #include <linux/module.h> 6 #include <linux/slab.h> 7 #include <linux/tty.h> 8 #include <uapi/linux/serial.h> 9 10 #define LEDTRIG_TTY_INTERVAL 50 11 12 struct ledtrig_tty_data { 13 struct led_classdev *led_cdev; 14 struct delayed_work dwork; 15 struct mutex mutex; 16 const char *ttyname; 17 struct tty_struct *tty; 18 int rx, tx; 19 }; 20 21 static void ledtrig_tty_restart(struct ledtrig_tty_data *trigger_data) 22 { 23 schedule_delayed_work(&trigger_data->dwork, 0); 24 } 25 26 static ssize_t ttyname_show(struct device *dev, 27 struct device_attribute *attr, char *buf) 28 { 29 struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); 30 ssize_t len = 0; 31 32 mutex_lock(&trigger_data->mutex); 33 34 if (trigger_data->ttyname) 35 len = sprintf(buf, "%s\n", trigger_data->ttyname); 36 37 mutex_unlock(&trigger_data->mutex); 38 39 return len; 40 } 41 42 static ssize_t ttyname_store(struct device *dev, 43 struct device_attribute *attr, const char *buf, 44 size_t size) 45 { 46 struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); 47 char *ttyname; 48 ssize_t ret = size; 49 bool running; 50 51 if (size > 0 && buf[size - 1] == '\n') 52 size -= 1; 53 54 if (size) { 55 ttyname = kmemdup_nul(buf, size, GFP_KERNEL); 56 if (!ttyname) 57 return -ENOMEM; 58 } else { 59 ttyname = NULL; 60 } 61 62 mutex_lock(&trigger_data->mutex); 63 64 running = trigger_data->ttyname != NULL; 65 66 kfree(trigger_data->ttyname); 67 tty_kref_put(trigger_data->tty); 68 trigger_data->tty = NULL; 69 70 trigger_data->ttyname = ttyname; 71 72 mutex_unlock(&trigger_data->mutex); 73 74 if (ttyname && !running) 75 ledtrig_tty_restart(trigger_data); 76 77 return ret; 78 } 79 static DEVICE_ATTR_RW(ttyname); 80 81 static void ledtrig_tty_work(struct work_struct *work) 82 { 83 struct ledtrig_tty_data *trigger_data = 84 container_of(work, struct ledtrig_tty_data, dwork.work); 85 struct serial_icounter_struct icount; 86 int ret; 87 88 mutex_lock(&trigger_data->mutex); 89 90 if (!trigger_data->ttyname) { 91 /* exit without rescheduling */ 92 mutex_unlock(&trigger_data->mutex); 93 return; 94 } 95 96 /* try to get the tty corresponding to $ttyname */ 97 if (!trigger_data->tty) { 98 dev_t devno; 99 struct tty_struct *tty; 100 int ret; 101 102 ret = tty_dev_name_to_number(trigger_data->ttyname, &devno); 103 if (ret < 0) 104 /* 105 * A device with this name might appear later, so keep 106 * retrying. 107 */ 108 goto out; 109 110 tty = tty_kopen_shared(devno); 111 if (IS_ERR(tty) || !tty) 112 /* What to do? retry or abort */ 113 goto out; 114 115 trigger_data->tty = tty; 116 } 117 118 ret = tty_get_icount(trigger_data->tty, &icount); 119 if (ret) { 120 dev_info(trigger_data->tty->dev, "Failed to get icount, stopped polling\n"); 121 mutex_unlock(&trigger_data->mutex); 122 return; 123 } 124 125 if (icount.rx != trigger_data->rx || 126 icount.tx != trigger_data->tx) { 127 unsigned long interval = LEDTRIG_TTY_INTERVAL; 128 129 led_blink_set_oneshot(trigger_data->led_cdev, &interval, 130 &interval, 0); 131 132 trigger_data->rx = icount.rx; 133 trigger_data->tx = icount.tx; 134 } 135 136 out: 137 mutex_unlock(&trigger_data->mutex); 138 schedule_delayed_work(&trigger_data->dwork, 139 msecs_to_jiffies(LEDTRIG_TTY_INTERVAL * 2)); 140 } 141 142 static struct attribute *ledtrig_tty_attrs[] = { 143 &dev_attr_ttyname.attr, 144 NULL 145 }; 146 ATTRIBUTE_GROUPS(ledtrig_tty); 147 148 static int ledtrig_tty_activate(struct led_classdev *led_cdev) 149 { 150 struct ledtrig_tty_data *trigger_data; 151 152 trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL); 153 if (!trigger_data) 154 return -ENOMEM; 155 156 led_set_trigger_data(led_cdev, trigger_data); 157 158 INIT_DELAYED_WORK(&trigger_data->dwork, ledtrig_tty_work); 159 trigger_data->led_cdev = led_cdev; 160 mutex_init(&trigger_data->mutex); 161 162 return 0; 163 } 164 165 static void ledtrig_tty_deactivate(struct led_classdev *led_cdev) 166 { 167 struct ledtrig_tty_data *trigger_data = led_get_trigger_data(led_cdev); 168 169 cancel_delayed_work_sync(&trigger_data->dwork); 170 171 kfree(trigger_data); 172 } 173 174 static struct led_trigger ledtrig_tty = { 175 .name = "tty", 176 .activate = ledtrig_tty_activate, 177 .deactivate = ledtrig_tty_deactivate, 178 .groups = ledtrig_tty_groups, 179 }; 180 module_led_trigger(ledtrig_tty); 181 182 MODULE_AUTHOR("Uwe Kleine-König <u.kleine-koenig@pengutronix.de>"); 183 MODULE_DESCRIPTION("UART LED trigger"); 184 MODULE_LICENSE("GPL v2"); 185