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