aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/led.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-11-30 02:58:45 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-12-22 14:33:37 -0500
commite1e5406854378dfada3f33c7192b012083a5b8e0 (patch)
treee878058f28b8f6db50ef5d73d09aed66dd9ad9f2 /net/mac80211/led.c
parentfe67c913f1ec2a01aaa9176c80ef36eaf87d705d (diff)
mac80211: add throughput based LED blink trigger
iwlwifi and other drivers like to blink their LED based on throughput. Implement this generically in mac80211, based on a throughput table the driver specifies. That way, drivers can set the blink frequencies depending on their desired behaviour and max throughput. All the drivers need to do is provide an LED class device, best with blink hardware offload. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/led.c')
-rw-r--r--net/mac80211/led.c121
1 files changed, 121 insertions, 0 deletions
diff --git a/net/mac80211/led.c b/net/mac80211/led.c
index 740a1d4e0a9c..79b13090aed7 100644
--- a/net/mac80211/led.c
+++ b/net/mac80211/led.c
@@ -103,6 +103,13 @@ void ieee80211_led_init(struct ieee80211_local *local)
103 local->radio_led = NULL; 103 local->radio_led = NULL;
104 } 104 }
105 } 105 }
106
107 if (local->tpt_led_trigger) {
108 if (led_trigger_register(&local->tpt_led_trigger->trig)) {
109 kfree(local->tpt_led_trigger);
110 local->tpt_led_trigger = NULL;
111 }
112 }
106} 113}
107 114
108void ieee80211_led_exit(struct ieee80211_local *local) 115void ieee80211_led_exit(struct ieee80211_local *local)
@@ -123,6 +130,11 @@ void ieee80211_led_exit(struct ieee80211_local *local)
123 led_trigger_unregister(local->rx_led); 130 led_trigger_unregister(local->rx_led);
124 kfree(local->rx_led); 131 kfree(local->rx_led);
125 } 132 }
133
134 if (local->tpt_led_trigger) {
135 led_trigger_unregister(&local->tpt_led_trigger->trig);
136 kfree(local->tpt_led_trigger);
137 }
126} 138}
127 139
128char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw) 140char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
@@ -156,3 +168,112 @@ char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
156 return local->rx_led_name; 168 return local->rx_led_name;
157} 169}
158EXPORT_SYMBOL(__ieee80211_get_rx_led_name); 170EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
171
172static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
173 struct tpt_led_trigger *tpt_trig)
174{
175 unsigned long traffic, delta;
176
177 traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
178
179 delta = traffic - tpt_trig->prev_traffic;
180 tpt_trig->prev_traffic = traffic;
181 return DIV_ROUND_UP(delta, 1024 / 8);
182}
183
184static void tpt_trig_timer(unsigned long data)
185{
186 struct ieee80211_local *local = (void *)data;
187 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
188 struct led_classdev *led_cdev;
189 unsigned long on, off, tpt;
190 int i;
191
192 if (!tpt_trig->running)
193 return;
194
195 mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
196
197 tpt = tpt_trig_traffic(local, tpt_trig);
198
199 /* default to just solid on */
200 on = 1;
201 off = 0;
202
203 for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
204 if (tpt_trig->blink_table[i].throughput < 0 ||
205 tpt > tpt_trig->blink_table[i].throughput) {
206 off = tpt_trig->blink_table[i].blink_time / 2;
207 on = tpt_trig->blink_table[i].blink_time - off;
208 break;
209 }
210 }
211
212 read_lock(&tpt_trig->trig.leddev_list_lock);
213 list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
214 led_blink_set(led_cdev, &on, &off);
215 read_unlock(&tpt_trig->trig.leddev_list_lock);
216}
217
218extern char *__ieee80211_create_tpt_led_trigger(
219 struct ieee80211_hw *hw,
220 const struct ieee80211_tpt_blink *blink_table,
221 unsigned int blink_table_len)
222{
223 struct ieee80211_local *local = hw_to_local(hw);
224 struct tpt_led_trigger *tpt_trig;
225
226 if (WARN_ON(local->tpt_led_trigger))
227 return NULL;
228
229 tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
230 if (!tpt_trig)
231 return NULL;
232
233 snprintf(tpt_trig->name, sizeof(tpt_trig->name),
234 "%stpt", wiphy_name(local->hw.wiphy));
235
236 tpt_trig->trig.name = tpt_trig->name;
237
238 tpt_trig->blink_table = blink_table;
239 tpt_trig->blink_table_len = blink_table_len;
240
241 setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
242
243 local->tpt_led_trigger = tpt_trig;
244
245 return tpt_trig->name;
246}
247EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
248
249void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
250{
251 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
252
253 if (!tpt_trig)
254 return;
255
256 /* reset traffic */
257 tpt_trig_traffic(local, tpt_trig);
258 tpt_trig->running = true;
259
260 tpt_trig_timer((unsigned long)local);
261 mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
262}
263
264void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
265{
266 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
267 struct led_classdev *led_cdev;
268
269 if (!tpt_trig)
270 return;
271
272 tpt_trig->running = false;
273 del_timer_sync(&tpt_trig->timer);
274
275 read_lock(&tpt_trig->trig.leddev_list_lock);
276 list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
277 led_brightness_set(led_cdev, LED_OFF);
278 read_unlock(&tpt_trig->trig.leddev_list_lock);
279}