led.c 7.8 KB

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