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 #define MAC80211_BLINK_DELAY 50 /* ms */ 16 17 void ieee80211_led_rx(struct ieee80211_local *local) 18 { 19 unsigned long led_delay = MAC80211_BLINK_DELAY; 20 if (unlikely(!local->rx_led)) 21 return; 22 led_trigger_blink_oneshot(local->rx_led, &led_delay, &led_delay, 0); 23 } 24 25 void ieee80211_led_tx(struct ieee80211_local *local) 26 { 27 unsigned long led_delay = MAC80211_BLINK_DELAY; 28 if (unlikely(!local->tx_led)) 29 return; 30 led_trigger_blink_oneshot(local->tx_led, &led_delay, &led_delay, 0); 31 } 32 33 void ieee80211_led_assoc(struct ieee80211_local *local, bool associated) 34 { 35 if (unlikely(!local->assoc_led)) 36 return; 37 if (associated) 38 led_trigger_event(local->assoc_led, LED_FULL); 39 else 40 led_trigger_event(local->assoc_led, LED_OFF); 41 } 42 43 void ieee80211_led_radio(struct ieee80211_local *local, bool enabled) 44 { 45 if (unlikely(!local->radio_led)) 46 return; 47 if (enabled) 48 led_trigger_event(local->radio_led, LED_FULL); 49 else 50 led_trigger_event(local->radio_led, LED_OFF); 51 } 52 53 void ieee80211_led_names(struct ieee80211_local *local) 54 { 55 snprintf(local->rx_led_name, sizeof(local->rx_led_name), 56 "%srx", wiphy_name(local->hw.wiphy)); 57 snprintf(local->tx_led_name, sizeof(local->tx_led_name), 58 "%stx", wiphy_name(local->hw.wiphy)); 59 snprintf(local->assoc_led_name, sizeof(local->assoc_led_name), 60 "%sassoc", wiphy_name(local->hw.wiphy)); 61 snprintf(local->radio_led_name, sizeof(local->radio_led_name), 62 "%sradio", wiphy_name(local->hw.wiphy)); 63 } 64 65 void ieee80211_led_init(struct ieee80211_local *local) 66 { 67 local->rx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); 68 if (local->rx_led) { 69 local->rx_led->name = local->rx_led_name; 70 if (led_trigger_register(local->rx_led)) { 71 kfree(local->rx_led); 72 local->rx_led = NULL; 73 } 74 } 75 76 local->tx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); 77 if (local->tx_led) { 78 local->tx_led->name = local->tx_led_name; 79 if (led_trigger_register(local->tx_led)) { 80 kfree(local->tx_led); 81 local->tx_led = NULL; 82 } 83 } 84 85 local->assoc_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); 86 if (local->assoc_led) { 87 local->assoc_led->name = local->assoc_led_name; 88 if (led_trigger_register(local->assoc_led)) { 89 kfree(local->assoc_led); 90 local->assoc_led = NULL; 91 } 92 } 93 94 local->radio_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); 95 if (local->radio_led) { 96 local->radio_led->name = local->radio_led_name; 97 if (led_trigger_register(local->radio_led)) { 98 kfree(local->radio_led); 99 local->radio_led = NULL; 100 } 101 } 102 103 if (local->tpt_led_trigger) { 104 if (led_trigger_register(&local->tpt_led_trigger->trig)) { 105 kfree(local->tpt_led_trigger); 106 local->tpt_led_trigger = NULL; 107 } 108 } 109 } 110 111 void ieee80211_led_exit(struct ieee80211_local *local) 112 { 113 if (local->radio_led) { 114 led_trigger_unregister(local->radio_led); 115 kfree(local->radio_led); 116 } 117 if (local->assoc_led) { 118 led_trigger_unregister(local->assoc_led); 119 kfree(local->assoc_led); 120 } 121 if (local->tx_led) { 122 led_trigger_unregister(local->tx_led); 123 kfree(local->tx_led); 124 } 125 if (local->rx_led) { 126 led_trigger_unregister(local->rx_led); 127 kfree(local->rx_led); 128 } 129 130 if (local->tpt_led_trigger) { 131 led_trigger_unregister(&local->tpt_led_trigger->trig); 132 kfree(local->tpt_led_trigger); 133 } 134 } 135 136 char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw) 137 { 138 struct ieee80211_local *local = hw_to_local(hw); 139 140 return local->radio_led_name; 141 } 142 EXPORT_SYMBOL(__ieee80211_get_radio_led_name); 143 144 char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw) 145 { 146 struct ieee80211_local *local = hw_to_local(hw); 147 148 return local->assoc_led_name; 149 } 150 EXPORT_SYMBOL(__ieee80211_get_assoc_led_name); 151 152 char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw) 153 { 154 struct ieee80211_local *local = hw_to_local(hw); 155 156 return local->tx_led_name; 157 } 158 EXPORT_SYMBOL(__ieee80211_get_tx_led_name); 159 160 char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw) 161 { 162 struct ieee80211_local *local = hw_to_local(hw); 163 164 return local->rx_led_name; 165 } 166 EXPORT_SYMBOL(__ieee80211_get_rx_led_name); 167 168 static unsigned long tpt_trig_traffic(struct ieee80211_local *local, 169 struct tpt_led_trigger *tpt_trig) 170 { 171 unsigned long traffic, delta; 172 173 traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes; 174 175 delta = traffic - tpt_trig->prev_traffic; 176 tpt_trig->prev_traffic = traffic; 177 return DIV_ROUND_UP(delta, 1024 / 8); 178 } 179 180 static void tpt_trig_timer(unsigned long data) 181 { 182 struct ieee80211_local *local = (void *)data; 183 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; 184 struct led_classdev *led_cdev; 185 unsigned long on, off, tpt; 186 int i; 187 188 if (!tpt_trig->running) 189 return; 190 191 mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ)); 192 193 tpt = tpt_trig_traffic(local, tpt_trig); 194 195 /* default to just solid on */ 196 on = 1; 197 off = 0; 198 199 for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) { 200 if (tpt_trig->blink_table[i].throughput < 0 || 201 tpt > tpt_trig->blink_table[i].throughput) { 202 off = tpt_trig->blink_table[i].blink_time / 2; 203 on = tpt_trig->blink_table[i].blink_time - off; 204 break; 205 } 206 } 207 208 read_lock(&tpt_trig->trig.leddev_list_lock); 209 list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list) 210 led_blink_set(led_cdev, &on, &off); 211 read_unlock(&tpt_trig->trig.leddev_list_lock); 212 } 213 214 char *__ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, 215 unsigned int flags, 216 const struct ieee80211_tpt_blink *blink_table, 217 unsigned int blink_table_len) 218 { 219 struct ieee80211_local *local = hw_to_local(hw); 220 struct tpt_led_trigger *tpt_trig; 221 222 if (WARN_ON(local->tpt_led_trigger)) 223 return NULL; 224 225 tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL); 226 if (!tpt_trig) 227 return NULL; 228 229 snprintf(tpt_trig->name, sizeof(tpt_trig->name), 230 "%stpt", wiphy_name(local->hw.wiphy)); 231 232 tpt_trig->trig.name = tpt_trig->name; 233 234 tpt_trig->blink_table = blink_table; 235 tpt_trig->blink_table_len = blink_table_len; 236 tpt_trig->want = flags; 237 238 setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local); 239 240 local->tpt_led_trigger = tpt_trig; 241 242 return tpt_trig->name; 243 } 244 EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger); 245 246 static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local) 247 { 248 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; 249 250 if (tpt_trig->running) 251 return; 252 253 /* reset traffic */ 254 tpt_trig_traffic(local, tpt_trig); 255 tpt_trig->running = true; 256 257 tpt_trig_timer((unsigned long)local); 258 mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ)); 259 } 260 261 static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local) 262 { 263 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; 264 struct led_classdev *led_cdev; 265 266 if (!tpt_trig->running) 267 return; 268 269 tpt_trig->running = false; 270 del_timer_sync(&tpt_trig->timer); 271 272 read_lock(&tpt_trig->trig.leddev_list_lock); 273 list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list) 274 led_set_brightness(led_cdev, LED_OFF); 275 read_unlock(&tpt_trig->trig.leddev_list_lock); 276 } 277 278 void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local, 279 unsigned int types_on, unsigned int types_off) 280 { 281 struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger; 282 bool allowed; 283 284 WARN_ON(types_on & types_off); 285 286 if (!tpt_trig) 287 return; 288 289 tpt_trig->active &= ~types_off; 290 tpt_trig->active |= types_on; 291 292 /* 293 * Regardless of wanted state, we shouldn't blink when 294 * the radio is disabled -- this can happen due to some 295 * code ordering issues with __ieee80211_recalc_idle() 296 * being called before the radio is started. 297 */ 298 allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO; 299 300 if (!allowed || !(tpt_trig->active & tpt_trig->want)) 301 ieee80211_stop_tpt_led_trig(local); 302 else 303 ieee80211_start_tpt_led_trig(local); 304 } 305