/net/mac80211/led.c

http://github.com/mirrors/linux · C · 384 lines · 293 code · 78 blank · 13 comment · 29 complexity · 41d281a0a153bfa9560f906c518bb927 MD5 · raw file

  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright 2006, Johannes Berg <johannes@sipsolutions.net>
  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. struct led_classdev *led_cdev;
  214. unsigned long on, off, tpt;
  215. int i;
  216. if (!tpt_trig->running)
  217. return;
  218. mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
  219. tpt = tpt_trig_traffic(local, tpt_trig);
  220. /* default to just solid on */
  221. on = 1;
  222. off = 0;
  223. for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
  224. if (tpt_trig->blink_table[i].throughput < 0 ||
  225. tpt > tpt_trig->blink_table[i].throughput) {
  226. off = tpt_trig->blink_table[i].blink_time / 2;
  227. on = tpt_trig->blink_table[i].blink_time - off;
  228. break;
  229. }
  230. }
  231. read_lock(&local->tpt_led.leddev_list_lock);
  232. list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
  233. led_blink_set(led_cdev, &on, &off);
  234. read_unlock(&local->tpt_led.leddev_list_lock);
  235. }
  236. const char *
  237. __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
  238. unsigned int flags,
  239. const struct ieee80211_tpt_blink *blink_table,
  240. unsigned int blink_table_len)
  241. {
  242. struct ieee80211_local *local = hw_to_local(hw);
  243. struct tpt_led_trigger *tpt_trig;
  244. if (WARN_ON(local->tpt_led_trigger))
  245. return NULL;
  246. tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
  247. if (!tpt_trig)
  248. return NULL;
  249. snprintf(tpt_trig->name, sizeof(tpt_trig->name),
  250. "%stpt", wiphy_name(local->hw.wiphy));
  251. local->tpt_led.name = tpt_trig->name;
  252. tpt_trig->blink_table = blink_table;
  253. tpt_trig->blink_table_len = blink_table_len;
  254. tpt_trig->want = flags;
  255. tpt_trig->local = local;
  256. timer_setup(&tpt_trig->timer, tpt_trig_timer, 0);
  257. local->tpt_led_trigger = tpt_trig;
  258. return tpt_trig->name;
  259. }
  260. EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
  261. static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
  262. {
  263. struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
  264. if (tpt_trig->running)
  265. return;
  266. /* reset traffic */
  267. tpt_trig_traffic(local, tpt_trig);
  268. tpt_trig->running = true;
  269. tpt_trig_timer(&tpt_trig->timer);
  270. mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
  271. }
  272. static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
  273. {
  274. struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
  275. struct led_classdev *led_cdev;
  276. if (!tpt_trig->running)
  277. return;
  278. tpt_trig->running = false;
  279. del_timer_sync(&tpt_trig->timer);
  280. read_lock(&local->tpt_led.leddev_list_lock);
  281. list_for_each_entry(led_cdev, &local->tpt_led.led_cdevs, trig_list)
  282. led_set_brightness(led_cdev, LED_OFF);
  283. read_unlock(&local->tpt_led.leddev_list_lock);
  284. }
  285. void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
  286. unsigned int types_on, unsigned int types_off)
  287. {
  288. struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
  289. bool allowed;
  290. WARN_ON(types_on & types_off);
  291. if (!tpt_trig)
  292. return;
  293. tpt_trig->active &= ~types_off;
  294. tpt_trig->active |= types_on;
  295. /*
  296. * Regardless of wanted state, we shouldn't blink when
  297. * the radio is disabled -- this can happen due to some
  298. * code ordering issues with __ieee80211_recalc_idle()
  299. * being called before the radio is started.
  300. */
  301. allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO;
  302. if (!allowed || !(tpt_trig->active & tpt_trig->want))
  303. ieee80211_stop_tpt_led_trig(local);
  304. else
  305. ieee80211_start_tpt_led_trig(local);
  306. }