ledtrig-heartbeat.c 6.0 KB

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