led.c (9837B)
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 12void 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 22void 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 32void 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 44void 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 52static 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 63static 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 72static 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 83static 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 92static 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 103static 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 112static 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 123static 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 132static 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 143static 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 152void 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 197void 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 214const 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} 220EXPORT_SYMBOL(__ieee80211_get_radio_led_name); 221 222const 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} 228EXPORT_SYMBOL(__ieee80211_get_assoc_led_name); 229 230const 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} 236EXPORT_SYMBOL(__ieee80211_get_tx_led_name); 237 238const 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} 244EXPORT_SYMBOL(__ieee80211_get_rx_led_name); 245 246static 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 258static 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 288const 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} 320EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger); 321 322static 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 337static 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 350void 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}