ledtrig-tty.c 4.1 KB

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