1#include <linux/kernel.h>
2#include <linux/module.h>
3#include <linux/init.h>
4#include <linux/proc_fs.h>
5#include <linux/string.h>
6
7#include <asm/auxio.h>
8
9#define LED_MAX_LENGTH 8 /* maximum chars written to proc file */
10
11static inline void led_toggle(void)
12{
13	unsigned char val = get_auxio();
14	unsigned char on, off;
15
16	if (val & AUXIO_LED) {
17		on = 0;
18		off = AUXIO_LED;
19	} else {
20		on = AUXIO_LED;
21		off = 0;
22	}
23
24	set_auxio(on, off);
25}
26
27static struct timer_list led_blink_timer;
28
29static void led_blink(unsigned long timeout)
30{
31	led_toggle();
32
33	/* reschedule */
34	if (!timeout) { /* blink according to load */
35		led_blink_timer.expires = jiffies +
36			((1 + (avenrun[0] >> FSHIFT)) * HZ);
37		led_blink_timer.data = 0;
38	} else { /* blink at user specified interval */
39		led_blink_timer.expires = jiffies + (timeout * HZ);
40		led_blink_timer.data = timeout;
41	}
42	add_timer(&led_blink_timer);
43}
44
45static int led_read_proc(char *buf, char **start, off_t offset, int count,
46			 int *eof, void *data)
47{
48	int len = 0;
49
50	if (get_auxio() & AUXIO_LED)
51		len = sprintf(buf, "on\n");
52	else
53		len = sprintf(buf, "off\n");
54
55	return len;
56}
57
58static int led_write_proc(struct file *file, const char __user *buffer,
59			  unsigned long count, void *data)
60{
61	char *buf = NULL;
62
63	if (count > LED_MAX_LENGTH)
64		count = LED_MAX_LENGTH;
65
66	buf = kmalloc(sizeof(char) * (count + 1), GFP_KERNEL);
67	if (!buf)
68		return -ENOMEM;
69
70	if (copy_from_user(buf, buffer, count)) {
71		kfree(buf);
72		return -EFAULT;
73	}
74
75	buf[count] = '\0';
76
77	if (buf[count - 1] == '\n')
78		buf[count - 1] = '\0';
79
80	/* before we change anything we want to stop any running timers,
81	 * otherwise calls such as on will have no persistent effect
82	 */
83	del_timer_sync(&led_blink_timer);
84
85	if (!strcmp(buf, "on")) {
86		auxio_set_led(AUXIO_LED_ON);
87	} else if (!strcmp(buf, "toggle")) {
88		led_toggle();
89	} else if ((*buf > '0') && (*buf <= '9')) {
90		led_blink(simple_strtoul(buf, NULL, 10));
91	} else if (!strcmp(buf, "load")) {
92		led_blink(0);
93	} else {
94		auxio_set_led(AUXIO_LED_OFF);
95	}
96
97	kfree(buf);
98
99	return count;
100}
101
102static struct proc_dir_entry *led;
103
104#define LED_VERSION	"0.1"
105
106static int __init led_init(void)
107{
108	init_timer(&led_blink_timer);
109	led_blink_timer.function = led_blink;
110
111	led = create_proc_entry("led", 0, NULL);
112	if (!led)
113		return -ENOMEM;
114
115	led->read_proc = led_read_proc; /* reader function */
116	led->write_proc = led_write_proc; /* writer function */
117	led->owner = THIS_MODULE;
118
119	printk(KERN_INFO
120	       "led: version %s, Lars Kotthoff <metalhead@metalhead.ws>\n",
121	       LED_VERSION);
122
123	return 0;
124}
125
126static void __exit led_exit(void)
127{
128	remove_proc_entry("led", NULL);
129	del_timer_sync(&led_blink_timer);
130}
131
132module_init(led_init);
133module_exit(led_exit);
134
135MODULE_AUTHOR("Lars Kotthoff <metalhead@metalhead.ws>");
136MODULE_DESCRIPTION("Provides control of the front LED on SPARC systems.");
137MODULE_LICENSE("GPL");
138MODULE_VERSION(LED_VERSION);
139