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/timer.h>
18#include <linux/sched.h>
19#include <linux/leds.h>
20#include "leds.h"
21
22struct heartbeat_trig_data {
23	unsigned int phase;
24	unsigned int period;
25	struct timer_list timer;
26};
27
28static void led_heartbeat_function(unsigned long data)
29{
30	struct led_classdev *led_cdev = (struct led_classdev *) data;
31	struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
32	unsigned long brightness = LED_OFF;
33	unsigned long delay = 0;
34
35	/* acts like an actual heart beat -- ie thump-thump-pause... */
36	switch (heartbeat_data->phase) {
37	case 0:
38		/*
39		 * The hyperbolic function below modifies the
40		 * heartbeat period length in dependency of the
41		 * current (1min) load. It goes through the points
42		 * f(0)=1260, f(1)=860, f(5)=510, f(inf)->300.
43		 */
44		heartbeat_data->period = 300 +
45			(6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
46		heartbeat_data->period =
47			msecs_to_jiffies(heartbeat_data->period);
48		delay = msecs_to_jiffies(70);
49		heartbeat_data->phase++;
50		brightness = LED_FULL;
51		break;
52	case 1:
53		delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
54		heartbeat_data->phase++;
55		break;
56	case 2:
57		delay = msecs_to_jiffies(70);
58		heartbeat_data->phase++;
59		brightness = LED_FULL;
60		break;
61	default:
62		delay = heartbeat_data->period - heartbeat_data->period / 4 -
63			msecs_to_jiffies(70);
64		heartbeat_data->phase = 0;
65		break;
66	}
67
68	led_set_brightness(led_cdev, brightness);
69	mod_timer(&heartbeat_data->timer, jiffies + delay);
70}
71
72static void heartbeat_trig_activate(struct led_classdev *led_cdev)
73{
74	struct heartbeat_trig_data *heartbeat_data;
75
76	heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL);
77	if (!heartbeat_data)
78		return;
79
80	led_cdev->trigger_data = heartbeat_data;
81	setup_timer(&heartbeat_data->timer,
82		    led_heartbeat_function, (unsigned long) led_cdev);
83	heartbeat_data->phase = 0;
84	led_heartbeat_function(heartbeat_data->timer.data);
85}
86
87static void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
88{
89	struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
90
91	if (heartbeat_data) {
92		del_timer_sync(&heartbeat_data->timer);
93		kfree(heartbeat_data);
94	}
95}
96
97static struct led_trigger heartbeat_led_trigger = {
98	.name     = "heartbeat",
99	.activate = heartbeat_trig_activate,
100	.deactivate = heartbeat_trig_deactivate,
101};
102
103static int __init heartbeat_trig_init(void)
104{
105	return led_trigger_register(&heartbeat_led_trigger);
106}
107
108static void __exit heartbeat_trig_exit(void)
109{
110	led_trigger_unregister(&heartbeat_led_trigger);
111}
112
113module_init(heartbeat_trig_init);
114module_exit(heartbeat_trig_exit);
115
116MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
117MODULE_DESCRIPTION("Heartbeat LED trigger");
118MODULE_LICENSE("GPL");
119