ledtrig-heartbeat.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. * LED Heartbeat Trigger
  3. *
  4. * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
  5. *
  6. * Based on Richard Purdie's ledtrig-timer.c and some arch's
  7. * CONFIG_HEARTBEAT code.
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License version 2 as
  11. * published by the Free Software Foundation.
  12. *
  13. */
  14. #include <linux/module.h>
  15. #include <linux/kernel.h>
  16. #include <linux/init.h>
  17. #include <linux/slab.h>
  18. #include <linux/timer.h>
  19. #include <linux/sched.h>
  20. #include <linux/sched/loadavg.h>
  21. #include <linux/leds.h>
  22. #include <linux/reboot.h>
  23. #include <linux/suspend.h>
  24. #include "../leds.h"
  25. static int panic_heartbeats;
  26. struct heartbeat_trig_data {
  27. unsigned int phase;
  28. unsigned int period;
  29. struct timer_list timer;
  30. unsigned int invert;
  31. };
  32. static void led_heartbeat_function(unsigned long data)
  33. {
  34. struct led_classdev *led_cdev = (struct led_classdev *) data;
  35. struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
  36. unsigned long brightness = LED_OFF;
  37. unsigned long delay = 0;
  38. if (unlikely(panic_heartbeats)) {
  39. led_set_brightness_nosleep(led_cdev, LED_OFF);
  40. return;
  41. }
  42. if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags))
  43. led_cdev->blink_brightness = led_cdev->new_blink_brightness;
  44. /* acts like an actual heart beat -- ie thump-thump-pause... */
  45. switch (heartbeat_data->phase) {
  46. case 0:
  47. /*
  48. * The hyperbolic function below modifies the
  49. * heartbeat period length in dependency of the
  50. * current (1min) load. It goes through the points
  51. * f(0)=1260, f(1)=860, f(5)=510, f(inf)->300.
  52. */
  53. heartbeat_data->period = 300 +
  54. (6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
  55. heartbeat_data->period =
  56. msecs_to_jiffies(heartbeat_data->period);
  57. delay = msecs_to_jiffies(70);
  58. heartbeat_data->phase++;
  59. if (!heartbeat_data->invert)
  60. brightness = led_cdev->blink_brightness;
  61. break;
  62. case 1:
  63. delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
  64. heartbeat_data->phase++;
  65. if (heartbeat_data->invert)
  66. brightness = led_cdev->blink_brightness;
  67. break;
  68. case 2:
  69. delay = msecs_to_jiffies(70);
  70. heartbeat_data->phase++;
  71. if (!heartbeat_data->invert)
  72. brightness = led_cdev->blink_brightness;
  73. break;
  74. default:
  75. delay = heartbeat_data->period - heartbeat_data->period / 4 -
  76. msecs_to_jiffies(70);
  77. heartbeat_data->phase = 0;
  78. if (heartbeat_data->invert)
  79. brightness = led_cdev->blink_brightness;
  80. break;
  81. }
  82. led_set_brightness_nosleep(led_cdev, brightness);
  83. mod_timer(&heartbeat_data->timer, jiffies + delay);
  84. }
  85. static ssize_t led_invert_show(struct device *dev,
  86. struct device_attribute *attr, char *buf)
  87. {
  88. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  89. struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
  90. return sprintf(buf, "%u\n", heartbeat_data->invert);
  91. }
  92. static ssize_t led_invert_store(struct device *dev,
  93. struct device_attribute *attr, const char *buf, size_t size)
  94. {
  95. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  96. struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
  97. unsigned long state;
  98. int ret;
  99. ret = kstrtoul(buf, 0, &state);
  100. if (ret)
  101. return ret;
  102. heartbeat_data->invert = !!state;
  103. return size;
  104. }
  105. static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
  106. static void heartbeat_trig_activate(struct led_classdev *led_cdev)
  107. {
  108. struct heartbeat_trig_data *heartbeat_data;
  109. int rc;
  110. heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL);
  111. if (!heartbeat_data)
  112. return;
  113. led_cdev->trigger_data = heartbeat_data;
  114. rc = device_create_file(led_cdev->dev, &dev_attr_invert);
  115. if (rc) {
  116. kfree(led_cdev->trigger_data);
  117. return;
  118. }
  119. setup_timer(&heartbeat_data->timer,
  120. led_heartbeat_function, (unsigned long) led_cdev);
  121. heartbeat_data->phase = 0;
  122. if (!led_cdev->blink_brightness)
  123. led_cdev->blink_brightness = led_cdev->max_brightness;
  124. led_heartbeat_function(heartbeat_data->timer.data);
  125. set_bit(LED_BLINK_SW, &led_cdev->work_flags);
  126. led_cdev->activated = true;
  127. }
  128. static void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
  129. {
  130. struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
  131. if (led_cdev->activated) {
  132. del_timer_sync(&heartbeat_data->timer);
  133. device_remove_file(led_cdev->dev, &dev_attr_invert);
  134. kfree(heartbeat_data);
  135. clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
  136. led_cdev->activated = false;
  137. }
  138. }
  139. static struct led_trigger heartbeat_led_trigger = {
  140. .name = "heartbeat",
  141. .activate = heartbeat_trig_activate,
  142. .deactivate = heartbeat_trig_deactivate,
  143. };
  144. static int heartbeat_pm_notifier(struct notifier_block *nb,
  145. unsigned long pm_event, void *unused)
  146. {
  147. int rc;
  148. switch (pm_event) {
  149. case PM_SUSPEND_PREPARE:
  150. case PM_HIBERNATION_PREPARE:
  151. case PM_RESTORE_PREPARE:
  152. led_trigger_unregister(&heartbeat_led_trigger);
  153. break;
  154. case PM_POST_SUSPEND:
  155. case PM_POST_HIBERNATION:
  156. case PM_POST_RESTORE:
  157. rc = led_trigger_register(&heartbeat_led_trigger);
  158. if (rc)
  159. pr_err("could not re-register heartbeat trigger\n");
  160. break;
  161. default:
  162. break;
  163. }
  164. return NOTIFY_DONE;
  165. }
  166. static int heartbeat_reboot_notifier(struct notifier_block *nb,
  167. unsigned long code, void *unused)
  168. {
  169. led_trigger_unregister(&heartbeat_led_trigger);
  170. return NOTIFY_DONE;
  171. }
  172. static int heartbeat_panic_notifier(struct notifier_block *nb,
  173. unsigned long code, void *unused)
  174. {
  175. panic_heartbeats = 1;
  176. return NOTIFY_DONE;
  177. }
  178. static struct notifier_block heartbeat_pm_nb = {
  179. .notifier_call = heartbeat_pm_notifier,
  180. };
  181. static struct notifier_block heartbeat_reboot_nb = {
  182. .notifier_call = heartbeat_reboot_notifier,
  183. };
  184. static struct notifier_block heartbeat_panic_nb = {
  185. .notifier_call = heartbeat_panic_notifier,
  186. };
  187. static int __init heartbeat_trig_init(void)
  188. {
  189. int rc = led_trigger_register(&heartbeat_led_trigger);
  190. if (!rc) {
  191. atomic_notifier_chain_register(&panic_notifier_list,
  192. &heartbeat_panic_nb);
  193. register_reboot_notifier(&heartbeat_reboot_nb);
  194. register_pm_notifier(&heartbeat_pm_nb);
  195. }
  196. return rc;
  197. }
  198. static void __exit heartbeat_trig_exit(void)
  199. {
  200. unregister_pm_notifier(&heartbeat_pm_nb);
  201. unregister_reboot_notifier(&heartbeat_reboot_nb);
  202. atomic_notifier_chain_unregister(&panic_notifier_list,
  203. &heartbeat_panic_nb);
  204. led_trigger_unregister(&heartbeat_led_trigger);
  205. }
  206. module_init(heartbeat_trig_init);
  207. module_exit(heartbeat_trig_exit);
  208. MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
  209. MODULE_DESCRIPTION("Heartbeat LED trigger");
  210. MODULE_LICENSE("GPL");