wow.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /*
  2. * Copyright (c) 2015 Qualcomm Atheros, Inc.
  3. *
  4. * Permission to use, copy, modify, and/or distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include "mac.h"
  17. #include <net/mac80211.h>
  18. #include "hif.h"
  19. #include "core.h"
  20. #include "debug.h"
  21. #include "wmi.h"
  22. #include "wmi-ops.h"
  23. static const struct wiphy_wowlan_support ath10k_wowlan_support = {
  24. .flags = WIPHY_WOWLAN_DISCONNECT |
  25. WIPHY_WOWLAN_MAGIC_PKT,
  26. .pattern_min_len = WOW_MIN_PATTERN_SIZE,
  27. .pattern_max_len = WOW_MAX_PATTERN_SIZE,
  28. .max_pkt_offset = WOW_MAX_PKT_OFFSET,
  29. };
  30. static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif)
  31. {
  32. struct ath10k *ar = arvif->ar;
  33. int i, ret;
  34. for (i = 0; i < WOW_EVENT_MAX; i++) {
  35. ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
  36. if (ret) {
  37. ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
  38. wow_wakeup_event(i), arvif->vdev_id, ret);
  39. return ret;
  40. }
  41. }
  42. for (i = 0; i < ar->wow.max_num_patterns; i++) {
  43. ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
  44. if (ret) {
  45. ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n",
  46. i, arvif->vdev_id, ret);
  47. return ret;
  48. }
  49. }
  50. return 0;
  51. }
  52. static int ath10k_wow_cleanup(struct ath10k *ar)
  53. {
  54. struct ath10k_vif *arvif;
  55. int ret;
  56. lockdep_assert_held(&ar->conf_mutex);
  57. list_for_each_entry(arvif, &ar->arvifs, list) {
  58. ret = ath10k_wow_vif_cleanup(arvif);
  59. if (ret) {
  60. ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n",
  61. arvif->vdev_id, ret);
  62. return ret;
  63. }
  64. }
  65. return 0;
  66. }
  67. static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
  68. struct cfg80211_wowlan *wowlan)
  69. {
  70. int ret, i;
  71. unsigned long wow_mask = 0;
  72. struct ath10k *ar = arvif->ar;
  73. const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
  74. int pattern_id = 0;
  75. /* Setup requested WOW features */
  76. switch (arvif->vdev_type) {
  77. case WMI_VDEV_TYPE_IBSS:
  78. __set_bit(WOW_BEACON_EVENT, &wow_mask);
  79. /* fall through */
  80. case WMI_VDEV_TYPE_AP:
  81. __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
  82. __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
  83. __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
  84. __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
  85. __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
  86. __set_bit(WOW_HTT_EVENT, &wow_mask);
  87. __set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
  88. break;
  89. case WMI_VDEV_TYPE_STA:
  90. if (wowlan->disconnect) {
  91. __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
  92. __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
  93. __set_bit(WOW_BMISS_EVENT, &wow_mask);
  94. __set_bit(WOW_CSA_IE_EVENT, &wow_mask);
  95. }
  96. if (wowlan->magic_pkt)
  97. __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
  98. break;
  99. default:
  100. break;
  101. }
  102. for (i = 0; i < wowlan->n_patterns; i++) {
  103. u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
  104. int j;
  105. if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
  106. continue;
  107. /* convert bytemask to bitmask */
  108. for (j = 0; j < patterns[i].pattern_len; j++)
  109. if (patterns[i].mask[j / 8] & BIT(j % 8))
  110. bitmask[j] = 0xff;
  111. ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
  112. pattern_id,
  113. patterns[i].pattern,
  114. bitmask,
  115. patterns[i].pattern_len,
  116. patterns[i].pkt_offset);
  117. if (ret) {
  118. ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",
  119. pattern_id,
  120. arvif->vdev_id, ret);
  121. return ret;
  122. }
  123. pattern_id++;
  124. __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
  125. }
  126. for (i = 0; i < WOW_EVENT_MAX; i++) {
  127. if (!test_bit(i, &wow_mask))
  128. continue;
  129. ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
  130. if (ret) {
  131. ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n",
  132. wow_wakeup_event(i), arvif->vdev_id, ret);
  133. return ret;
  134. }
  135. }
  136. return 0;
  137. }
  138. static int ath10k_wow_set_wakeups(struct ath10k *ar,
  139. struct cfg80211_wowlan *wowlan)
  140. {
  141. struct ath10k_vif *arvif;
  142. int ret;
  143. lockdep_assert_held(&ar->conf_mutex);
  144. list_for_each_entry(arvif, &ar->arvifs, list) {
  145. ret = ath10k_vif_wow_set_wakeups(arvif, wowlan);
  146. if (ret) {
  147. ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n",
  148. arvif->vdev_id, ret);
  149. return ret;
  150. }
  151. }
  152. return 0;
  153. }
  154. static int ath10k_wow_enable(struct ath10k *ar)
  155. {
  156. int ret;
  157. lockdep_assert_held(&ar->conf_mutex);
  158. reinit_completion(&ar->target_suspend);
  159. ret = ath10k_wmi_wow_enable(ar);
  160. if (ret) {
  161. ath10k_warn(ar, "failed to issue wow enable: %d\n", ret);
  162. return ret;
  163. }
  164. ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);
  165. if (ret == 0) {
  166. ath10k_warn(ar, "timed out while waiting for suspend completion\n");
  167. return -ETIMEDOUT;
  168. }
  169. return 0;
  170. }
  171. static int ath10k_wow_wakeup(struct ath10k *ar)
  172. {
  173. int ret;
  174. lockdep_assert_held(&ar->conf_mutex);
  175. reinit_completion(&ar->wow.wakeup_completed);
  176. ret = ath10k_wmi_wow_host_wakeup_ind(ar);
  177. if (ret) {
  178. ath10k_warn(ar, "failed to send wow wakeup indication: %d\n",
  179. ret);
  180. return ret;
  181. }
  182. ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ);
  183. if (ret == 0) {
  184. ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n");
  185. return -ETIMEDOUT;
  186. }
  187. return 0;
  188. }
  189. int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
  190. struct cfg80211_wowlan *wowlan)
  191. {
  192. struct ath10k *ar = hw->priv;
  193. int ret;
  194. mutex_lock(&ar->conf_mutex);
  195. if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
  196. ar->fw_features))) {
  197. ret = 1;
  198. goto exit;
  199. }
  200. ret = ath10k_wow_cleanup(ar);
  201. if (ret) {
  202. ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
  203. ret);
  204. goto exit;
  205. }
  206. ret = ath10k_wow_set_wakeups(ar, wowlan);
  207. if (ret) {
  208. ath10k_warn(ar, "failed to set wow wakeup events: %d\n",
  209. ret);
  210. goto cleanup;
  211. }
  212. ret = ath10k_wow_enable(ar);
  213. if (ret) {
  214. ath10k_warn(ar, "failed to start wow: %d\n", ret);
  215. goto cleanup;
  216. }
  217. ret = ath10k_hif_suspend(ar);
  218. if (ret) {
  219. ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
  220. goto wakeup;
  221. }
  222. goto exit;
  223. wakeup:
  224. ath10k_wow_wakeup(ar);
  225. cleanup:
  226. ath10k_wow_cleanup(ar);
  227. exit:
  228. mutex_unlock(&ar->conf_mutex);
  229. return ret ? 1 : 0;
  230. }
  231. int ath10k_wow_op_resume(struct ieee80211_hw *hw)
  232. {
  233. struct ath10k *ar = hw->priv;
  234. int ret;
  235. mutex_lock(&ar->conf_mutex);
  236. if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
  237. ar->fw_features))) {
  238. ret = 1;
  239. goto exit;
  240. }
  241. ret = ath10k_hif_resume(ar);
  242. if (ret) {
  243. ath10k_warn(ar, "failed to resume hif: %d\n", ret);
  244. goto exit;
  245. }
  246. ret = ath10k_wow_wakeup(ar);
  247. if (ret)
  248. ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
  249. exit:
  250. if (ret) {
  251. switch (ar->state) {
  252. case ATH10K_STATE_ON:
  253. ar->state = ATH10K_STATE_RESTARTING;
  254. ret = 1;
  255. break;
  256. case ATH10K_STATE_OFF:
  257. case ATH10K_STATE_RESTARTING:
  258. case ATH10K_STATE_RESTARTED:
  259. case ATH10K_STATE_UTF:
  260. case ATH10K_STATE_WEDGED:
  261. ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n",
  262. ar->state);
  263. ret = -EIO;
  264. break;
  265. }
  266. }
  267. mutex_unlock(&ar->conf_mutex);
  268. return ret;
  269. }
  270. int ath10k_wow_init(struct ath10k *ar)
  271. {
  272. if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, ar->fw_features))
  273. return 0;
  274. if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))
  275. return -EINVAL;
  276. ar->wow.wowlan_support = ath10k_wowlan_support;
  277. ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
  278. ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
  279. return 0;
  280. }