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