wext.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. /*
  2. * Intel Wireless Multicomm 3200 WiFi driver
  3. *
  4. * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
  5. * Samuel Ortiz <samuel.ortiz@intel.com>
  6. * Zhu Yi <yi.zhu@intel.com>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License version
  10. * 2 as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20. * 02110-1301, USA.
  21. *
  22. */
  23. #include <linux/kernel.h>
  24. #include <linux/netdevice.h>
  25. #include <linux/wireless.h>
  26. #include <linux/if_arp.h>
  27. #include <linux/etherdevice.h>
  28. #include <net/cfg80211.h>
  29. #include <net/iw_handler.h>
  30. #include "iwm.h"
  31. #include "umac.h"
  32. #include "commands.h"
  33. #include "debug.h"
  34. static struct iw_statistics *iwm_get_wireless_stats(struct net_device *dev)
  35. {
  36. struct iwm_priv *iwm = ndev_to_iwm(dev);
  37. struct iw_statistics *wstats = &iwm->wstats;
  38. if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
  39. memset(wstats, 0, sizeof(struct iw_statistics));
  40. wstats->qual.updated = IW_QUAL_ALL_INVALID;
  41. }
  42. return wstats;
  43. }
  44. static int iwm_wext_siwfreq(struct net_device *dev,
  45. struct iw_request_info *info,
  46. struct iw_freq *freq, char *extra)
  47. {
  48. struct iwm_priv *iwm = ndev_to_iwm(dev);
  49. if (freq->flags == IW_FREQ_AUTO)
  50. return 0;
  51. /* frequency/channel can only be set in IBSS mode */
  52. if (iwm->conf.mode != UMAC_MODE_IBSS)
  53. return -EOPNOTSUPP;
  54. return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra);
  55. }
  56. static int iwm_wext_giwfreq(struct net_device *dev,
  57. struct iw_request_info *info,
  58. struct iw_freq *freq, char *extra)
  59. {
  60. struct iwm_priv *iwm = ndev_to_iwm(dev);
  61. if (iwm->conf.mode == UMAC_MODE_IBSS)
  62. return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
  63. freq->e = 0;
  64. freq->m = iwm->channel;
  65. return 0;
  66. }
  67. static int iwm_wext_siwap(struct net_device *dev, struct iw_request_info *info,
  68. struct sockaddr *ap_addr, char *extra)
  69. {
  70. struct iwm_priv *iwm = ndev_to_iwm(dev);
  71. int ret;
  72. IWM_DBG_WEXT(iwm, DBG, "Set BSSID: %pM\n", ap_addr->sa_data);
  73. if (iwm->conf.mode == UMAC_MODE_IBSS)
  74. return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
  75. if (!test_bit(IWM_STATUS_READY, &iwm->status))
  76. return -EIO;
  77. if (is_zero_ether_addr(ap_addr->sa_data) ||
  78. is_broadcast_ether_addr(ap_addr->sa_data)) {
  79. IWM_DBG_WEXT(iwm, DBG, "clear mandatory bssid %pM\n",
  80. iwm->umac_profile->bssid[0]);
  81. memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
  82. iwm->umac_profile->bss_num = 0;
  83. } else {
  84. IWM_DBG_WEXT(iwm, DBG, "add mandatory bssid %pM\n",
  85. ap_addr->sa_data);
  86. memcpy(&iwm->umac_profile->bssid[0], ap_addr->sa_data,
  87. ETH_ALEN);
  88. iwm->umac_profile->bss_num = 1;
  89. }
  90. if (iwm->umac_profile_active) {
  91. int i;
  92. if (!memcmp(&iwm->umac_profile->bssid[0], iwm->bssid, ETH_ALEN))
  93. return 0;
  94. /*
  95. * If we're clearing the BSSID, and we're associated,
  96. * we have to clear the keys as they're no longer valid.
  97. */
  98. if (is_zero_ether_addr(ap_addr->sa_data)) {
  99. for (i = 0; i < IWM_NUM_KEYS; i++)
  100. iwm->keys[i].key_len = 0;
  101. }
  102. ret = iwm_invalidate_mlme_profile(iwm);
  103. if (ret < 0) {
  104. IWM_ERR(iwm, "Couldn't invalidate profile\n");
  105. return ret;
  106. }
  107. }
  108. if (iwm->umac_profile->ssid.ssid_len)
  109. return iwm_send_mlme_profile(iwm);
  110. return 0;
  111. }
  112. static int iwm_wext_giwap(struct net_device *dev, struct iw_request_info *info,
  113. struct sockaddr *ap_addr, char *extra)
  114. {
  115. struct iwm_priv *iwm = ndev_to_iwm(dev);
  116. switch (iwm->conf.mode) {
  117. case UMAC_MODE_IBSS:
  118. return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
  119. case UMAC_MODE_BSS:
  120. if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
  121. ap_addr->sa_family = ARPHRD_ETHER;
  122. memcpy(&ap_addr->sa_data, iwm->bssid, ETH_ALEN);
  123. } else
  124. memset(&ap_addr->sa_data, 0, ETH_ALEN);
  125. break;
  126. default:
  127. return -EOPNOTSUPP;
  128. }
  129. return 0;
  130. }
  131. static int iwm_wext_siwessid(struct net_device *dev,
  132. struct iw_request_info *info,
  133. struct iw_point *data, char *ssid)
  134. {
  135. struct iwm_priv *iwm = ndev_to_iwm(dev);
  136. size_t len = data->length;
  137. int ret;
  138. IWM_DBG_WEXT(iwm, DBG, "Set ESSID: >%s<\n", ssid);
  139. if (iwm->conf.mode == UMAC_MODE_IBSS)
  140. return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);
  141. if (!test_bit(IWM_STATUS_READY, &iwm->status))
  142. return -EIO;
  143. if (len > 0 && ssid[len - 1] == '\0')
  144. len--;
  145. if (iwm->umac_profile_active) {
  146. if (iwm->umac_profile->ssid.ssid_len == len &&
  147. !memcmp(iwm->umac_profile->ssid.ssid, ssid, len))
  148. return 0;
  149. ret = iwm_invalidate_mlme_profile(iwm);
  150. if (ret < 0) {
  151. IWM_ERR(iwm, "Couldn't invalidate profile\n");
  152. return ret;
  153. }
  154. }
  155. iwm->umac_profile->ssid.ssid_len = len;
  156. memcpy(iwm->umac_profile->ssid.ssid, ssid, len);
  157. return iwm_send_mlme_profile(iwm);
  158. }
  159. static int iwm_wext_giwessid(struct net_device *dev,
  160. struct iw_request_info *info,
  161. struct iw_point *data, char *ssid)
  162. {
  163. struct iwm_priv *iwm = ndev_to_iwm(dev);
  164. if (iwm->conf.mode == UMAC_MODE_IBSS)
  165. return cfg80211_ibss_wext_giwessid(dev, info, data, ssid);
  166. if (!test_bit(IWM_STATUS_READY, &iwm->status))
  167. return -EIO;
  168. data->length = iwm->umac_profile->ssid.ssid_len;
  169. if (data->length) {
  170. memcpy(ssid, iwm->umac_profile->ssid.ssid, data->length);
  171. data->flags = 1;
  172. } else
  173. data->flags = 0;
  174. return 0;
  175. }
  176. static int iwm_wext_giwrate(struct net_device *dev,
  177. struct iw_request_info *info,
  178. struct iw_param *rate, char *extra)
  179. {
  180. struct iwm_priv *iwm = ndev_to_iwm(dev);
  181. rate->value = iwm->rate * 1000000;
  182. return 0;
  183. }
  184. static int iwm_set_wpa_version(struct iwm_priv *iwm, u8 wpa_version)
  185. {
  186. if (wpa_version & IW_AUTH_WPA_VERSION_WPA2)
  187. iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
  188. else if (wpa_version & IW_AUTH_WPA_VERSION_WPA)
  189. iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
  190. else
  191. iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
  192. return 0;
  193. }
  194. static int iwm_set_key_mgt(struct iwm_priv *iwm, u8 key_mgt)
  195. {
  196. u8 *auth_type = &iwm->umac_profile->sec.auth_type;
  197. IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt);
  198. if (key_mgt == IW_AUTH_KEY_MGMT_802_1X)
  199. *auth_type = UMAC_AUTH_TYPE_8021X;
  200. else if (key_mgt == IW_AUTH_KEY_MGMT_PSK) {
  201. if (iwm->umac_profile->sec.flags &
  202. (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
  203. *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
  204. else
  205. *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
  206. } else {
  207. IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
  208. return -EINVAL;
  209. }
  210. return 0;
  211. }
  212. static int iwm_set_cipher(struct iwm_priv *iwm, u8 cipher, u8 ucast)
  213. {
  214. u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
  215. &iwm->umac_profile->sec.mcast_cipher;
  216. switch (cipher) {
  217. case IW_AUTH_CIPHER_NONE:
  218. *profile_cipher = UMAC_CIPHER_TYPE_NONE;
  219. break;
  220. case IW_AUTH_CIPHER_WEP40:
  221. *profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
  222. break;
  223. case IW_AUTH_CIPHER_TKIP:
  224. *profile_cipher = UMAC_CIPHER_TYPE_TKIP;
  225. break;
  226. case IW_AUTH_CIPHER_CCMP:
  227. *profile_cipher = UMAC_CIPHER_TYPE_CCMP;
  228. break;
  229. case IW_AUTH_CIPHER_WEP104:
  230. *profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
  231. break;
  232. default:
  233. IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
  234. return -ENOTSUPP;
  235. }
  236. return 0;
  237. }
  238. static int iwm_set_auth_alg(struct iwm_priv *iwm, u8 auth_alg)
  239. {
  240. u8 *auth_type = &iwm->umac_profile->sec.auth_type;
  241. IWM_DBG_WEXT(iwm, DBG, "auth_alg: 0x%x\n", auth_alg);
  242. switch (auth_alg) {
  243. case IW_AUTH_ALG_OPEN_SYSTEM:
  244. *auth_type = UMAC_AUTH_TYPE_OPEN;
  245. break;
  246. case IW_AUTH_ALG_SHARED_KEY:
  247. if (iwm->umac_profile->sec.flags &
  248. (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
  249. if (*auth_type == UMAC_AUTH_TYPE_8021X)
  250. return -EINVAL;
  251. *auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
  252. } else {
  253. IWM_DBG_WEXT(iwm, DBG, "WEP shared key\n");
  254. *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
  255. }
  256. break;
  257. case IW_AUTH_ALG_LEAP:
  258. default:
  259. IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", auth_alg);
  260. return -ENOTSUPP;
  261. }
  262. return 0;
  263. }
  264. static int iwm_wext_siwauth(struct net_device *dev,
  265. struct iw_request_info *info,
  266. struct iw_param *data, char *extra)
  267. {
  268. struct iwm_priv *iwm = ndev_to_iwm(dev);
  269. int ret;
  270. if ((data->flags) &
  271. (IW_AUTH_WPA_VERSION | IW_AUTH_KEY_MGMT |
  272. IW_AUTH_WPA_ENABLED | IW_AUTH_80211_AUTH_ALG)) {
  273. /* We need to invalidate the current profile */
  274. if (iwm->umac_profile_active) {
  275. ret = iwm_invalidate_mlme_profile(iwm);
  276. if (ret < 0) {
  277. IWM_ERR(iwm, "Couldn't invalidate profile\n");
  278. return ret;
  279. }
  280. }
  281. }
  282. switch (data->flags & IW_AUTH_INDEX) {
  283. case IW_AUTH_WPA_VERSION:
  284. return iwm_set_wpa_version(iwm, data->value);
  285. break;
  286. case IW_AUTH_CIPHER_PAIRWISE:
  287. return iwm_set_cipher(iwm, data->value, 1);
  288. break;
  289. case IW_AUTH_CIPHER_GROUP:
  290. return iwm_set_cipher(iwm, data->value, 0);
  291. break;
  292. case IW_AUTH_KEY_MGMT:
  293. return iwm_set_key_mgt(iwm, data->value);
  294. break;
  295. case IW_AUTH_80211_AUTH_ALG:
  296. return iwm_set_auth_alg(iwm, data->value);
  297. break;
  298. default:
  299. return -ENOTSUPP;
  300. }
  301. return 0;
  302. }
  303. static int iwm_wext_giwauth(struct net_device *dev,
  304. struct iw_request_info *info,
  305. struct iw_param *data, char *extra)
  306. {
  307. return 0;
  308. }
  309. static const iw_handler iwm_handlers[] =
  310. {
  311. (iw_handler) NULL, /* SIOCSIWCOMMIT */
  312. (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */
  313. (iw_handler) NULL, /* SIOCSIWNWID */
  314. (iw_handler) NULL, /* SIOCGIWNWID */
  315. (iw_handler) iwm_wext_siwfreq, /* SIOCSIWFREQ */
  316. (iw_handler) iwm_wext_giwfreq, /* SIOCGIWFREQ */
  317. (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */
  318. (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */
  319. (iw_handler) NULL, /* SIOCSIWSENS */
  320. (iw_handler) NULL, /* SIOCGIWSENS */
  321. (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */
  322. (iw_handler) cfg80211_wext_giwrange, /* SIOCGIWRANGE */
  323. (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */
  324. (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */
  325. (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */
  326. (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */
  327. (iw_handler) NULL, /* SIOCSIWSPY */
  328. (iw_handler) NULL, /* SIOCGIWSPY */
  329. (iw_handler) NULL, /* SIOCSIWTHRSPY */
  330. (iw_handler) NULL, /* SIOCGIWTHRSPY */
  331. (iw_handler) iwm_wext_siwap, /* SIOCSIWAP */
  332. (iw_handler) iwm_wext_giwap, /* SIOCGIWAP */
  333. (iw_handler) NULL, /* SIOCSIWMLME */
  334. (iw_handler) NULL, /* SIOCGIWAPLIST */
  335. (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */
  336. (iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */
  337. (iw_handler) iwm_wext_siwessid, /* SIOCSIWESSID */
  338. (iw_handler) iwm_wext_giwessid, /* SIOCGIWESSID */
  339. (iw_handler) NULL, /* SIOCSIWNICKN */
  340. (iw_handler) NULL, /* SIOCGIWNICKN */
  341. (iw_handler) NULL, /* -- hole -- */
  342. (iw_handler) NULL, /* -- hole -- */
  343. (iw_handler) NULL, /* SIOCSIWRATE */
  344. (iw_handler) iwm_wext_giwrate, /* SIOCGIWRATE */
  345. (iw_handler) cfg80211_wext_siwrts, /* SIOCSIWRTS */
  346. (iw_handler) cfg80211_wext_giwrts, /* SIOCGIWRTS */
  347. (iw_handler) cfg80211_wext_siwfrag, /* SIOCSIWFRAG */
  348. (iw_handler) cfg80211_wext_giwfrag, /* SIOCGIWFRAG */
  349. (iw_handler) cfg80211_wext_siwtxpower, /* SIOCSIWTXPOW */
  350. (iw_handler) cfg80211_wext_giwtxpower, /* SIOCGIWTXPOW */
  351. (iw_handler) NULL, /* SIOCSIWRETRY */
  352. (iw_handler) NULL, /* SIOCGIWRETRY */
  353. (iw_handler) cfg80211_wext_siwencode, /* SIOCSIWENCODE */
  354. (iw_handler) cfg80211_wext_giwencode, /* SIOCGIWENCODE */
  355. (iw_handler) cfg80211_wext_siwpower, /* SIOCSIWPOWER */
  356. (iw_handler) cfg80211_wext_giwpower, /* SIOCGIWPOWER */
  357. (iw_handler) NULL, /* -- hole -- */
  358. (iw_handler) NULL, /* -- hole -- */
  359. (iw_handler) NULL, /* SIOCSIWGENIE */
  360. (iw_handler) NULL, /* SIOCGIWGENIE */
  361. (iw_handler) iwm_wext_siwauth, /* SIOCSIWAUTH */
  362. (iw_handler) iwm_wext_giwauth, /* SIOCGIWAUTH */
  363. (iw_handler) cfg80211_wext_siwencodeext, /* SIOCSIWENCODEEXT */
  364. (iw_handler) NULL, /* SIOCGIWENCODEEXT */
  365. (iw_handler) NULL, /* SIOCSIWPMKSA */
  366. (iw_handler) NULL, /* -- hole -- */
  367. };
  368. const struct iw_handler_def iwm_iw_handler_def = {
  369. .num_standard = ARRAY_SIZE(iwm_handlers),
  370. .standard = (iw_handler *) iwm_handlers,
  371. .get_wireless_stats = iwm_get_wireless_stats,
  372. };