1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
--- a/compat/compat-2.6.37.c
+++ b/compat/compat-2.6.37.c
@@ -152,3 +152,175 @@ int compat_genl_unregister_family(struct
}
EXPORT_SYMBOL(compat_genl_unregister_family);
+#ifdef CONFIG_LEDS_CLASS
+
+#undef led_brightness_set
+#undef led_classdev_unregister
+
+spinlock_t led_lock;
+static LIST_HEAD(led_timers);
+
+struct led_timer {
+ struct list_head list;
+ struct led_classdev *cdev;
+ struct timer_list blink_timer;
+ unsigned long blink_delay_on;
+ unsigned long blink_delay_off;
+ int blink_brightness;
+};
+
+static void led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ led_cdev->brightness = brightness;
+ led_cdev->brightness_set(led_cdev, brightness);
+}
+
+static struct led_timer *led_get_timer(struct led_classdev *led_cdev)
+{
+ struct led_timer *p;
+ unsigned long flags;
+
+ spin_lock_irqsave(&led_lock, flags);
+ list_for_each_entry(p, &led_timers, list) {
+ if (p->cdev == led_cdev)
+ goto found;
+ }
+ p = NULL;
+found:
+ spin_unlock_irqrestore(&led_lock, flags);
+ return p;
+}
+
+static void led_stop_software_blink(struct led_timer *led)
+{
+ del_timer_sync(&led->blink_timer);
+ led->blink_delay_on = 0;
+ led->blink_delay_off = 0;
+}
+
+static void led_timer_function(unsigned long data)
+{
+ struct led_timer *led = (struct led_timer *)data;
+ unsigned long brightness;
+ unsigned long delay;
+
+ if (!led->blink_delay_on || !led->blink_delay_off) {
+ led->cdev->brightness_set(led->cdev, LED_OFF);
+ return;
+ }
+
+ brightness = led->cdev->brightness;
+ if (!brightness) {
+ /* Time to switch the LED on. */
+ brightness = led->blink_brightness;
+ delay = led->blink_delay_on;
+ } else {
+ /* Store the current brightness value to be able
+ * to restore it when the delay_off period is over.
+ */
+ led->blink_brightness = brightness;
+ brightness = LED_OFF;
+ delay = led->blink_delay_off;
+ }
+
+ led_brightness_set(led->cdev, brightness);
+ mod_timer(&led->blink_timer, jiffies + msecs_to_jiffies(delay));
+}
+
+static struct led_timer *led_new_timer(struct led_classdev *led_cdev)
+{
+ struct led_timer *led;
+ unsigned long flags;
+
+ led = kzalloc(sizeof(struct led_timer), GFP_ATOMIC);
+ if (!led)
+ return NULL;
+
+ led->cdev = led_cdev;
+ init_timer(&led->blink_timer);
+ led->blink_timer.function = led_timer_function;
+ led->blink_timer.data = (unsigned long) led;
+
+ spin_lock_irqsave(&led_lock, flags);
+ list_add(&led->list, &led_timers);
+ spin_unlock_irqrestore(&led_lock, flags);
+
+ return led;
+}
+
+void led_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct led_timer *led = led_get_timer(led_cdev);
+ int current_brightness;
+
+ if (!led) {
+ led = led_new_timer(led_cdev);
+ if (!led)
+ return;
+ }
+
+ /* blink with 1 Hz as default if nothing specified */
+ if (!*delay_on && !*delay_off)
+ *delay_on = *delay_off = 500;
+
+ if (led->blink_delay_on == *delay_on &&
+ led->blink_delay_off == *delay_off)
+ return;
+
+ current_brightness = led_cdev->brightness;
+ if (current_brightness)
+ led->blink_brightness = current_brightness;
+ if (!led->blink_brightness)
+ led->blink_brightness = led_cdev->max_brightness;
+
+ led_stop_software_blink(led);
+ led->blink_delay_on = *delay_on;
+ led->blink_delay_off = *delay_off;
+
+ /* never on - don't blink */
+ if (!*delay_on)
+ return;
+
+ /* never off - just set to brightness */
+ if (!*delay_off) {
+ led_brightness_set(led_cdev, led->blink_brightness);
+ return;
+ }
+
+ mod_timer(&led->blink_timer, jiffies + 1);
+}
+EXPORT_SYMBOL(led_blink_set);
+
+void compat_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct led_timer *led = led_get_timer(led_cdev);
+
+ if (led)
+ led_stop_software_blink(led);
+
+ return led_cdev->brightness_set(led_cdev, brightness);
+}
+EXPORT_SYMBOL(compat_led_brightness_set);
+
+void compat_led_classdev_unregister(struct led_classdev *led_cdev)
+{
+ struct led_timer *led = led_get_timer(led_cdev);
+ unsigned long flags;
+
+ if (led) {
+ del_timer_sync(&led->blink_timer);
+ spin_lock_irqsave(&led_lock, flags);
+ list_del(&led->list);
+ spin_unlock_irqrestore(&led_lock, flags);
+ kfree(led);
+ }
+
+ led_classdev_unregister(led_cdev);
+}
+EXPORT_SYMBOL(compat_led_classdev_unregister);
+
+#endif
--- a/include/linux/compat-2.6.37.h
+++ b/include/linux/compat-2.6.37.h
@@ -6,6 +6,7 @@
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37))
#include <linux/skbuff.h>
+#include <linux/leds.h>
#define SDIO_CLASS_BT_AMP 0x09 /* Type-A Bluetooth AMP interface */
@@ -93,6 +94,18 @@ int genl_unregister_family(struct genl_f
#define genl_register_mc_group(_fam, _grp) genl_register_mc_group(&(_fam)->family, _grp)
#define genl_unregister_mc_group(_fam, _grp) genl_unregister_mc_group(&(_fam)->family, _grp)
+
+extern void led_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off);
+
+#define led_classdev_unregister compat_led_classdev_unregister
+extern void led_classdev_unregister(struct led_classdev *led_cdev);
+
+#define led_brightness_set compat_led_brightness_set
+extern void led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness);
+
#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)) */
#endif /* LINUX_26_37_COMPAT_H */
|