/* * SME code for cfg80211's connect emulation. * * Copyright 2009 Johannes Berg * Copyright (C) 2009 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include "nl80211.h" void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, u16 status, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING)) return; if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(wdev->current_bss); wdev->current_bss = NULL; } if (status == WLAN_STATUS_SUCCESS) { bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, wdev->ssid, wdev->ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); if (WARN_ON(!bss)) return; cfg80211_hold_bss(bss); wdev->current_bss = bss; wdev->sme_state = CFG80211_SME_CONNECTED; } else { wdev->sme_state = CFG80211_SME_IDLE; } nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, status, gfp); #ifdef CONFIG_WIRELESS_EXT if (req_ie && status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = req_ie_len; wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); } if (resp_ie && status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = resp_ie_len; wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); } memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; if (bssid) memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); #endif } EXPORT_SYMBOL(cfg80211_connect_result); void cfg80211_roamed(struct net_device *dev, const u8 *bssid, const u8 *req_ie, size_t req_ie_len, const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) return; /* internal error -- how did we get to CONNECTED w/o BSS? */ if (WARN_ON(!wdev->current_bss)) { return; } cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(wdev->current_bss); wdev->current_bss = NULL; bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, wdev->ssid, wdev->ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); if (WARN_ON(!bss)) return; cfg80211_hold_bss(bss); wdev->current_bss = bss; nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, gfp); #ifdef CONFIG_WIRELESS_EXT if (req_ie) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = req_ie_len; wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); } if (resp_ie) { memset(&wrqu, 0, sizeof(wrqu)); wrqu.data.length = resp_ie_len; wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); } memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); #endif } EXPORT_SYMBOL(cfg80211_roamed); static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, size_t ie_len, u16 reason, bool from_ap) { struct wireless_dev *wdev = dev->ieee80211_ptr; #ifdef CONFIG_WIRELESS_EXT union iwreq_data wrqu; #endif if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) return; if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) return; if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); cfg80211_put_bss(wdev->current_bss); } wdev->current_bss = NULL; wdev->sme_state = CFG80211_SME_IDLE; nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, reason, ie, ie_len, from_ap, gfp); #ifdef CONFIG_WIRELESS_EXT memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); #endif } void cfg80211_disconnected(struct net_device *dev, u16 reason, u8 *ie, size_t ie_len, gfp_t gfp) { __cfg80211_disconnected(dev, reason, ie, ie_len, true, gfp); } EXPORT_SYMBOL(cfg80211_disconnected); int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect) { int err; struct wireless_dev *wdev = dev->ieee80211_ptr; if (wdev->sme_state != CFG80211_SME_IDLE) return -EALREADY; if (!rdev->ops->connect) { return -EOPNOTSUPP; } else { wdev->sme_state = CFG80211_SME_CONNECTING; err = rdev->ops->connect(&rdev->wiphy, dev, connect); if (err) { wdev->sme_state = CFG80211_SME_IDLE; return err; } } memcpy(wdev->ssid, connect->ssid, connect->ssid_len); wdev->ssid_len = connect->ssid_len; return 0; } int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason) { int err; if (!rdev->ops->disconnect) { return -EOPNOTSUPP; } else { err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); if (err) return err; } __cfg80211_disconnected(dev, 0, NULL, 0, false, GFP_KERNEL); return 0; }