1 /* 2 * Copyright 2006, Johannes Berg <johannes@sipsolutions.net> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9 /* just for IFNAMSIZ */ 10 #include <linux/if.h> 11 #include <linux/slab.h> 12 #include <linux/export.h> 13 #include "led.h" 14 15 void ieee80211_led_assoc(struct ieee80211_local *local, bool associated) 16 { 17 if (!atomic_read(&local->assoc_led_active)) 18 return; 19 if (associated) 20 led_trigger_event(&local->assoc_led, LED_FULL); 21 else 22 led_trigger_event(&local->assoc_led, LED_OFF); 23 } 24 25 void ieee80211_led_radio(struct ieee80211_local *local, bool enabled) 26 { 27 if (!atomic_read(&local->radio_led_active)) 28 return; 29 if (enabled) 30 led_trigger_event(&local->radio_led, LED_FULL); 31 else 32 led_trigger_event(&local->radio_led, LED_OFF); 33 } 34 35 void ieee80211_alloc_led_names(struct ieee80211_local *local) 36 { 37 local->rx_led.name = kasprintf(GFP_KERNEL, "%srx", 38 wiphy_name(local->hw.wiphy)); 39 local->tx_led.name = kasprintf(GFP_KERNEL, "%stx", 40 wiphy_name(local->hw.wiphy)); 41 local->assoc_led.name = kasprintf(GFP_KERNEL, "%sassoc", 42 wiphy_name(local->hw.wiphy)); 43 local->radio_led.name = kasprintf(GFP_KERNEL, "%sradio", 44 wiphy_name(local->hw.wiphy)); 45 } 46 47 void ieee80211_free_led_names(struct ieee80211_local *local) 48 { 49 kfree(local->rx_led.name); 50 kfree(local->tx_led.name); 51 kfree(local->assoc_led.name); 52 kfree(local->radio_led.name); 53 } 54 55 static int ieee80211_tx_led_activate(struct led_classdev *led_cdev) 56 { 57 struct ieee80211_local *local = container_of(led_cdev->trigger, 58 struct ieee80211_local, 59 tx_led); 60 61 atomic_inc(&local->tx_led_active); 62 63 return 0; 64 } 65 66 static void ieee80211_tx_led_deactivate(struct led_classdev *led_cdev) 67 { 68 struct ieee80211_local *local = container_of(led_cdev->trigger, 69 struct ieee80211_local, 70 tx_led); 71 72 atomic_dec(&local->tx_led_active); 73 } 74 75 static int ieee80211_rx_led_activate(struct led_classdev *led_cdev) 76 { 77 struct ieee80211_local *local = container_of(led_cdev->trigger, 78 struct ieee80211_local, 79 rx_led); 80 81 atomic_inc(&local->rx_led_active); 82 83 return 0; 84 } 85 86 static void ieee80211_rx_led_deactivate(struct led_classdev *led_cdev) 87 { 88 struct ieee80211_local *local = container_of(led_cdev->trigger, 89 struct ieee80211_local, 90 rx_led); 91 92 atomic_dec(&local->rx_led_active); 93 } 94 95 static int ieee80211_assoc_led_activate(struct led_classdev *led_cdev) 96 { 97 struct ieee80211_local *local = container_of(led_cdev->trigger, 98 struct ieee80211_local, 99 assoc_led); 100 101 atomic_inc(&local->assoc_led_active); 102 103 return 0; 104 } 105 106 static void ieee80211_assoc_led_deactivate(struct led_classdev *led_cdev) 107 { 108 struct ieee80211_local *local = container_of(led_cdev->trigger, 109 struct ieee80211_local, 110 assoc_led); 111 112 atomic_dec(&local->assoc_led_active); 113 } 114 115 static int ieee80211_radio_led_activate(struct led_classdev *led_cdev) 116 { 117 struct ieee80211_local *local = container_of(led_cdev->trigger, 118 struct ieee80211_local, 119 radio_led); 120 121 atomic_inc(&local->radio_led_active); 122 123 return 0; 124 } 125 126 static void ieee80211_radio_led_deactivate(struct led_classdev *led_cdev) 127 { 128 struct ieee80211_local *local = container_of(led_cdev->trigger, 129 struct ieee80211_local, 130 radio_led); 131 132 atomic_dec(&local->radio_led_active); 133 } 134 135 static int ieee80211_tpt_led_activate(struct led_classdev *led_cdev) 136 { 137 struct ieee80211_local *local = container_of(led_cdev->trigger, 138 struct ieee80211_local, 139 tpt_led); 140 141 atomic_inc(&local->tpt_led_active); 142 143 return 0; 144 } 145 146 static void ieee80211_tpt_led_deactivate(struct led_classdev *led_cdev) 147 { 148 struct ieee80211_local *local = container_of(led_cdev->trigger, 149 struct ieee80211_local, 150 tpt_led); 151 152 atomic_dec(&local->tpt_led_active); 153 } 154 155 void ieee80211_led_init(struct ieee80211_local *local) 156 { 157 atomic_set(&local->rx_led_active, 0); 158 local->rx_led.activate = ieee80211_rx_led_activate; 159 local->rx_led.deactivate = ieee80211_rx_led_deactivate; 160 if (local->rx_led.name && led_trigger_register(&local->rx_led)) { 161 kfree(local->rx_led.name); 162 local->rx_led.name = NULL; 163 } 164 165 atomic_set(&local->tx_led_active, 0); 166 local->tx_led.activate = ieee80211_tx_led_activate; 167 local->tx_led.deactivate = ieee80211_tx_led_deactivate; 168 if (local->tx_led.name && led_trigger_register(&local->tx_led)) { 169 kfree(local->tx_led.name); 170 local->tx_led.name = NULL; 171 } 172 173 atomic_set(&local->assoc_led_active, 0); 174 local->assoc_led.activate = ieee80211_assoc_led_activate; 175 local->assoc_led.deactivate = ieee80211_assoc_led_deactivate; 176 if (local->assoc_led.name && led_trigger_register(&local->assoc_led)) { 177 kfree(local->assoc_led.name); 178 local->assoc_led.name = NULL; 179 } 180 181 atomic_set(&local->radio_led_active, 0); 182 local->radio_led.activate = ieee80211_radio_led_activate; 183 local->radio_led.deactivate = ieee80211_radio_led_deactivate; 184 if (local->radio_led.name && led_trigger_register(&local->radio_led)) { 185 kfree(local->radio_led.name); 186 local->radio_led.name = NULL; 187 } 188 189 atomic_set(&local->tpt_led_active, 0); 190 if (local->tpt_led_trigger) { 191 local->tpt_led.activate = ieee80211_tpt_led_activate; 192 local->tpt_led.deactivate = ieee80211_tpt_led_deactivate; 193 if (led_trigger_register(&local->tpt_led)) { 194 kfree(local->tpt_led_trigger); 195 local->tpt_led_trigger = NULL; 196 } 197 } 198 } 199 200 void ieee80211_led_exit(struct ieee80211_local *local) 201 { 202 if (local->radio_led.name) 203 led_trigger_unregister(&local->radio_led); 204 if (local->assoc_led.name) 205 led_trigger_unregister(&local->assoc_led); 206 if (local->tx_led.name) 207 led_trigger_unregister(&local->tx_led); 208 if (local->rx_led.name) 209 led_trigger_unregister(&local->rx_led); 210 211 if (local->tpt_led_trigger) { 212 led_trigger_unregister(&local->tpt_led); 213 kfree(local->tpt_led_trigger); 214 } 215 } 216 217 const char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw) 218 { 219 struct ieee80211_local *local = hw_to_local(hw); 220 221 return local->radio_led.name; 222 } 223 EXPORT_SYMBOL(__ieee80211_get_radio_led_name); 224 225 const char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw) 226 { 227 struct ieee80211_local *local = hw_to_local(hw); 228 229 return local->assoc_led.name; 230 } 231 EXPORT_SYMBOL(__ieee80211_get_assoc_led_name); 232 233 const char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw) 234 { 235 struct ieee80211_local *local = hw_to_local(hw); 236 237 return local->tx_led.name; 238 } 239 EXPORT_SYMBOL(__ieee80211_get_tx_led_name); 240 241 const char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw) 242 { 243 struct ieee80211_local *local = hw_to_local(hw); 244 245 return local->rx_led.name; 246 } 247 EXPORT_SYMBOL(__ieee80211_get_rx_led_name); 248 249 static unsigned long tpt_trig_traffic(struct ieee80211_local *local, 250 struct tpt_led_trigger *tpt_trig) 251 { 252 unsigned long traffic, delta; 253 254 traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes; 255 256 delta = traffic - tpt_trig->prev_traffic; 257 tpt_trig->prev_traffic = traffic; 258 return DIV_ROUND_UP(delta, 1024 / 8); 259 } 260 261 static void tpt_trig_timer(struct timer_list *t) 262 { 263 struct tpt_led_trigger *tpt_trig = from_timer(tpt_trig, t, timer); 264 struct ieee80211_local *local = tpt_trig->local; 265 struct led_classdev *led_cdev; 266 unsigned long on, off, tpt; 267 int i; 268 269 if (!tpt_trig->running) 270 return; 271 272 mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ)); 273 274 tpt = tpt_trig_traffic(local, tpt_trig); 275 276 /* default to just solid on */ 277 on = 1; 278 off = 0; 279 280 for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) { 281 if (tpt_trig->blink_table[i].throughput < 0 || 282 tpt > tpt_trig->blink_table[i].throughput) { 283 off = tpt_trig->blink_table[i].blink_time / 2; 284 on = tpt_trig->blink_table[i].blink_time - off; 285 break; 286 } 287 } 288 289 read_lock(&local->tpt_led.leddev_list_lock); 290 list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list) 291 led_blink_set(led_cdev, &on, &off); 292 read_unlock(&local->tpt_led.leddev_list_lock); 293 } 294 295 const char * 296 __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, 297 unsigned int flags, 298 const struct ieee80211_tpt_blink *blink_table, 299 unsigned int blink_table_len) 300 { 301 struct ieee80211_local *local = hw_to_local(hw); 302 struct tpt_led_trigger *tpt_trig; 303 304 if (WARN_ON(local->tpt_led_trigger)) 305 return NULL; 306 307 tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL); 308 if (!tpt_trig) 309 return NULL; 310 311 snprintf(tpt_trig->name, sizeof(tpt_trig->name), 312 "%stpt", wiphy_name(local->hw.wiphy)); 313 314 local->tpt_led.name = tpt_trig->name; 315 316 tpt_trig->blink_table = blink_table; 317 tpt_trig->blink_table_len = blink_table_len; 318 tpt_trig->want = flags; 319 tpt_trig->local = local; 320 321 timer_setup(&tpt_trig->timer, tpt_trig_timer, 0); 322 323 local->tpt_led_trigger = tpt_trig; 324 325 return tpt_trig->name; 326 } 327 EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger); 328 329 static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local) 330 { 331 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; 332 333 if (tpt_trig->running) 334 return; 335 336 /* reset traffic */ 337 tpt_trig_traffic(local, tpt_trig); 338 tpt_trig->running = true; 339 340 tpt_trig_timer(&tpt_trig->timer); 341 mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ)); 342 } 343 344 static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local) 345 { 346 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; 347 struct led_classdev *led_cdev; 348 349 if (!tpt_trig->running) 350 return; 351 352 tpt_trig->running = false; 353 del_timer_sync(&tpt_trig->timer); 354 355 read_lock(&local->tpt_led.leddev_list_lock); 356 list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list) 357 led_set_brightness(led_cdev, LED_OFF); 358 read_unlock(&local->tpt_led.leddev_list_lock); 359 } 360 361 void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local, 362 unsigned int types_on, unsigned int types_off) 363 { 364 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; 365 bool allowed; 366 367 WARN_ON(types_on & types_off); 368 369 if (!tpt_trig) 370 return; 371 372 tpt_trig->active &= ~types_off; 373 tpt_trig->active |= types_on; 374 375 /* 376 * Regardless of wanted state, we shouldn't blink when 377 * the radio is disabled -- this can happen due to some 378 * code ordering issues with __ieee80211_recalc_idle() 379 * being called before the radio is started. 380 */ 381 allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO; 382 383 if (!allowed || !(tpt_trig->active & tpt_trig->want)) 384 ieee80211_stop_tpt_led_trig(local); 385 else 386 ieee80211_start_tpt_led_trig(local); 387 } 388