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 "../leds.h" 23 24 static int panic_heartbeats; 25 26 struct heartbeat_trig_data { 27 unsigned int phase; 28 unsigned int period; 29 struct timer_list timer; 30 }; 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 39 if (unlikely(panic_heartbeats)) { 40 led_set_brightness(led_cdev, LED_OFF); 41 return; 42 } 43 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 brightness = led_cdev->max_brightness; 60 break; 61 case 1: 62 delay = heartbeat_data->period / 4 - msecs_to_jiffies(70); 63 heartbeat_data->phase++; 64 break; 65 case 2: 66 delay = msecs_to_jiffies(70); 67 heartbeat_data->phase++; 68 brightness = led_cdev->max_brightness; 69 break; 70 default: 71 delay = heartbeat_data->period - heartbeat_data->period / 4 - 72 msecs_to_jiffies(70); 73 heartbeat_data->phase = 0; 74 break; 75 } 76 77 __led_set_brightness(led_cdev, brightness); 78 mod_timer(&heartbeat_data->timer, jiffies + delay); 79 } 80 81 static void heartbeat_trig_activate(struct led_classdev *led_cdev) 82 { 83 struct heartbeat_trig_data *heartbeat_data; 84 85 heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL); 86 if (!heartbeat_data) 87 return; 88 89 led_cdev->trigger_data = heartbeat_data; 90 setup_timer(&heartbeat_data->timer, 91 led_heartbeat_function, (unsigned long) led_cdev); 92 heartbeat_data->phase = 0; 93 led_heartbeat_function(heartbeat_data->timer.data); 94 led_cdev->activated = true; 95 } 96 97 static void heartbeat_trig_deactivate(struct led_classdev *led_cdev) 98 { 99 struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data; 100 101 if (led_cdev->activated) { 102 del_timer_sync(&heartbeat_data->timer); 103 kfree(heartbeat_data); 104 led_cdev->activated = false; 105 } 106 } 107 108 static struct led_trigger heartbeat_led_trigger = { 109 .name = "heartbeat", 110 .activate = heartbeat_trig_activate, 111 .deactivate = heartbeat_trig_deactivate, 112 }; 113 114 static int heartbeat_reboot_notifier(struct notifier_block *nb, 115 unsigned long code, void *unused) 116 { 117 led_trigger_unregister(&heartbeat_led_trigger); 118 return NOTIFY_DONE; 119 } 120 121 static int heartbeat_panic_notifier(struct notifier_block *nb, 122 unsigned long code, void *unused) 123 { 124 panic_heartbeats = 1; 125 return NOTIFY_DONE; 126 } 127 128 static struct notifier_block heartbeat_reboot_nb = { 129 .notifier_call = heartbeat_reboot_notifier, 130 }; 131 132 static struct notifier_block heartbeat_panic_nb = { 133 .notifier_call = heartbeat_panic_notifier, 134 }; 135 136 static int __init heartbeat_trig_init(void) 137 { 138 int rc = led_trigger_register(&heartbeat_led_trigger); 139 140 if (!rc) { 141 atomic_notifier_chain_register(&panic_notifier_list, 142 &heartbeat_panic_nb); 143 register_reboot_notifier(&heartbeat_reboot_nb); 144 } 145 return rc; 146 } 147 148 static void __exit heartbeat_trig_exit(void) 149 { 150 unregister_reboot_notifier(&heartbeat_reboot_nb); 151 atomic_notifier_chain_unregister(&panic_notifier_list, 152 &heartbeat_panic_nb); 153 led_trigger_unregister(&heartbeat_led_trigger); 154 } 155 156 module_init(heartbeat_trig_init); 157 module_exit(heartbeat_trig_exit); 158 159 MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>"); 160 MODULE_DESCRIPTION("Heartbeat LED trigger"); 161 MODULE_LICENSE("GPL"); 162