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 struct led_classdev *led_cdev; 263 unsigned long on, off, tpt; 264 int i; 265 266 if (!tpt_trig->running) 267 return; 268 269 mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ)); 270 271 tpt = tpt_trig_traffic(local, tpt_trig); 272 273 /* default to just solid on */ 274 on = 1; 275 off = 0; 276 277 for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) { 278 if (tpt_trig->blink_table[i].throughput < 0 || 279 tpt > tpt_trig->blink_table[i].throughput) { 280 off = tpt_trig->blink_table[i].blink_time / 2; 281 on = tpt_trig->blink_table[i].blink_time - off; 282 break; 283 } 284 } 285 286 read_lock(&local->tpt_led.leddev_list_lock); 287 list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list) 288 led_blink_set(led_cdev, &on, &off); 289 read_unlock(&local->tpt_led.leddev_list_lock); 290 } 291 292 const char * 293 __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, 294 unsigned int flags, 295 const struct ieee80211_tpt_blink *blink_table, 296 unsigned int blink_table_len) 297 { 298 struct ieee80211_local *local = hw_to_local(hw); 299 struct tpt_led_trigger *tpt_trig; 300 301 if (WARN_ON(local->tpt_led_trigger)) 302 return NULL; 303 304 tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL); 305 if (!tpt_trig) 306 return NULL; 307 308 snprintf(tpt_trig->name, sizeof(tpt_trig->name), 309 "%stpt", wiphy_name(local->hw.wiphy)); 310 311 local->tpt_led.name = tpt_trig->name; 312 313 tpt_trig->blink_table = blink_table; 314 tpt_trig->blink_table_len = blink_table_len; 315 tpt_trig->want = flags; 316 tpt_trig->local = local; 317 318 timer_setup(&tpt_trig->timer, tpt_trig_timer, 0); 319 320 local->tpt_led_trigger = tpt_trig; 321 322 return tpt_trig->name; 323 } 324 EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger); 325 326 static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local) 327 { 328 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; 329 330 if (tpt_trig->running) 331 return; 332 333 /* reset traffic */ 334 tpt_trig_traffic(local, tpt_trig); 335 tpt_trig->running = true; 336 337 tpt_trig_timer(&tpt_trig->timer); 338 mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ)); 339 } 340 341 static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local) 342 { 343 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; 344 struct led_classdev *led_cdev; 345 346 if (!tpt_trig->running) 347 return; 348 349 tpt_trig->running = false; 350 del_timer_sync(&tpt_trig->timer); 351 352 read_lock(&local->tpt_led.leddev_list_lock); 353 list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list) 354 led_set_brightness(led_cdev, LED_OFF); 355 read_unlock(&local->tpt_led.leddev_list_lock); 356 } 357 358 void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local, 359 unsigned int types_on, unsigned int types_off) 360 { 361 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; 362 bool allowed; 363 364 WARN_ON(types_on & types_off); 365 366 if (!tpt_trig) 367 return; 368 369 tpt_trig->active &= ~types_off; 370 tpt_trig->active |= types_on; 371 372 /* 373 * Regardless of wanted state, we shouldn't blink when 374 * the radio is disabled -- this can happen due to some 375 * code ordering issues with __ieee80211_recalc_idle() 376 * being called before the radio is started. 377 */ 378 allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO; 379 380 if (!allowed || !(tpt_trig->active & tpt_trig->want)) 381 ieee80211_stop_tpt_led_trig(local); 382 else 383 ieee80211_start_tpt_led_trig(local); 384 } 385