0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/module.h>
0012 #include <linux/kernel.h>
0013 #include <linux/init.h>
0014 #include <linux/panic_notifier.h>
0015 #include <linux/slab.h>
0016 #include <linux/timer.h>
0017 #include <linux/sched.h>
0018 #include <linux/sched/loadavg.h>
0019 #include <linux/leds.h>
0020 #include <linux/reboot.h>
0021 #include "../leds.h"
0022
0023 static int panic_heartbeats;
0024
0025 struct heartbeat_trig_data {
0026 struct led_classdev *led_cdev;
0027 unsigned int phase;
0028 unsigned int period;
0029 struct timer_list timer;
0030 unsigned int invert;
0031 };
0032
0033 static void led_heartbeat_function(struct timer_list *t)
0034 {
0035 struct heartbeat_trig_data *heartbeat_data =
0036 from_timer(heartbeat_data, t, timer);
0037 struct led_classdev *led_cdev;
0038 unsigned long brightness = LED_OFF;
0039 unsigned long delay = 0;
0040
0041 led_cdev = heartbeat_data->led_cdev;
0042
0043 if (unlikely(panic_heartbeats)) {
0044 led_set_brightness_nosleep(led_cdev, LED_OFF);
0045 return;
0046 }
0047
0048 if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags))
0049 led_cdev->blink_brightness = led_cdev->new_blink_brightness;
0050
0051
0052 switch (heartbeat_data->phase) {
0053 case 0:
0054
0055
0056
0057
0058
0059
0060 heartbeat_data->period = 300 +
0061 (6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
0062 heartbeat_data->period =
0063 msecs_to_jiffies(heartbeat_data->period);
0064 delay = msecs_to_jiffies(70);
0065 heartbeat_data->phase++;
0066 if (!heartbeat_data->invert)
0067 brightness = led_cdev->blink_brightness;
0068 break;
0069 case 1:
0070 delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
0071 heartbeat_data->phase++;
0072 if (heartbeat_data->invert)
0073 brightness = led_cdev->blink_brightness;
0074 break;
0075 case 2:
0076 delay = msecs_to_jiffies(70);
0077 heartbeat_data->phase++;
0078 if (!heartbeat_data->invert)
0079 brightness = led_cdev->blink_brightness;
0080 break;
0081 default:
0082 delay = heartbeat_data->period - heartbeat_data->period / 4 -
0083 msecs_to_jiffies(70);
0084 heartbeat_data->phase = 0;
0085 if (heartbeat_data->invert)
0086 brightness = led_cdev->blink_brightness;
0087 break;
0088 }
0089
0090 led_set_brightness_nosleep(led_cdev, brightness);
0091 mod_timer(&heartbeat_data->timer, jiffies + delay);
0092 }
0093
0094 static ssize_t led_invert_show(struct device *dev,
0095 struct device_attribute *attr, char *buf)
0096 {
0097 struct heartbeat_trig_data *heartbeat_data =
0098 led_trigger_get_drvdata(dev);
0099
0100 return sprintf(buf, "%u\n", heartbeat_data->invert);
0101 }
0102
0103 static ssize_t led_invert_store(struct device *dev,
0104 struct device_attribute *attr, const char *buf, size_t size)
0105 {
0106 struct heartbeat_trig_data *heartbeat_data =
0107 led_trigger_get_drvdata(dev);
0108 unsigned long state;
0109 int ret;
0110
0111 ret = kstrtoul(buf, 0, &state);
0112 if (ret)
0113 return ret;
0114
0115 heartbeat_data->invert = !!state;
0116
0117 return size;
0118 }
0119
0120 static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
0121
0122 static struct attribute *heartbeat_trig_attrs[] = {
0123 &dev_attr_invert.attr,
0124 NULL
0125 };
0126 ATTRIBUTE_GROUPS(heartbeat_trig);
0127
0128 static int heartbeat_trig_activate(struct led_classdev *led_cdev)
0129 {
0130 struct heartbeat_trig_data *heartbeat_data;
0131
0132 heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL);
0133 if (!heartbeat_data)
0134 return -ENOMEM;
0135
0136 led_set_trigger_data(led_cdev, heartbeat_data);
0137 heartbeat_data->led_cdev = led_cdev;
0138
0139 timer_setup(&heartbeat_data->timer, led_heartbeat_function, 0);
0140 heartbeat_data->phase = 0;
0141 if (!led_cdev->blink_brightness)
0142 led_cdev->blink_brightness = led_cdev->max_brightness;
0143 led_heartbeat_function(&heartbeat_data->timer);
0144 set_bit(LED_BLINK_SW, &led_cdev->work_flags);
0145
0146 return 0;
0147 }
0148
0149 static void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
0150 {
0151 struct heartbeat_trig_data *heartbeat_data =
0152 led_get_trigger_data(led_cdev);
0153
0154 del_timer_sync(&heartbeat_data->timer);
0155 kfree(heartbeat_data);
0156 clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
0157 }
0158
0159 static struct led_trigger heartbeat_led_trigger = {
0160 .name = "heartbeat",
0161 .activate = heartbeat_trig_activate,
0162 .deactivate = heartbeat_trig_deactivate,
0163 .groups = heartbeat_trig_groups,
0164 };
0165
0166 static int heartbeat_reboot_notifier(struct notifier_block *nb,
0167 unsigned long code, void *unused)
0168 {
0169 led_trigger_unregister(&heartbeat_led_trigger);
0170 return NOTIFY_DONE;
0171 }
0172
0173 static int heartbeat_panic_notifier(struct notifier_block *nb,
0174 unsigned long code, void *unused)
0175 {
0176 panic_heartbeats = 1;
0177 return NOTIFY_DONE;
0178 }
0179
0180 static struct notifier_block heartbeat_reboot_nb = {
0181 .notifier_call = heartbeat_reboot_notifier,
0182 };
0183
0184 static struct notifier_block heartbeat_panic_nb = {
0185 .notifier_call = heartbeat_panic_notifier,
0186 };
0187
0188 static int __init heartbeat_trig_init(void)
0189 {
0190 int rc = led_trigger_register(&heartbeat_led_trigger);
0191
0192 if (!rc) {
0193 atomic_notifier_chain_register(&panic_notifier_list,
0194 &heartbeat_panic_nb);
0195 register_reboot_notifier(&heartbeat_reboot_nb);
0196 }
0197 return rc;
0198 }
0199
0200 static void __exit heartbeat_trig_exit(void)
0201 {
0202 unregister_reboot_notifier(&heartbeat_reboot_nb);
0203 atomic_notifier_chain_unregister(&panic_notifier_list,
0204 &heartbeat_panic_nb);
0205 led_trigger_unregister(&heartbeat_led_trigger);
0206 }
0207
0208 module_init(heartbeat_trig_init);
0209 module_exit(heartbeat_trig_exit);
0210
0211 MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
0212 MODULE_DESCRIPTION("Heartbeat LED trigger");
0213 MODULE_LICENSE("GPL v2");