]>
Commit | Line | Data |
---|---|---|
ee1858d3 LK |
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 | ||
11 | static 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 | ||
27 | static struct timer_list led_blink_timer; | |
28 | ||
29 | static 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 | ||
45 | static 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 | ||
58 | static int led_write_proc(struct file *file, const char *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 | /* work around \n when echo'ing into proc */ | |
78 | if (buf[count - 1] == '\n') | |
79 | buf[count - 1] = '\0'; | |
80 | ||
81 | /* before we change anything we want to stop any running timers, | |
82 | * otherwise calls such as on will have no persistent effect | |
83 | */ | |
84 | del_timer_sync(&led_blink_timer); | |
85 | ||
86 | if (!strcmp(buf, "on")) { | |
87 | auxio_set_led(AUXIO_LED_ON); | |
88 | } else if (!strcmp(buf, "toggle")) { | |
89 | led_toggle(); | |
90 | } else if ((*buf > '0') && (*buf <= '9')) { | |
91 | led_blink(simple_strtoul(buf, NULL, 10)); | |
92 | } else if (!strcmp(buf, "load")) { | |
93 | led_blink(0); | |
94 | } else { | |
95 | auxio_set_led(AUXIO_LED_OFF); | |
96 | } | |
97 | ||
98 | kfree(buf); | |
99 | ||
100 | return count; | |
101 | } | |
102 | ||
103 | static struct proc_dir_entry *led; | |
104 | ||
105 | #define LED_VERSION "0.1" | |
106 | ||
107 | static int __init led_init(void) | |
108 | { | |
109 | init_timer(&led_blink_timer); | |
110 | led_blink_timer.function = led_blink; | |
111 | ||
112 | led = create_proc_entry("led", 0, NULL); | |
113 | if (!led) | |
114 | return -ENOMEM; | |
115 | ||
116 | led->read_proc = led_read_proc; /* reader function */ | |
117 | led->write_proc = led_write_proc; /* writer function */ | |
118 | led->owner = THIS_MODULE; | |
119 | ||
120 | printk(KERN_INFO | |
121 | "led: version %s, Lars Kotthoff <metalhead@metalhead.ws>\n", | |
122 | LED_VERSION); | |
123 | ||
124 | return 0; | |
125 | } | |
126 | ||
127 | static void __exit led_exit(void) | |
128 | { | |
129 | remove_proc_entry("led", NULL); | |
130 | del_timer_sync(&led_blink_timer); | |
131 | } | |
132 | ||
133 | module_init(led_init); | |
134 | module_exit(led_exit); | |
135 | ||
136 | MODULE_AUTHOR("Lars Kotthoff <metalhead@metalhead.ws>"); | |
137 | MODULE_DESCRIPTION("Provides control of the front LED on SPARC systems."); | |
138 | MODULE_LICENSE("GPL"); | |
139 | MODULE_VERSION(LED_VERSION); |