diff options
Diffstat (limited to 'drivers/net/wireless/iwmc3200wifi/cfg80211.c')
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/cfg80211.c | 378 |
1 files changed, 371 insertions, 7 deletions
diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c index 96f714e6e12b..a6e852f4f92c 100644 --- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c +++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c | |||
@@ -23,6 +23,7 @@ | |||
23 | 23 | ||
24 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
25 | #include <linux/netdevice.h> | 25 | #include <linux/netdevice.h> |
26 | #include <linux/etherdevice.h> | ||
26 | #include <linux/wireless.h> | 27 | #include <linux/wireless.h> |
27 | #include <linux/ieee80211.h> | 28 | #include <linux/ieee80211.h> |
28 | #include <net/cfg80211.h> | 29 | #include <net/cfg80211.h> |
@@ -130,6 +131,133 @@ static struct ieee80211_supported_band iwm_band_5ghz = { | |||
130 | .n_bitrates = iwm_a_rates_size, | 131 | .n_bitrates = iwm_a_rates_size, |
131 | }; | 132 | }; |
132 | 133 | ||
134 | static int iwm_key_init(struct iwm_key *key, u8 key_index, | ||
135 | const u8 *mac_addr, struct key_params *params) | ||
136 | { | ||
137 | key->hdr.key_idx = key_index; | ||
138 | if (!mac_addr || is_broadcast_ether_addr(mac_addr)) { | ||
139 | key->hdr.multicast = 1; | ||
140 | memset(key->hdr.mac, 0xff, ETH_ALEN); | ||
141 | } else { | ||
142 | key->hdr.multicast = 0; | ||
143 | memcpy(key->hdr.mac, mac_addr, ETH_ALEN); | ||
144 | } | ||
145 | |||
146 | if (params) { | ||
147 | if (params->key_len > WLAN_MAX_KEY_LEN || | ||
148 | params->seq_len > IW_ENCODE_SEQ_MAX_SIZE) | ||
149 | return -EINVAL; | ||
150 | |||
151 | key->cipher = params->cipher; | ||
152 | key->key_len = params->key_len; | ||
153 | key->seq_len = params->seq_len; | ||
154 | memcpy(key->key, params->key, key->key_len); | ||
155 | memcpy(key->seq, params->seq, key->seq_len); | ||
156 | } | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, | ||
162 | u8 key_index, const u8 *mac_addr, | ||
163 | struct key_params *params) | ||
164 | { | ||
165 | struct iwm_priv *iwm = ndev_to_iwm(ndev); | ||
166 | struct iwm_key *key = &iwm->keys[key_index]; | ||
167 | int ret; | ||
168 | |||
169 | IWM_DBG_WEXT(iwm, DBG, "Adding key for %pM\n", mac_addr); | ||
170 | |||
171 | memset(key, 0, sizeof(struct iwm_key)); | ||
172 | ret = iwm_key_init(key, key_index, mac_addr, params); | ||
173 | if (ret < 0) { | ||
174 | IWM_ERR(iwm, "Invalid key_params\n"); | ||
175 | return ret; | ||
176 | } | ||
177 | |||
178 | return iwm_set_key(iwm, 0, key); | ||
179 | } | ||
180 | |||
181 | static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, | ||
182 | u8 key_index, const u8 *mac_addr, void *cookie, | ||
183 | void (*callback)(void *cookie, | ||
184 | struct key_params*)) | ||
185 | { | ||
186 | struct iwm_priv *iwm = ndev_to_iwm(ndev); | ||
187 | struct iwm_key *key = &iwm->keys[key_index]; | ||
188 | struct key_params params; | ||
189 | |||
190 | IWM_DBG_WEXT(iwm, DBG, "Getting key %d\n", key_index); | ||
191 | |||
192 | memset(¶ms, 0, sizeof(params)); | ||
193 | |||
194 | params.cipher = key->cipher; | ||
195 | params.key_len = key->key_len; | ||
196 | params.seq_len = key->seq_len; | ||
197 | params.seq = key->seq; | ||
198 | params.key = key->key; | ||
199 | |||
200 | callback(cookie, ¶ms); | ||
201 | |||
202 | return key->key_len ? 0 : -ENOENT; | ||
203 | } | ||
204 | |||
205 | |||
206 | static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, | ||
207 | u8 key_index, const u8 *mac_addr) | ||
208 | { | ||
209 | struct iwm_priv *iwm = ndev_to_iwm(ndev); | ||
210 | struct iwm_key *key = &iwm->keys[key_index]; | ||
211 | |||
212 | if (!iwm->keys[key_index].key_len) { | ||
213 | IWM_DBG_WEXT(iwm, DBG, "Key %d not used\n", key_index); | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | if (key_index == iwm->default_key) | ||
218 | iwm->default_key = -1; | ||
219 | |||
220 | return iwm_set_key(iwm, 1, key); | ||
221 | } | ||
222 | |||
223 | static int iwm_cfg80211_set_default_key(struct wiphy *wiphy, | ||
224 | struct net_device *ndev, | ||
225 | u8 key_index) | ||
226 | { | ||
227 | struct iwm_priv *iwm = ndev_to_iwm(ndev); | ||
228 | |||
229 | IWM_DBG_WEXT(iwm, DBG, "Default key index is: %d\n", key_index); | ||
230 | |||
231 | if (!iwm->keys[key_index].key_len) { | ||
232 | IWM_ERR(iwm, "Key %d not used\n", key_index); | ||
233 | return -EINVAL; | ||
234 | } | ||
235 | |||
236 | iwm->default_key = key_index; | ||
237 | |||
238 | return iwm_set_tx_key(iwm, key_index); | ||
239 | } | ||
240 | |||
241 | int iwm_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, | ||
242 | u8 *mac, struct station_info *sinfo) | ||
243 | { | ||
244 | struct iwm_priv *iwm = ndev_to_iwm(ndev); | ||
245 | |||
246 | if (memcmp(mac, iwm->bssid, ETH_ALEN)) | ||
247 | return -ENOENT; | ||
248 | |||
249 | sinfo->filled |= STATION_INFO_TX_BITRATE; | ||
250 | sinfo->txrate.legacy = iwm->rate * 10; | ||
251 | |||
252 | if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { | ||
253 | sinfo->filled |= STATION_INFO_SIGNAL; | ||
254 | sinfo->signal = iwm->wstats.qual.level; | ||
255 | } | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | |||
133 | int iwm_cfg80211_inform_bss(struct iwm_priv *iwm) | 261 | int iwm_cfg80211_inform_bss(struct iwm_priv *iwm) |
134 | { | 262 | { |
135 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | 263 | struct wiphy *wiphy = iwm_to_wiphy(iwm); |
@@ -167,20 +295,15 @@ int iwm_cfg80211_inform_bss(struct iwm_priv *iwm) | |||
167 | return 0; | 295 | return 0; |
168 | } | 296 | } |
169 | 297 | ||
170 | static int iwm_cfg80211_change_iface(struct wiphy *wiphy, int ifindex, | 298 | static int iwm_cfg80211_change_iface(struct wiphy *wiphy, |
299 | struct net_device *ndev, | ||
171 | enum nl80211_iftype type, u32 *flags, | 300 | enum nl80211_iftype type, u32 *flags, |
172 | struct vif_params *params) | 301 | struct vif_params *params) |
173 | { | 302 | { |
174 | struct net_device *ndev; | ||
175 | struct wireless_dev *wdev; | 303 | struct wireless_dev *wdev; |
176 | struct iwm_priv *iwm; | 304 | struct iwm_priv *iwm; |
177 | u32 old_mode; | 305 | u32 old_mode; |
178 | 306 | ||
179 | /* we're under RTNL */ | ||
180 | ndev = __dev_get_by_index(&init_net, ifindex); | ||
181 | if (!ndev) | ||
182 | return -ENODEV; | ||
183 | |||
184 | wdev = ndev->ieee80211_ptr; | 307 | wdev = ndev->ieee80211_ptr; |
185 | iwm = ndev_to_iwm(ndev); | 308 | iwm = ndev_to_iwm(ndev); |
186 | old_mode = iwm->conf.mode; | 309 | old_mode = iwm->conf.mode; |
@@ -329,12 +452,250 @@ static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) | |||
329 | return 0; | 452 | return 0; |
330 | } | 453 | } |
331 | 454 | ||
455 | static int iwm_set_auth_type(struct iwm_priv *iwm, | ||
456 | enum nl80211_auth_type sme_auth_type) | ||
457 | { | ||
458 | u8 *auth_type = &iwm->umac_profile->sec.auth_type; | ||
459 | |||
460 | switch (sme_auth_type) { | ||
461 | case NL80211_AUTHTYPE_AUTOMATIC: | ||
462 | case NL80211_AUTHTYPE_OPEN_SYSTEM: | ||
463 | IWM_DBG_WEXT(iwm, DBG, "OPEN auth\n"); | ||
464 | *auth_type = UMAC_AUTH_TYPE_OPEN; | ||
465 | break; | ||
466 | case NL80211_AUTHTYPE_SHARED_KEY: | ||
467 | if (iwm->umac_profile->sec.flags & | ||
468 | (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) { | ||
469 | IWM_DBG_WEXT(iwm, DBG, "WPA auth alg\n"); | ||
470 | *auth_type = UMAC_AUTH_TYPE_RSNA_PSK; | ||
471 | } else { | ||
472 | IWM_DBG_WEXT(iwm, DBG, "WEP shared key auth alg\n"); | ||
473 | *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; | ||
474 | } | ||
475 | |||
476 | break; | ||
477 | default: | ||
478 | IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", sme_auth_type); | ||
479 | return -ENOTSUPP; | ||
480 | } | ||
481 | |||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version) | ||
486 | { | ||
487 | IWM_DBG_WEXT(iwm, DBG, "wpa_version: %d\n", wpa_version); | ||
488 | |||
489 | if (!wpa_version) { | ||
490 | iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE; | ||
491 | return 0; | ||
492 | } | ||
493 | |||
494 | if (wpa_version & NL80211_WPA_VERSION_2) | ||
495 | iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK; | ||
496 | |||
497 | if (wpa_version & NL80211_WPA_VERSION_1) | ||
498 | iwm->umac_profile->sec.flags |= UMAC_SEC_FLG_WPA_ON_MSK; | ||
499 | |||
500 | return 0; | ||
501 | } | ||
502 | |||
503 | static int iwm_set_cipher(struct iwm_priv *iwm, u32 cipher, bool ucast) | ||
504 | { | ||
505 | u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher : | ||
506 | &iwm->umac_profile->sec.mcast_cipher; | ||
507 | |||
508 | if (!cipher) { | ||
509 | *profile_cipher = UMAC_CIPHER_TYPE_NONE; | ||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | IWM_DBG_WEXT(iwm, DBG, "%ccast cipher is 0x%x\n", ucast ? 'u' : 'm', | ||
514 | cipher); | ||
515 | |||
516 | switch (cipher) { | ||
517 | case IW_AUTH_CIPHER_NONE: | ||
518 | *profile_cipher = UMAC_CIPHER_TYPE_NONE; | ||
519 | break; | ||
520 | case WLAN_CIPHER_SUITE_WEP40: | ||
521 | *profile_cipher = UMAC_CIPHER_TYPE_WEP_40; | ||
522 | break; | ||
523 | case WLAN_CIPHER_SUITE_WEP104: | ||
524 | *profile_cipher = UMAC_CIPHER_TYPE_WEP_104; | ||
525 | break; | ||
526 | case WLAN_CIPHER_SUITE_TKIP: | ||
527 | *profile_cipher = UMAC_CIPHER_TYPE_TKIP; | ||
528 | break; | ||
529 | case WLAN_CIPHER_SUITE_CCMP: | ||
530 | *profile_cipher = UMAC_CIPHER_TYPE_CCMP; | ||
531 | break; | ||
532 | default: | ||
533 | IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher); | ||
534 | return -ENOTSUPP; | ||
535 | } | ||
536 | |||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | static int iwm_set_key_mgt(struct iwm_priv *iwm, u32 key_mgt) | ||
541 | { | ||
542 | u8 *auth_type = &iwm->umac_profile->sec.auth_type; | ||
543 | |||
544 | IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt); | ||
545 | |||
546 | if (key_mgt == WLAN_AKM_SUITE_8021X) | ||
547 | *auth_type = UMAC_AUTH_TYPE_8021X; | ||
548 | else if (key_mgt == WLAN_AKM_SUITE_PSK) { | ||
549 | if (iwm->umac_profile->sec.flags & | ||
550 | (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) | ||
551 | *auth_type = UMAC_AUTH_TYPE_RSNA_PSK; | ||
552 | else | ||
553 | *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; | ||
554 | } else { | ||
555 | IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt); | ||
556 | return -EINVAL; | ||
557 | } | ||
558 | |||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | |||
563 | static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, | ||
564 | struct cfg80211_connect_params *sme) | ||
565 | { | ||
566 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
567 | struct ieee80211_channel *chan = sme->channel; | ||
568 | int ret; | ||
569 | |||
570 | if (!test_bit(IWM_STATUS_READY, &iwm->status)) | ||
571 | return -EIO; | ||
572 | |||
573 | if (!sme->ssid) | ||
574 | return -EINVAL; | ||
575 | |||
576 | if (chan) | ||
577 | iwm->channel = | ||
578 | ieee80211_frequency_to_channel(chan->center_freq); | ||
579 | |||
580 | iwm->umac_profile->ssid.ssid_len = sme->ssid_len; | ||
581 | memcpy(iwm->umac_profile->ssid.ssid, sme->ssid, sme->ssid_len); | ||
582 | |||
583 | if (sme->bssid) { | ||
584 | IWM_DBG_WEXT(iwm, DBG, "BSSID: %pM\n", sme->bssid); | ||
585 | memcpy(&iwm->umac_profile->bssid[0], sme->bssid, ETH_ALEN); | ||
586 | iwm->umac_profile->bss_num = 1; | ||
587 | } else { | ||
588 | memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN); | ||
589 | iwm->umac_profile->bss_num = 0; | ||
590 | } | ||
591 | |||
592 | ret = iwm_set_wpa_version(iwm, sme->crypto.wpa_versions); | ||
593 | if (ret < 0) | ||
594 | return ret; | ||
595 | |||
596 | ret = iwm_set_auth_type(iwm, sme->auth_type); | ||
597 | if (ret < 0) | ||
598 | return ret; | ||
599 | |||
600 | if (sme->crypto.n_ciphers_pairwise) { | ||
601 | ret = iwm_set_cipher(iwm, sme->crypto.ciphers_pairwise[0], | ||
602 | true); | ||
603 | if (ret < 0) | ||
604 | return ret; | ||
605 | } | ||
606 | |||
607 | ret = iwm_set_cipher(iwm, sme->crypto.cipher_group, false); | ||
608 | if (ret < 0) | ||
609 | return ret; | ||
610 | |||
611 | if (sme->crypto.n_akm_suites) { | ||
612 | ret = iwm_set_key_mgt(iwm, sme->crypto.akm_suites[0]); | ||
613 | if (ret < 0) | ||
614 | return ret; | ||
615 | } | ||
616 | |||
617 | return iwm_send_mlme_profile(iwm); | ||
618 | } | ||
619 | |||
620 | static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, | ||
621 | u16 reason_code) | ||
622 | { | ||
623 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
624 | |||
625 | IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active); | ||
626 | |||
627 | if (iwm->umac_profile_active) | ||
628 | return iwm_invalidate_mlme_profile(iwm); | ||
629 | |||
630 | return 0; | ||
631 | } | ||
632 | |||
633 | static int iwm_cfg80211_set_txpower(struct wiphy *wiphy, | ||
634 | enum tx_power_setting type, int dbm) | ||
635 | { | ||
636 | switch (type) { | ||
637 | case TX_POWER_AUTOMATIC: | ||
638 | return 0; | ||
639 | default: | ||
640 | return -EOPNOTSUPP; | ||
641 | } | ||
642 | |||
643 | return 0; | ||
644 | } | ||
645 | |||
646 | static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm) | ||
647 | { | ||
648 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
649 | |||
650 | *dbm = iwm->txpower; | ||
651 | |||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy, | ||
656 | struct net_device *dev, | ||
657 | bool enabled, int timeout) | ||
658 | { | ||
659 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
660 | u32 power_index; | ||
661 | |||
662 | if (enabled) | ||
663 | power_index = IWM_POWER_INDEX_DEFAULT; | ||
664 | else | ||
665 | power_index = IWM_POWER_INDEX_MIN; | ||
666 | |||
667 | if (power_index == iwm->conf.power_index) | ||
668 | return 0; | ||
669 | |||
670 | iwm->conf.power_index = power_index; | ||
671 | |||
672 | return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
673 | CFG_POWER_INDEX, iwm->conf.power_index); | ||
674 | } | ||
675 | |||
332 | static struct cfg80211_ops iwm_cfg80211_ops = { | 676 | static struct cfg80211_ops iwm_cfg80211_ops = { |
333 | .change_virtual_intf = iwm_cfg80211_change_iface, | 677 | .change_virtual_intf = iwm_cfg80211_change_iface, |
678 | .add_key = iwm_cfg80211_add_key, | ||
679 | .get_key = iwm_cfg80211_get_key, | ||
680 | .del_key = iwm_cfg80211_del_key, | ||
681 | .set_default_key = iwm_cfg80211_set_default_key, | ||
682 | .get_station = iwm_cfg80211_get_station, | ||
334 | .scan = iwm_cfg80211_scan, | 683 | .scan = iwm_cfg80211_scan, |
335 | .set_wiphy_params = iwm_cfg80211_set_wiphy_params, | 684 | .set_wiphy_params = iwm_cfg80211_set_wiphy_params, |
685 | .connect = iwm_cfg80211_connect, | ||
686 | .disconnect = iwm_cfg80211_disconnect, | ||
336 | .join_ibss = iwm_cfg80211_join_ibss, | 687 | .join_ibss = iwm_cfg80211_join_ibss, |
337 | .leave_ibss = iwm_cfg80211_leave_ibss, | 688 | .leave_ibss = iwm_cfg80211_leave_ibss, |
689 | .set_tx_power = iwm_cfg80211_set_txpower, | ||
690 | .get_tx_power = iwm_cfg80211_get_txpower, | ||
691 | .set_power_mgmt = iwm_cfg80211_set_power_mgmt, | ||
692 | }; | ||
693 | |||
694 | static const u32 cipher_suites[] = { | ||
695 | WLAN_CIPHER_SUITE_WEP40, | ||
696 | WLAN_CIPHER_SUITE_WEP104, | ||
697 | WLAN_CIPHER_SUITE_TKIP, | ||
698 | WLAN_CIPHER_SUITE_CCMP, | ||
338 | }; | 699 | }; |
339 | 700 | ||
340 | struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev) | 701 | struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev) |
@@ -379,6 +740,9 @@ struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev) | |||
379 | wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz; | 740 | wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz; |
380 | wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; | 741 | wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; |
381 | 742 | ||
743 | wdev->wiphy->cipher_suites = cipher_suites; | ||
744 | wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); | ||
745 | |||
382 | ret = wiphy_register(wdev->wiphy); | 746 | ret = wiphy_register(wdev->wiphy); |
383 | if (ret < 0) { | 747 | if (ret < 0) { |
384 | dev_err(dev, "Couldn't register wiphy device\n"); | 748 | dev_err(dev, "Couldn't register wiphy device\n"); |