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