led.c 9.6 KB

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