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 void 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 64 static void ieee80211_tx_led_deactivate(struct led_classdev *led_cdev) 65 { 66 struct ieee80211_local *local = container_of(led_cdev->trigger, 67 struct ieee80211_local, 68 tx_led); 69 70 atomic_dec(&local->tx_led_active); 71 } 72 73 static void ieee80211_rx_led_activate(struct led_classdev *led_cdev) 74 { 75 struct ieee80211_local *local = container_of(led_cdev->trigger, 76 struct ieee80211_local, 77 rx_led); 78 79 atomic_inc(&local->rx_led_active); 80 } 81 82 static void ieee80211_rx_led_deactivate(struct led_classdev *led_cdev) 83 { 84 struct ieee80211_local *local = container_of(led_cdev->trigger, 85 struct ieee80211_local, 86 rx_led); 87 88 atomic_dec(&local->rx_led_active); 89 } 90 91 static void ieee80211_assoc_led_activate(struct led_classdev *led_cdev) 92 { 93 struct ieee80211_local *local = container_of(led_cdev->trigger, 94 struct ieee80211_local, 95 assoc_led); 96 97 atomic_inc(&local->assoc_led_active); 98 } 99 100 static void ieee80211_assoc_led_deactivate(struct led_classdev *led_cdev) 101 { 102 struct ieee80211_local *local = container_of(led_cdev->trigger, 103 struct ieee80211_local, 104 assoc_led); 105 106 atomic_dec(&local->assoc_led_active); 107 } 108 109 static void ieee80211_radio_led_activate(struct led_classdev *led_cdev) 110 { 111 struct ieee80211_local *local = container_of(led_cdev->trigger, 112 struct ieee80211_local, 113 radio_led); 114 115 atomic_inc(&local->radio_led_active); 116 } 117 118 static void ieee80211_radio_led_deactivate(struct led_classdev *led_cdev) 119 { 120 struct ieee80211_local *local = container_of(led_cdev->trigger, 121 struct ieee80211_local, 122 radio_led); 123 124 atomic_dec(&local->radio_led_active); 125 } 126 127 static void ieee80211_tpt_led_activate(struct led_classdev *led_cdev) 128 { 129 struct ieee80211_local *local = container_of(led_cdev->trigger, 130 struct ieee80211_local, 131 tpt_led); 132 133 atomic_inc(&local->tpt_led_active); 134 } 135 136 static void ieee80211_tpt_led_deactivate(struct led_classdev *led_cdev) 137 { 138 struct ieee80211_local *local = container_of(led_cdev->trigger, 139 struct ieee80211_local, 140 tpt_led); 141 142 atomic_dec(&local->tpt_led_active); 143 } 144 145 void ieee80211_led_init(struct ieee80211_local *local) 146 { 147 atomic_set(&local->rx_led_active, 0); 148 local->rx_led.activate = ieee80211_rx_led_activate; 149 local->rx_led.deactivate = ieee80211_rx_led_deactivate; 150 if (local->rx_led.name && led_trigger_register(&local->rx_led)) { 151 kfree(local->rx_led.name); 152 local->rx_led.name = NULL; 153 } 154 155 atomic_set(&local->tx_led_active, 0); 156 local->tx_led.activate = ieee80211_tx_led_activate; 157 local->tx_led.deactivate = ieee80211_tx_led_deactivate; 158 if (local->tx_led.name && led_trigger_register(&local->tx_led)) { 159 kfree(local->tx_led.name); 160 local->tx_led.name = NULL; 161 } 162 163 atomic_set(&local->assoc_led_active, 0); 164 local->assoc_led.activate = ieee80211_assoc_led_activate; 165 local->assoc_led.deactivate = ieee80211_assoc_led_deactivate; 166 if (local->assoc_led.name && led_trigger_register(&local->assoc_led)) { 167 kfree(local->assoc_led.name); 168 local->assoc_led.name = NULL; 169 } 170 171 atomic_set(&local->radio_led_active, 0); 172 local->radio_led.activate = ieee80211_radio_led_activate; 173 local->radio_led.deactivate = ieee80211_radio_led_deactivate; 174 if (local->radio_led.name && led_trigger_register(&local->radio_led)) { 175 kfree(local->radio_led.name); 176 local->radio_led.name = NULL; 177 } 178 179 atomic_set(&local->tpt_led_active, 0); 180 if (local->tpt_led_trigger) { 181 local->tpt_led.activate = ieee80211_tpt_led_activate; 182 local->tpt_led.deactivate = ieee80211_tpt_led_deactivate; 183 if (led_trigger_register(&local->tpt_led)) { 184 kfree(local->tpt_led_trigger); 185 local->tpt_led_trigger = NULL; 186 } 187 } 188 } 189 190 void ieee80211_led_exit(struct ieee80211_local *local) 191 { 192 if (local->radio_led.name) 193 led_trigger_unregister(&local->radio_led); 194 if (local->assoc_led.name) 195 led_trigger_unregister(&local->assoc_led); 196 if (local->tx_led.name) 197 led_trigger_unregister(&local->tx_led); 198 if (local->rx_led.name) 199 led_trigger_unregister(&local->rx_led); 200 201 if (local->tpt_led_trigger) { 202 led_trigger_unregister(&local->tpt_led); 203 kfree(local->tpt_led_trigger); 204 } 205 } 206 207 const char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw) 208 { 209 struct ieee80211_local *local = hw_to_local(hw); 210 211 return local->radio_led.name; 212 } 213 EXPORT_SYMBOL(__ieee80211_get_radio_led_name); 214 215 const char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw) 216 { 217 struct ieee80211_local *local = hw_to_local(hw); 218 219 return local->assoc_led.name; 220 } 221 EXPORT_SYMBOL(__ieee80211_get_assoc_led_name); 222 223 const char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw) 224 { 225 struct ieee80211_local *local = hw_to_local(hw); 226 227 return local->tx_led.name; 228 } 229 EXPORT_SYMBOL(__ieee80211_get_tx_led_name); 230 231 const char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw) 232 { 233 struct ieee80211_local *local = hw_to_local(hw); 234 235 return local->rx_led.name; 236 } 237 EXPORT_SYMBOL(__ieee80211_get_rx_led_name); 238 239 static unsigned long tpt_trig_traffic(struct ieee80211_local *local, 240 struct tpt_led_trigger *tpt_trig) 241 { 242 unsigned long traffic, delta; 243 244 traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes; 245 246 delta = traffic - tpt_trig->prev_traffic; 247 tpt_trig->prev_traffic = traffic; 248 return DIV_ROUND_UP(delta, 1024 / 8); 249 } 250 251 static void tpt_trig_timer(struct timer_list *t) 252 { 253 struct tpt_led_trigger *tpt_trig = from_timer(tpt_trig, t, timer); 254 struct ieee80211_local *local = tpt_trig->local; 255 struct led_classdev *led_cdev; 256 unsigned long on, off, tpt; 257 int i; 258 259 if (!tpt_trig->running) 260 return; 261 262 mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ)); 263 264 tpt = tpt_trig_traffic(local, tpt_trig); 265 266 /* default to just solid on */ 267 on = 1; 268 off = 0; 269 270 for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) { 271 if (tpt_trig->blink_table[i].throughput < 0 || 272 tpt > tpt_trig->blink_table[i].throughput) { 273 off = tpt_trig->blink_table[i].blink_time / 2; 274 on = tpt_trig->blink_table[i].blink_time - off; 275 break; 276 } 277 } 278 279 read_lock(&local->tpt_led.leddev_list_lock); 280 list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list) 281 led_blink_set(led_cdev, &on, &off); 282 read_unlock(&local->tpt_led.leddev_list_lock); 283 } 284 285 const char * 286 __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, 287 unsigned int flags, 288 const struct ieee80211_tpt_blink *blink_table, 289 unsigned int blink_table_len) 290 { 291 struct ieee80211_local *local = hw_to_local(hw); 292 struct tpt_led_trigger *tpt_trig; 293 294 if (WARN_ON(local->tpt_led_trigger)) 295 return NULL; 296 297 tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL); 298 if (!tpt_trig) 299 return NULL; 300 301 snprintf(tpt_trig->name, sizeof(tpt_trig->name), 302 "%stpt", wiphy_name(local->hw.wiphy)); 303 304 local->tpt_led.name = tpt_trig->name; 305 306 tpt_trig->blink_table = blink_table; 307 tpt_trig->blink_table_len = blink_table_len; 308 tpt_trig->want = flags; 309 tpt_trig->local = local; 310 311 timer_setup(&tpt_trig->timer, tpt_trig_timer, 0); 312 313 local->tpt_led_trigger = tpt_trig; 314 315 return tpt_trig->name; 316 } 317 EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger); 318 319 static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local) 320 { 321 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; 322 323 if (tpt_trig->running) 324 return; 325 326 /* reset traffic */ 327 tpt_trig_traffic(local, tpt_trig); 328 tpt_trig->running = true; 329 330 tpt_trig_timer(&tpt_trig->timer); 331 mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ)); 332 } 333 334 static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local) 335 { 336 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; 337 struct led_classdev *led_cdev; 338 339 if (!tpt_trig->running) 340 return; 341 342 tpt_trig->running = false; 343 del_timer_sync(&tpt_trig->timer); 344 345 read_lock(&local->tpt_led.leddev_list_lock); 346 list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list) 347 led_set_brightness(led_cdev, LED_OFF); 348 read_unlock(&local->tpt_led.leddev_list_lock); 349 } 350 351 void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local, 352 unsigned int types_on, unsigned int types_off) 353 { 354 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; 355 bool allowed; 356 357 WARN_ON(types_on & types_off); 358 359 if (!tpt_trig) 360 return; 361 362 tpt_trig->active &= ~types_off; 363 tpt_trig->active |= types_on; 364 365 /* 366 * Regardless of wanted state, we shouldn't blink when 367 * the radio is disabled -- this can happen due to some 368 * code ordering issues with __ieee80211_recalc_idle() 369 * being called before the radio is started. 370 */ 371 allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO; 372 373 if (!allowed || !(tpt_trig->active & tpt_trig->want)) 374 ieee80211_stop_tpt_led_trig(local); 375 else 376 ieee80211_start_tpt_led_trig(local); 377 } 378