1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <linux/completion.h> 4 #include <linux/delay.h> 5 #include <linux/leds.h> 6 #include <linux/module.h> 7 #include <linux/slab.h> 8 #include <linux/tty.h> 9 #include <uapi/linux/serial.h> 10 11 #define LEDTRIG_TTY_INTERVAL 50 12 13 struct ledtrig_tty_data { 14 struct led_classdev *led_cdev; 15 struct delayed_work dwork; 16 struct completion sysfs; 17 const char *ttyname; 18 struct tty_struct *tty; 19 int rx, tx; 20 bool mode_rx; 21 bool mode_tx; 22 bool mode_cts; 23 bool mode_dsr; 24 bool mode_dcd; 25 bool mode_rng; 26 }; 27 28 /* Indicates which state the LED should now display */ 29 enum led_trigger_tty_state { 30 TTY_LED_BLINK, 31 TTY_LED_ENABLE, 32 TTY_LED_DISABLE, 33 }; 34 35 enum led_trigger_tty_modes { 36 TRIGGER_TTY_RX = 0, 37 TRIGGER_TTY_TX, 38 TRIGGER_TTY_CTS, 39 TRIGGER_TTY_DSR, 40 TRIGGER_TTY_DCD, 41 TRIGGER_TTY_RNG, 42 }; 43 44 static int ledtrig_tty_wait_for_completion(struct device *dev) 45 { 46 struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); 47 int ret; 48 49 ret = wait_for_completion_timeout(&trigger_data->sysfs, 50 msecs_to_jiffies(LEDTRIG_TTY_INTERVAL * 20)); 51 if (ret == 0) 52 return -ETIMEDOUT; 53 54 return ret; 55 } 56 57 static ssize_t ttyname_show(struct device *dev, 58 struct device_attribute *attr, char *buf) 59 { 60 struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); 61 ssize_t len = 0; 62 int completion; 63 64 reinit_completion(&trigger_data->sysfs); 65 completion = ledtrig_tty_wait_for_completion(dev); 66 if (completion < 0) 67 return completion; 68 69 if (trigger_data->ttyname) 70 len = sprintf(buf, "%s\n", trigger_data->ttyname); 71 72 return len; 73 } 74 75 static ssize_t ttyname_store(struct device *dev, 76 struct device_attribute *attr, const char *buf, 77 size_t size) 78 { 79 struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); 80 char *ttyname; 81 ssize_t ret = size; 82 int completion; 83 84 if (size > 0 && buf[size - 1] == '\n') 85 size -= 1; 86 87 if (size) { 88 ttyname = kmemdup_nul(buf, size, GFP_KERNEL); 89 if (!ttyname) 90 return -ENOMEM; 91 } else { 92 ttyname = NULL; 93 } 94 95 reinit_completion(&trigger_data->sysfs); 96 completion = ledtrig_tty_wait_for_completion(dev); 97 if (completion < 0) 98 return completion; 99 100 kfree(trigger_data->ttyname); 101 tty_kref_put(trigger_data->tty); 102 trigger_data->tty = NULL; 103 104 trigger_data->ttyname = ttyname; 105 106 return ret; 107 } 108 static DEVICE_ATTR_RW(ttyname); 109 110 static ssize_t ledtrig_tty_attr_show(struct device *dev, char *buf, 111 enum led_trigger_tty_modes attr) 112 { 113 struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); 114 bool state; 115 116 switch (attr) { 117 case TRIGGER_TTY_RX: 118 state = trigger_data->mode_rx; 119 break; 120 case TRIGGER_TTY_TX: 121 state = trigger_data->mode_tx; 122 break; 123 case TRIGGER_TTY_CTS: 124 state = trigger_data->mode_cts; 125 break; 126 case TRIGGER_TTY_DSR: 127 state = trigger_data->mode_dsr; 128 break; 129 case TRIGGER_TTY_DCD: 130 state = trigger_data->mode_dcd; 131 break; 132 case TRIGGER_TTY_RNG: 133 state = trigger_data->mode_rng; 134 break; 135 } 136 137 return sysfs_emit(buf, "%u\n", state); 138 } 139 140 static ssize_t ledtrig_tty_attr_store(struct device *dev, const char *buf, 141 size_t size, enum led_trigger_tty_modes attr) 142 { 143 struct ledtrig_tty_data *trigger_data = led_trigger_get_drvdata(dev); 144 bool state; 145 int ret; 146 147 ret = kstrtobool(buf, &state); 148 if (ret) 149 return ret; 150 151 switch (attr) { 152 case TRIGGER_TTY_RX: 153 trigger_data->mode_rx = state; 154 break; 155 case TRIGGER_TTY_TX: 156 trigger_data->mode_tx = state; 157 break; 158 case TRIGGER_TTY_CTS: 159 trigger_data->mode_cts = state; 160 break; 161 case TRIGGER_TTY_DSR: 162 trigger_data->mode_dsr = state; 163 break; 164 case TRIGGER_TTY_DCD: 165 trigger_data->mode_dcd = state; 166 break; 167 case TRIGGER_TTY_RNG: 168 trigger_data->mode_rng = state; 169 break; 170 } 171 172 return size; 173 } 174 175 #define DEFINE_TTY_TRIGGER(trigger_name, trigger) \ 176 static ssize_t trigger_name##_show(struct device *dev, \ 177 struct device_attribute *attr, char *buf) \ 178 { \ 179 return ledtrig_tty_attr_show(dev, buf, trigger); \ 180 } \ 181 static ssize_t trigger_name##_store(struct device *dev, \ 182 struct device_attribute *attr, const char *buf, size_t size) \ 183 { \ 184 return ledtrig_tty_attr_store(dev, buf, size, trigger); \ 185 } \ 186 static DEVICE_ATTR_RW(trigger_name) 187 188 DEFINE_TTY_TRIGGER(rx, TRIGGER_TTY_RX); 189 DEFINE_TTY_TRIGGER(tx, TRIGGER_TTY_TX); 190 DEFINE_TTY_TRIGGER(cts, TRIGGER_TTY_CTS); 191 DEFINE_TTY_TRIGGER(dsr, TRIGGER_TTY_DSR); 192 DEFINE_TTY_TRIGGER(dcd, TRIGGER_TTY_DCD); 193 DEFINE_TTY_TRIGGER(rng, TRIGGER_TTY_RNG); 194 195 static void ledtrig_tty_work(struct work_struct *work) 196 { 197 struct ledtrig_tty_data *trigger_data = 198 container_of(work, struct ledtrig_tty_data, dwork.work); 199 enum led_trigger_tty_state state = TTY_LED_DISABLE; 200 unsigned long interval = LEDTRIG_TTY_INTERVAL; 201 bool invert = false; 202 int status; 203 int ret; 204 205 if (!trigger_data->ttyname) 206 goto out; 207 208 /* try to get the tty corresponding to $ttyname */ 209 if (!trigger_data->tty) { 210 dev_t devno; 211 struct tty_struct *tty; 212 int ret; 213 214 ret = tty_dev_name_to_number(trigger_data->ttyname, &devno); 215 if (ret < 0) 216 /* 217 * A device with this name might appear later, so keep 218 * retrying. 219 */ 220 goto out; 221 222 tty = tty_kopen_shared(devno); 223 if (IS_ERR(tty) || !tty) 224 /* What to do? retry or abort */ 225 goto out; 226 227 trigger_data->tty = tty; 228 } 229 230 status = tty_get_tiocm(trigger_data->tty); 231 if (status > 0) { 232 if (trigger_data->mode_cts) { 233 if (status & TIOCM_CTS) 234 state = TTY_LED_ENABLE; 235 } 236 237 if (trigger_data->mode_dsr) { 238 if (status & TIOCM_DSR) 239 state = TTY_LED_ENABLE; 240 } 241 242 if (trigger_data->mode_dcd) { 243 if (status & TIOCM_CAR) 244 state = TTY_LED_ENABLE; 245 } 246 247 if (trigger_data->mode_rng) { 248 if (status & TIOCM_RNG) 249 state = TTY_LED_ENABLE; 250 } 251 } 252 253 /* 254 * The evaluation of rx/tx must be done after the evaluation 255 * of TIOCM_*, because rx/tx has priority. 256 */ 257 if (trigger_data->mode_rx || trigger_data->mode_tx) { 258 struct serial_icounter_struct icount; 259 260 ret = tty_get_icount(trigger_data->tty, &icount); 261 if (ret) 262 goto out; 263 264 if (trigger_data->mode_tx && (icount.tx != trigger_data->tx)) { 265 trigger_data->tx = icount.tx; 266 invert = state == TTY_LED_ENABLE; 267 state = TTY_LED_BLINK; 268 } 269 270 if (trigger_data->mode_rx && (icount.rx != trigger_data->rx)) { 271 trigger_data->rx = icount.rx; 272 invert = state == TTY_LED_ENABLE; 273 state = TTY_LED_BLINK; 274 } 275 } 276 277 out: 278 switch (state) { 279 case TTY_LED_BLINK: 280 led_blink_set_oneshot(trigger_data->led_cdev, &interval, 281 &interval, invert); 282 break; 283 case TTY_LED_ENABLE: 284 led_set_brightness(trigger_data->led_cdev, 285 trigger_data->led_cdev->blink_brightness); 286 break; 287 case TTY_LED_DISABLE: 288 fallthrough; 289 default: 290 led_set_brightness(trigger_data->led_cdev, LED_OFF); 291 break; 292 } 293 294 complete_all(&trigger_data->sysfs); 295 schedule_delayed_work(&trigger_data->dwork, 296 msecs_to_jiffies(LEDTRIG_TTY_INTERVAL * 2)); 297 } 298 299 static struct attribute *ledtrig_tty_attrs[] = { 300 &dev_attr_ttyname.attr, 301 &dev_attr_rx.attr, 302 &dev_attr_tx.attr, 303 &dev_attr_cts.attr, 304 &dev_attr_dsr.attr, 305 &dev_attr_dcd.attr, 306 &dev_attr_rng.attr, 307 NULL 308 }; 309 ATTRIBUTE_GROUPS(ledtrig_tty); 310 311 static int ledtrig_tty_activate(struct led_classdev *led_cdev) 312 { 313 struct ledtrig_tty_data *trigger_data; 314 315 trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL); 316 if (!trigger_data) 317 return -ENOMEM; 318 319 /* Enable default rx/tx mode */ 320 trigger_data->mode_rx = true; 321 trigger_data->mode_tx = true; 322 323 led_set_trigger_data(led_cdev, trigger_data); 324 325 INIT_DELAYED_WORK(&trigger_data->dwork, ledtrig_tty_work); 326 trigger_data->led_cdev = led_cdev; 327 init_completion(&trigger_data->sysfs); 328 329 schedule_delayed_work(&trigger_data->dwork, 0); 330 331 return 0; 332 } 333 334 static void ledtrig_tty_deactivate(struct led_classdev *led_cdev) 335 { 336 struct ledtrig_tty_data *trigger_data = led_get_trigger_data(led_cdev); 337 338 cancel_delayed_work_sync(&trigger_data->dwork); 339 340 kfree(trigger_data->ttyname); 341 tty_kref_put(trigger_data->tty); 342 trigger_data->tty = NULL; 343 344 kfree(trigger_data); 345 } 346 347 static struct led_trigger ledtrig_tty = { 348 .name = "tty", 349 .activate = ledtrig_tty_activate, 350 .deactivate = ledtrig_tty_deactivate, 351 .groups = ledtrig_tty_groups, 352 }; 353 module_led_trigger(ledtrig_tty); 354 355 MODULE_AUTHOR("Uwe Kleine-König <u.kleine-koenig@pengutronix.de>"); 356 MODULE_DESCRIPTION("UART LED trigger"); 357 MODULE_LICENSE("GPL v2"); 358