iwl-sta.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /******************************************************************************
  2. *
  3. * Copyright(c) 2003 - 2008 Intel Corporation. All rights reserved.
  4. *
  5. * Portions of this file are derived from the ipw3945 project, as well
  6. * as portions of the ieee80211 subsystem header files.
  7. *
  8. * This program is free software; you can redistribute it and/or modify it
  9. * under the terms of version 2 of the GNU General Public License as
  10. * published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful, but WITHOUT
  13. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  15. * more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along with
  18. * this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
  20. *
  21. * The full GNU General Public License is included in this distribution in the
  22. * file called LICENSE.
  23. *
  24. * Contact Information:
  25. * James P. Ketrenos <ipw2100-admin@linux.intel.com>
  26. * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
  27. *
  28. *****************************************************************************/
  29. #include <net/mac80211.h>
  30. #include "iwl-eeprom.h"
  31. #include "iwl-4965.h"
  32. #include "iwl-core.h"
  33. #include "iwl-sta.h"
  34. #include "iwl-io.h"
  35. #include "iwl-helpers.h"
  36. #include "iwl-4965.h"
  37. #include "iwl-sta.h"
  38. int iwl_get_free_ucode_key_index(struct iwl_priv *priv)
  39. {
  40. int i;
  41. for (i = 0; i < STA_KEY_MAX_NUM; i++)
  42. if (!test_and_set_bit(i, &priv->ucode_key_table))
  43. return i;
  44. return -1;
  45. }
  46. int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, u8 send_if_empty)
  47. {
  48. int i, not_empty = 0;
  49. u8 buff[sizeof(struct iwl_wep_cmd) +
  50. sizeof(struct iwl_wep_key) * WEP_KEYS_MAX];
  51. struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff;
  52. size_t cmd_size = sizeof(struct iwl_wep_cmd);
  53. struct iwl_host_cmd cmd = {
  54. .id = REPLY_WEPKEY,
  55. .data = wep_cmd,
  56. .meta.flags = CMD_ASYNC,
  57. };
  58. memset(wep_cmd, 0, cmd_size +
  59. (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX));
  60. for (i = 0; i < WEP_KEYS_MAX ; i++) {
  61. wep_cmd->key[i].key_index = i;
  62. if (priv->wep_keys[i].key_size) {
  63. wep_cmd->key[i].key_offset = i;
  64. not_empty = 1;
  65. } else {
  66. wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET;
  67. }
  68. wep_cmd->key[i].key_size = priv->wep_keys[i].key_size;
  69. memcpy(&wep_cmd->key[i].key[3], priv->wep_keys[i].key,
  70. priv->wep_keys[i].key_size);
  71. }
  72. wep_cmd->global_key_type = WEP_KEY_WEP_TYPE;
  73. wep_cmd->num_keys = WEP_KEYS_MAX;
  74. cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX;
  75. cmd.len = cmd_size;
  76. if (not_empty || send_if_empty)
  77. return iwl_send_cmd(priv, &cmd);
  78. else
  79. return 0;
  80. }
  81. int iwl_remove_default_wep_key(struct iwl_priv *priv,
  82. struct ieee80211_key_conf *keyconf)
  83. {
  84. int ret;
  85. unsigned long flags;
  86. spin_lock_irqsave(&priv->sta_lock, flags);
  87. if (!test_and_clear_bit(keyconf->keyidx, &priv->ucode_key_table))
  88. IWL_ERROR("index %d not used in uCode key table.\n",
  89. keyconf->keyidx);
  90. priv->default_wep_key--;
  91. memset(&priv->wep_keys[keyconf->keyidx], 0, sizeof(priv->wep_keys[0]));
  92. ret = iwl_send_static_wepkey_cmd(priv, 1);
  93. spin_unlock_irqrestore(&priv->sta_lock, flags);
  94. return ret;
  95. }
  96. int iwl_set_default_wep_key(struct iwl_priv *priv,
  97. struct ieee80211_key_conf *keyconf)
  98. {
  99. int ret;
  100. unsigned long flags;
  101. keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV;
  102. keyconf->hw_key_idx = keyconf->keyidx;
  103. priv->stations[IWL_AP_ID].keyinfo.alg = ALG_WEP;
  104. spin_lock_irqsave(&priv->sta_lock, flags);
  105. priv->default_wep_key++;
  106. if (test_and_set_bit(keyconf->keyidx, &priv->ucode_key_table))
  107. IWL_ERROR("index %d already used in uCode key table.\n",
  108. keyconf->keyidx);
  109. priv->wep_keys[keyconf->keyidx].key_size = keyconf->keylen;
  110. memcpy(&priv->wep_keys[keyconf->keyidx].key, &keyconf->key,
  111. keyconf->keylen);
  112. ret = iwl_send_static_wepkey_cmd(priv, 0);
  113. spin_unlock_irqrestore(&priv->sta_lock, flags);
  114. return ret;
  115. }
  116. static int iwl_set_wep_dynamic_key_info(struct iwl_priv *priv,
  117. struct ieee80211_key_conf *keyconf,
  118. u8 sta_id)
  119. {
  120. unsigned long flags;
  121. __le16 key_flags = 0;
  122. int ret;
  123. keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV;
  124. keyconf->hw_key_idx = keyconf->keyidx;
  125. key_flags |= (STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK);
  126. key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
  127. key_flags &= ~STA_KEY_FLG_INVALID;
  128. if (keyconf->keylen == WEP_KEY_LEN_128)
  129. key_flags |= STA_KEY_FLG_KEY_SIZE_MSK;
  130. if (sta_id == priv->hw_params.bcast_sta_id)
  131. key_flags |= STA_KEY_MULTICAST_MSK;
  132. spin_lock_irqsave(&priv->sta_lock, flags);
  133. priv->stations[sta_id].keyinfo.alg = keyconf->alg;
  134. priv->stations[sta_id].keyinfo.keylen = keyconf->keylen;
  135. priv->stations[sta_id].keyinfo.keyidx = keyconf->keyidx;
  136. memcpy(priv->stations[sta_id].keyinfo.key,
  137. keyconf->key, keyconf->keylen);
  138. memcpy(&priv->stations[sta_id].sta.key.key[3],
  139. keyconf->key, keyconf->keylen);
  140. priv->stations[sta_id].sta.key.key_offset =
  141. iwl_get_free_ucode_key_index(priv);
  142. priv->stations[sta_id].sta.key.key_flags = key_flags;
  143. priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
  144. priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
  145. ret = iwl4965_send_add_station(priv,
  146. &priv->stations[sta_id].sta, CMD_ASYNC);
  147. spin_unlock_irqrestore(&priv->sta_lock, flags);
  148. return ret;
  149. }
  150. static int iwl_set_ccmp_dynamic_key_info(struct iwl_priv *priv,
  151. struct ieee80211_key_conf *keyconf,
  152. u8 sta_id)
  153. {
  154. unsigned long flags;
  155. __le16 key_flags = 0;
  156. key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK);
  157. key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
  158. key_flags &= ~STA_KEY_FLG_INVALID;
  159. if (sta_id == priv->hw_params.bcast_sta_id)
  160. key_flags |= STA_KEY_MULTICAST_MSK;
  161. keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
  162. keyconf->hw_key_idx = keyconf->keyidx;
  163. spin_lock_irqsave(&priv->sta_lock, flags);
  164. priv->stations[sta_id].keyinfo.alg = keyconf->alg;
  165. priv->stations[sta_id].keyinfo.keylen = keyconf->keylen;
  166. memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key,
  167. keyconf->keylen);
  168. memcpy(priv->stations[sta_id].sta.key.key, keyconf->key,
  169. keyconf->keylen);
  170. priv->stations[sta_id].sta.key.key_offset =
  171. iwl_get_free_ucode_key_index(priv);
  172. priv->stations[sta_id].sta.key.key_flags = key_flags;
  173. priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
  174. priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
  175. spin_unlock_irqrestore(&priv->sta_lock, flags);
  176. IWL_DEBUG_INFO("hwcrypto: modify ucode station key info\n");
  177. return iwl4965_send_add_station(priv,
  178. &priv->stations[sta_id].sta, CMD_ASYNC);
  179. }
  180. static int iwl_set_tkip_dynamic_key_info(struct iwl_priv *priv,
  181. struct ieee80211_key_conf *keyconf,
  182. u8 sta_id)
  183. {
  184. unsigned long flags;
  185. int ret = 0;
  186. keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
  187. keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
  188. keyconf->hw_key_idx = keyconf->keyidx;
  189. spin_lock_irqsave(&priv->sta_lock, flags);
  190. priv->stations[sta_id].keyinfo.alg = keyconf->alg;
  191. priv->stations[sta_id].keyinfo.conf = keyconf;
  192. priv->stations[sta_id].keyinfo.keylen = 16;
  193. priv->stations[sta_id].sta.key.key_offset =
  194. iwl_get_free_ucode_key_index(priv);
  195. /* This copy is acutally not needed: we get the key with each TX */
  196. memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, 16);
  197. memcpy(priv->stations[sta_id].sta.key.key, keyconf->key, 16);
  198. spin_unlock_irqrestore(&priv->sta_lock, flags);
  199. return ret;
  200. }
  201. int iwl_remove_dynamic_key(struct iwl_priv *priv, u8 sta_id)
  202. {
  203. unsigned long flags;
  204. priv->key_mapping_key = 0;
  205. spin_lock_irqsave(&priv->sta_lock, flags);
  206. if (!test_and_clear_bit(priv->stations[sta_id].sta.key.key_offset,
  207. &priv->ucode_key_table))
  208. IWL_ERROR("index %d not used in uCode key table.\n",
  209. priv->stations[sta_id].sta.key.key_offset);
  210. memset(&priv->stations[sta_id].keyinfo, 0,
  211. sizeof(struct iwl4965_hw_key));
  212. memset(&priv->stations[sta_id].sta.key, 0,
  213. sizeof(struct iwl4965_keyinfo));
  214. priv->stations[sta_id].sta.key.key_flags = STA_KEY_FLG_NO_ENC;
  215. priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
  216. priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
  217. spin_unlock_irqrestore(&priv->sta_lock, flags);
  218. IWL_DEBUG_INFO("hwcrypto: clear ucode station key info\n");
  219. return iwl4965_send_add_station(priv, &priv->stations[sta_id].sta, 0);
  220. }
  221. int iwl_set_dynamic_key(struct iwl_priv *priv,
  222. struct ieee80211_key_conf *key, u8 sta_id)
  223. {
  224. int ret;
  225. priv->key_mapping_key = 1;
  226. switch (key->alg) {
  227. case ALG_CCMP:
  228. ret = iwl_set_ccmp_dynamic_key_info(priv, key, sta_id);
  229. break;
  230. case ALG_TKIP:
  231. ret = iwl_set_tkip_dynamic_key_info(priv, key, sta_id);
  232. break;
  233. case ALG_WEP:
  234. ret = iwl_set_wep_dynamic_key_info(priv, key, sta_id);
  235. break;
  236. default:
  237. IWL_ERROR("Unknown alg: %s alg = %d\n", __func__, key->alg);
  238. ret = -EINVAL;
  239. }
  240. return ret;
  241. }
  242. #ifdef CONFIG_IWLWIFI_DEBUG
  243. static void iwl_dump_lq_cmd(struct iwl_priv *priv,
  244. struct iwl_link_quality_cmd *lq)
  245. {
  246. int i;
  247. IWL_DEBUG_RATE("lq station id 0x%x\n", lq->sta_id);
  248. IWL_DEBUG_RATE("lq dta 0x%X 0x%X\n",
  249. lq->general_params.single_stream_ant_msk,
  250. lq->general_params.dual_stream_ant_msk);
  251. for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++)
  252. IWL_DEBUG_RATE("lq index %d 0x%X\n",
  253. i, lq->rs_table[i].rate_n_flags);
  254. }
  255. #else
  256. static inline void iwl_dump_lq_cmd(struct iwl_priv *priv,
  257. struct iwl_link_quality_cmd *lq)
  258. {
  259. }
  260. #endif
  261. int iwl_send_lq_cmd(struct iwl_priv *priv,
  262. struct iwl_link_quality_cmd *lq, u8 flags)
  263. {
  264. struct iwl_host_cmd cmd = {
  265. .id = REPLY_TX_LINK_QUALITY_CMD,
  266. .len = sizeof(struct iwl_link_quality_cmd),
  267. .meta.flags = flags,
  268. .data = lq,
  269. };
  270. if ((lq->sta_id == 0xFF) &&
  271. (priv->iw_mode == IEEE80211_IF_TYPE_IBSS))
  272. return -EINVAL;
  273. if (lq->sta_id == 0xFF)
  274. lq->sta_id = IWL_AP_ID;
  275. iwl_dump_lq_cmd(priv,lq);
  276. if (iwl_is_associated(priv) && priv->assoc_station_added &&
  277. priv->lq_mngr.lq_ready)
  278. return iwl_send_cmd(priv, &cmd);
  279. return 0;
  280. }
  281. EXPORT_SYMBOL(iwl_send_lq_cmd);