wow.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. /*
  2. * Copyright (c) 2013 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 "ath9k.h"
  17. static const struct wiphy_wowlan_support ath9k_wowlan_support_legacy = {
  18. .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
  19. .n_patterns = MAX_NUM_USER_PATTERN,
  20. .pattern_min_len = 1,
  21. .pattern_max_len = MAX_PATTERN_SIZE,
  22. };
  23. static const struct wiphy_wowlan_support ath9k_wowlan_support = {
  24. .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
  25. .n_patterns = MAX_NUM_PATTERN - 2,
  26. .pattern_min_len = 1,
  27. .pattern_max_len = MAX_PATTERN_SIZE,
  28. };
  29. static u8 ath9k_wow_map_triggers(struct ath_softc *sc,
  30. struct cfg80211_wowlan *wowlan)
  31. {
  32. u8 wow_triggers = 0;
  33. if (wowlan->disconnect)
  34. wow_triggers |= AH_WOW_LINK_CHANGE |
  35. AH_WOW_BEACON_MISS;
  36. if (wowlan->magic_pkt)
  37. wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
  38. if (wowlan->n_patterns)
  39. wow_triggers |= AH_WOW_USER_PATTERN_EN;
  40. return wow_triggers;
  41. }
  42. static int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
  43. {
  44. struct ath_hw *ah = sc->sc_ah;
  45. struct ath_common *common = ath9k_hw_common(ah);
  46. int pattern_count = 0;
  47. int ret, i, byte_cnt = 0;
  48. u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
  49. u8 dis_deauth_mask[MAX_PATTERN_SIZE];
  50. memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE);
  51. memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE);
  52. /*
  53. * Create Dissassociate / Deauthenticate packet filter
  54. *
  55. * 2 bytes 2 byte 6 bytes 6 bytes 6 bytes
  56. * +--------------+----------+---------+--------+--------+----
  57. * + Frame Control+ Duration + DA + SA + BSSID +
  58. * +--------------+----------+---------+--------+--------+----
  59. *
  60. * The above is the management frame format for disassociate/
  61. * deauthenticate pattern, from this we need to match the first byte
  62. * of 'Frame Control' and DA, SA, and BSSID fields
  63. * (skipping 2nd byte of FC and Duration feild.
  64. *
  65. * Disassociate pattern
  66. * --------------------
  67. * Frame control = 00 00 1010
  68. * DA, SA, BSSID = x:x:x:x:x:x
  69. * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x
  70. * | x:x:x:x:x:x -- 22 bytes
  71. *
  72. * Deauthenticate pattern
  73. * ----------------------
  74. * Frame control = 00 00 1100
  75. * DA, SA, BSSID = x:x:x:x:x:x
  76. * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x
  77. * | x:x:x:x:x:x -- 22 bytes
  78. */
  79. /* Fill out the mask with all FF's */
  80. for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++)
  81. dis_deauth_mask[i] = 0xff;
  82. /* copy the first byte of frame control field */
  83. dis_deauth_pattern[byte_cnt] = 0xa0;
  84. byte_cnt++;
  85. /* skip 2nd byte of frame control and Duration field */
  86. byte_cnt += 3;
  87. /*
  88. * need not match the destination mac address, it can be a broadcast
  89. * mac address or an unicast to this station
  90. */
  91. byte_cnt += 6;
  92. /* copy the source mac address */
  93. memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
  94. byte_cnt += 6;
  95. /* copy the bssid, its same as the source mac address */
  96. memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
  97. /* Create Disassociate pattern mask */
  98. dis_deauth_mask[0] = 0xfe;
  99. dis_deauth_mask[1] = 0x03;
  100. dis_deauth_mask[2] = 0xc0;
  101. ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
  102. pattern_count, byte_cnt);
  103. if (ret)
  104. goto exit;
  105. pattern_count++;
  106. /*
  107. * for de-authenticate pattern, only the first byte of the frame
  108. * control field gets changed from 0xA0 to 0xC0
  109. */
  110. dis_deauth_pattern[0] = 0xC0;
  111. ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
  112. pattern_count, byte_cnt);
  113. exit:
  114. return ret;
  115. }
  116. static int ath9k_wow_add_pattern(struct ath_softc *sc,
  117. struct cfg80211_wowlan *wowlan)
  118. {
  119. struct ath_hw *ah = sc->sc_ah;
  120. struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
  121. u8 wow_pattern[MAX_PATTERN_SIZE];
  122. u8 wow_mask[MAX_PATTERN_SIZE];
  123. int mask_len, ret = 0;
  124. s8 i = 0;
  125. for (i = 0; i < wowlan->n_patterns; i++) {
  126. mask_len = DIV_ROUND_UP(patterns[i].pattern_len, 8);
  127. memset(wow_pattern, 0, MAX_PATTERN_SIZE);
  128. memset(wow_mask, 0, MAX_PATTERN_SIZE);
  129. memcpy(wow_pattern, patterns[i].pattern, patterns[i].pattern_len);
  130. memcpy(wow_mask, patterns[i].mask, mask_len);
  131. ret = ath9k_hw_wow_apply_pattern(ah,
  132. wow_pattern,
  133. wow_mask,
  134. i + 2,
  135. patterns[i].pattern_len);
  136. if (ret)
  137. break;
  138. }
  139. return ret;
  140. }
  141. int ath9k_suspend(struct ieee80211_hw *hw,
  142. struct cfg80211_wowlan *wowlan)
  143. {
  144. struct ath_softc *sc = hw->priv;
  145. struct ath_hw *ah = sc->sc_ah;
  146. struct ath_common *common = ath9k_hw_common(ah);
  147. u8 triggers;
  148. int ret = 0;
  149. ath9k_deinit_channel_context(sc);
  150. mutex_lock(&sc->mutex);
  151. if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
  152. ath_err(common, "Device not present\n");
  153. ret = -ENODEV;
  154. goto fail_wow;
  155. }
  156. if (WARN_ON(!wowlan)) {
  157. ath_err(common, "None of the WoW triggers enabled\n");
  158. ret = -EINVAL;
  159. goto fail_wow;
  160. }
  161. if (sc->cur_chan->nvifs > 1) {
  162. ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
  163. ret = 1;
  164. goto fail_wow;
  165. }
  166. if (ath9k_is_chanctx_enabled()) {
  167. if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) {
  168. ath_dbg(common, WOW,
  169. "Multi-channel WOW is not supported\n");
  170. ret = 1;
  171. goto fail_wow;
  172. }
  173. }
  174. if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
  175. ath_dbg(common, WOW, "None of the STA vifs are associated\n");
  176. ret = 1;
  177. goto fail_wow;
  178. }
  179. triggers = ath9k_wow_map_triggers(sc, wowlan);
  180. if (!triggers) {
  181. ath_dbg(common, WOW, "No valid WoW triggers\n");
  182. ret = 1;
  183. goto fail_wow;
  184. }
  185. ath_cancel_work(sc);
  186. ath_stop_ani(sc);
  187. ath9k_ps_wakeup(sc);
  188. ath9k_stop_btcoex(sc);
  189. /*
  190. * Enable wake up on recieving disassoc/deauth
  191. * frame by default.
  192. */
  193. ret = ath9k_wow_add_disassoc_deauth_pattern(sc);
  194. if (ret) {
  195. ath_err(common,
  196. "Unable to add disassoc/deauth pattern: %d\n", ret);
  197. goto fail_wow;
  198. }
  199. if (triggers & AH_WOW_USER_PATTERN_EN) {
  200. ret = ath9k_wow_add_pattern(sc, wowlan);
  201. if (ret) {
  202. ath_err(common,
  203. "Unable to add user pattern: %d\n", ret);
  204. goto fail_wow;
  205. }
  206. }
  207. spin_lock_bh(&sc->sc_pcu_lock);
  208. /*
  209. * To avoid false wake, we enable beacon miss interrupt only
  210. * when we go to sleep. We save the current interrupt mask
  211. * so we can restore it after the system wakes up
  212. */
  213. sc->wow_intr_before_sleep = ah->imask;
  214. ah->imask &= ~ATH9K_INT_GLOBAL;
  215. ath9k_hw_disable_interrupts(ah);
  216. ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL;
  217. ath9k_hw_set_interrupts(ah);
  218. ath9k_hw_enable_interrupts(ah);
  219. spin_unlock_bh(&sc->sc_pcu_lock);
  220. /*
  221. * we can now sync irq and kill any running tasklets, since we already
  222. * disabled interrupts and not holding a spin lock
  223. */
  224. synchronize_irq(sc->irq);
  225. tasklet_kill(&sc->intr_tq);
  226. ath9k_hw_wow_enable(ah, triggers);
  227. ath9k_ps_restore(sc);
  228. ath_dbg(common, WOW, "Suspend with WoW triggers: 0x%x\n", triggers);
  229. set_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
  230. fail_wow:
  231. mutex_unlock(&sc->mutex);
  232. return ret;
  233. }
  234. int ath9k_resume(struct ieee80211_hw *hw)
  235. {
  236. struct ath_softc *sc = hw->priv;
  237. struct ath_hw *ah = sc->sc_ah;
  238. struct ath_common *common = ath9k_hw_common(ah);
  239. u8 status;
  240. mutex_lock(&sc->mutex);
  241. ath9k_ps_wakeup(sc);
  242. spin_lock_bh(&sc->sc_pcu_lock);
  243. ath9k_hw_disable_interrupts(ah);
  244. ah->imask = sc->wow_intr_before_sleep;
  245. ath9k_hw_set_interrupts(ah);
  246. ath9k_hw_enable_interrupts(ah);
  247. spin_unlock_bh(&sc->sc_pcu_lock);
  248. status = ath9k_hw_wow_wakeup(ah);
  249. ath_dbg(common, WOW, "Resume with WoW status: 0x%x\n", status);
  250. ath_restart_work(sc);
  251. ath9k_start_btcoex(sc);
  252. clear_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
  253. ath9k_ps_restore(sc);
  254. mutex_unlock(&sc->mutex);
  255. return 0;
  256. }
  257. void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
  258. {
  259. struct ath_softc *sc = hw->priv;
  260. struct ath_common *common = ath9k_hw_common(sc->sc_ah);
  261. mutex_lock(&sc->mutex);
  262. device_set_wakeup_enable(sc->dev, enabled);
  263. mutex_unlock(&sc->mutex);
  264. ath_dbg(common, WOW, "WoW wakeup source is %s\n",
  265. (enabled) ? "enabled" : "disabled");
  266. }
  267. void ath9k_init_wow(struct ieee80211_hw *hw)
  268. {
  269. struct ath_softc *sc = hw->priv;
  270. struct ath_hw *ah = sc->sc_ah;
  271. if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow) {
  272. if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565_11_OR_LATER(ah))
  273. hw->wiphy->wowlan = &ath9k_wowlan_support;
  274. else
  275. hw->wiphy->wowlan = &ath9k_wowlan_support_legacy;
  276. device_init_wakeup(sc->dev, 1);
  277. }
  278. }
  279. void ath9k_deinit_wow(struct ieee80211_hw *hw)
  280. {
  281. struct ath_softc *sc = hw->priv;
  282. if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow)
  283. device_init_wakeup(sc->dev, 0);
  284. }