diff options
Diffstat (limited to 'net/wireless/sme.c')
-rw-r--r-- | net/wireless/sme.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/net/wireless/sme.c b/net/wireless/sme.c new file mode 100644 index 000000000000..fc117031d0bb --- /dev/null +++ b/net/wireless/sme.c | |||
@@ -0,0 +1,224 @@ | |||
1 | /* | ||
2 | * SME code for cfg80211's connect emulation. | ||
3 | * | ||
4 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
6 | */ | ||
7 | |||
8 | #include <linux/etherdevice.h> | ||
9 | #include <linux/if_arp.h> | ||
10 | #include <linux/workqueue.h> | ||
11 | #include <net/cfg80211.h> | ||
12 | #include <net/rtnetlink.h> | ||
13 | #include "nl80211.h" | ||
14 | |||
15 | |||
16 | void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | ||
17 | const u8 *req_ie, size_t req_ie_len, | ||
18 | const u8 *resp_ie, size_t resp_ie_len, | ||
19 | u16 status, gfp_t gfp) | ||
20 | { | ||
21 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
22 | struct cfg80211_bss *bss; | ||
23 | #ifdef CONFIG_WIRELESS_EXT | ||
24 | union iwreq_data wrqu; | ||
25 | #endif | ||
26 | |||
27 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) | ||
28 | return; | ||
29 | |||
30 | if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING)) | ||
31 | return; | ||
32 | |||
33 | if (wdev->current_bss) { | ||
34 | cfg80211_unhold_bss(wdev->current_bss); | ||
35 | cfg80211_put_bss(wdev->current_bss); | ||
36 | wdev->current_bss = NULL; | ||
37 | } | ||
38 | |||
39 | if (status == WLAN_STATUS_SUCCESS) { | ||
40 | bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, | ||
41 | wdev->ssid, wdev->ssid_len, | ||
42 | WLAN_CAPABILITY_ESS, | ||
43 | WLAN_CAPABILITY_ESS); | ||
44 | |||
45 | if (WARN_ON(!bss)) | ||
46 | return; | ||
47 | |||
48 | cfg80211_hold_bss(bss); | ||
49 | wdev->current_bss = bss; | ||
50 | |||
51 | wdev->sme_state = CFG80211_SME_CONNECTED; | ||
52 | } else { | ||
53 | wdev->sme_state = CFG80211_SME_IDLE; | ||
54 | } | ||
55 | |||
56 | nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, | ||
57 | req_ie, req_ie_len, resp_ie, resp_ie_len, | ||
58 | status, gfp); | ||
59 | |||
60 | #ifdef CONFIG_WIRELESS_EXT | ||
61 | if (req_ie && status == WLAN_STATUS_SUCCESS) { | ||
62 | memset(&wrqu, 0, sizeof(wrqu)); | ||
63 | wrqu.data.length = req_ie_len; | ||
64 | wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); | ||
65 | } | ||
66 | |||
67 | if (resp_ie && status == WLAN_STATUS_SUCCESS) { | ||
68 | memset(&wrqu, 0, sizeof(wrqu)); | ||
69 | wrqu.data.length = resp_ie_len; | ||
70 | wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); | ||
71 | } | ||
72 | |||
73 | memset(&wrqu, 0, sizeof(wrqu)); | ||
74 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
75 | if (bssid) | ||
76 | memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); | ||
77 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | ||
78 | #endif | ||
79 | } | ||
80 | EXPORT_SYMBOL(cfg80211_connect_result); | ||
81 | |||
82 | void cfg80211_roamed(struct net_device *dev, const u8 *bssid, | ||
83 | const u8 *req_ie, size_t req_ie_len, | ||
84 | const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) | ||
85 | { | ||
86 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
87 | struct cfg80211_bss *bss; | ||
88 | #ifdef CONFIG_WIRELESS_EXT | ||
89 | union iwreq_data wrqu; | ||
90 | #endif | ||
91 | |||
92 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) | ||
93 | return; | ||
94 | |||
95 | if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) | ||
96 | return; | ||
97 | |||
98 | /* internal error -- how did we get to CONNECTED w/o BSS? */ | ||
99 | if (WARN_ON(!wdev->current_bss)) { | ||
100 | return; | ||
101 | } | ||
102 | |||
103 | cfg80211_unhold_bss(wdev->current_bss); | ||
104 | cfg80211_put_bss(wdev->current_bss); | ||
105 | wdev->current_bss = NULL; | ||
106 | |||
107 | bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, | ||
108 | wdev->ssid, wdev->ssid_len, | ||
109 | WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); | ||
110 | |||
111 | if (WARN_ON(!bss)) | ||
112 | return; | ||
113 | |||
114 | cfg80211_hold_bss(bss); | ||
115 | wdev->current_bss = bss; | ||
116 | |||
117 | nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid, | ||
118 | req_ie, req_ie_len, resp_ie, resp_ie_len, gfp); | ||
119 | |||
120 | #ifdef CONFIG_WIRELESS_EXT | ||
121 | if (req_ie) { | ||
122 | memset(&wrqu, 0, sizeof(wrqu)); | ||
123 | wrqu.data.length = req_ie_len; | ||
124 | wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); | ||
125 | } | ||
126 | |||
127 | if (resp_ie) { | ||
128 | memset(&wrqu, 0, sizeof(wrqu)); | ||
129 | wrqu.data.length = resp_ie_len; | ||
130 | wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); | ||
131 | } | ||
132 | |||
133 | memset(&wrqu, 0, sizeof(wrqu)); | ||
134 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
135 | memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); | ||
136 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | ||
137 | #endif | ||
138 | } | ||
139 | EXPORT_SYMBOL(cfg80211_roamed); | ||
140 | |||
141 | static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, | ||
142 | u8 *ie, size_t ie_len, u16 reason, | ||
143 | bool from_ap) | ||
144 | { | ||
145 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
146 | #ifdef CONFIG_WIRELESS_EXT | ||
147 | union iwreq_data wrqu; | ||
148 | #endif | ||
149 | |||
150 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) | ||
151 | return; | ||
152 | |||
153 | if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) | ||
154 | return; | ||
155 | |||
156 | if (wdev->current_bss) { | ||
157 | cfg80211_unhold_bss(wdev->current_bss); | ||
158 | cfg80211_put_bss(wdev->current_bss); | ||
159 | } | ||
160 | |||
161 | wdev->current_bss = NULL; | ||
162 | wdev->sme_state = CFG80211_SME_IDLE; | ||
163 | |||
164 | nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, | ||
165 | reason, ie, ie_len, from_ap, gfp); | ||
166 | |||
167 | #ifdef CONFIG_WIRELESS_EXT | ||
168 | memset(&wrqu, 0, sizeof(wrqu)); | ||
169 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
170 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | ||
171 | #endif | ||
172 | } | ||
173 | |||
174 | void cfg80211_disconnected(struct net_device *dev, u16 reason, | ||
175 | u8 *ie, size_t ie_len, gfp_t gfp) | ||
176 | { | ||
177 | __cfg80211_disconnected(dev, reason, ie, ie_len, true, gfp); | ||
178 | } | ||
179 | EXPORT_SYMBOL(cfg80211_disconnected); | ||
180 | |||
181 | int cfg80211_connect(struct cfg80211_registered_device *rdev, | ||
182 | struct net_device *dev, | ||
183 | struct cfg80211_connect_params *connect) | ||
184 | { | ||
185 | int err; | ||
186 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
187 | |||
188 | if (wdev->sme_state != CFG80211_SME_IDLE) | ||
189 | return -EALREADY; | ||
190 | |||
191 | if (!rdev->ops->connect) { | ||
192 | return -EOPNOTSUPP; | ||
193 | } else { | ||
194 | wdev->sme_state = CFG80211_SME_CONNECTING; | ||
195 | err = rdev->ops->connect(&rdev->wiphy, dev, connect); | ||
196 | if (err) { | ||
197 | wdev->sme_state = CFG80211_SME_IDLE; | ||
198 | return err; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | memcpy(wdev->ssid, connect->ssid, connect->ssid_len); | ||
203 | wdev->ssid_len = connect->ssid_len; | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, | ||
209 | struct net_device *dev, u16 reason) | ||
210 | { | ||
211 | int err; | ||
212 | |||
213 | if (!rdev->ops->disconnect) { | ||
214 | return -EOPNOTSUPP; | ||
215 | } else { | ||
216 | err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); | ||
217 | if (err) | ||
218 | return err; | ||
219 | } | ||
220 | |||
221 | __cfg80211_disconnected(dev, 0, NULL, 0, false, GFP_KERNEL); | ||
222 | |||
223 | return 0; | ||
224 | } | ||