Commit 370121e5190a86a2d8a717ecd6f33028c7dc6fd4
Committed by
John W. Linville
1 parent
1c2e02750b
Exists in
master
and in
7 other branches
[PATCH] wireless: Add softmac layer to the kernel
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Showing 13 changed files with 2920 additions and 0 deletions Side-by-side Diff
- include/net/ieee80211softmac.h
- include/net/ieee80211softmac_wx.h
- net/ieee80211/Kconfig
- net/ieee80211/softmac/Kconfig
- net/ieee80211/softmac/Makefile
- net/ieee80211/softmac/ieee80211softmac_assoc.c
- net/ieee80211/softmac/ieee80211softmac_auth.c
- net/ieee80211/softmac/ieee80211softmac_event.c
- net/ieee80211/softmac/ieee80211softmac_io.c
- net/ieee80211/softmac/ieee80211softmac_module.c
- net/ieee80211/softmac/ieee80211softmac_priv.h
- net/ieee80211/softmac/ieee80211softmac_scan.c
- net/ieee80211/softmac/ieee80211softmac_wx.c
include/net/ieee80211softmac.h
1 | +#ifndef IEEE80211SOFTMAC_H_ | |
2 | +#define IEEE80211SOFTMAC_H_ | |
3 | + | |
4 | +#include <linux/kernel.h> | |
5 | +#include <linux/spinlock.h> | |
6 | +#include <linux/workqueue.h> | |
7 | +#include <linux/list.h> | |
8 | +#include <net/ieee80211.h> | |
9 | + | |
10 | +/* Once the API is considered more or less stable, | |
11 | + * this should be incremented on API incompatible changes. | |
12 | + */ | |
13 | +#define IEEE80211SOFTMAC_API 0 | |
14 | + | |
15 | +#define IEEE80211SOFTMAC_MAX_RATES_LEN 8 | |
16 | +#define IEEE80211SOFTMAC_MAX_EX_RATES_LEN 255 | |
17 | + | |
18 | +struct ieee80211softmac_ratesinfo { | |
19 | + u8 count; | |
20 | + u8 rates[IEEE80211SOFTMAC_MAX_RATES_LEN + IEEE80211SOFTMAC_MAX_EX_RATES_LEN]; | |
21 | +}; | |
22 | + | |
23 | +/* internal structures */ | |
24 | +struct ieee80211softmac_network; | |
25 | +struct ieee80211softmac_scaninfo; | |
26 | + | |
27 | +struct ieee80211softmac_essid { | |
28 | + u8 len; | |
29 | + char data[IW_ESSID_MAX_SIZE+1]; | |
30 | +}; | |
31 | + | |
32 | +struct ieee80211softmac_wpa { | |
33 | + char *IE; | |
34 | + int IElen; | |
35 | + int IEbuflen; | |
36 | +}; | |
37 | + | |
38 | +/* | |
39 | + * Information about association | |
40 | + * | |
41 | + * Do we need a lock for this? | |
42 | + * We only ever use this structure inlined | |
43 | + * into our global struct. I've used its lock, | |
44 | + * but maybe we need a local one here? | |
45 | + */ | |
46 | +struct ieee80211softmac_assoc_info { | |
47 | + /* | |
48 | + * This is the requested ESSID. It is written | |
49 | + * only by the WX handlers. | |
50 | + * | |
51 | + */ | |
52 | + struct ieee80211softmac_essid req_essid; | |
53 | + /* | |
54 | + * the ESSID of the network we're currently | |
55 | + * associated (or trying) to. This is | |
56 | + * updated to the network's actual ESSID | |
57 | + * even if the requested ESSID was 'ANY' | |
58 | + */ | |
59 | + struct ieee80211softmac_essid associate_essid; | |
60 | + | |
61 | + /* BSSID we're trying to associate to */ | |
62 | + char bssid[ETH_ALEN]; | |
63 | + | |
64 | + /* some flags. | |
65 | + * static_essid is valid if the essid is constant, | |
66 | + * this is for use by the wx handlers only. | |
67 | + * | |
68 | + * associating is true, if the network has been | |
69 | + * auth'ed on and we are in the process of associating. | |
70 | + * | |
71 | + * bssvalid is true if we found a matching network | |
72 | + * and saved it's BSSID into the bssid above. | |
73 | + */ | |
74 | + u8 static_essid:1, | |
75 | + associating:1, | |
76 | + bssvalid:1; | |
77 | + | |
78 | + /* Scan retries remaining */ | |
79 | + int scan_retry; | |
80 | + | |
81 | + struct work_struct work; | |
82 | + struct work_struct timeout; | |
83 | +}; | |
84 | + | |
85 | +enum { | |
86 | + IEEE80211SOFTMAC_AUTH_OPEN_REQUEST = 1, | |
87 | + IEEE80211SOFTMAC_AUTH_OPEN_RESPONSE = 2, | |
88 | +}; | |
89 | + | |
90 | +enum { | |
91 | + IEEE80211SOFTMAC_AUTH_SHARED_REQUEST = 1, | |
92 | + IEEE80211SOFTMAC_AUTH_SHARED_CHALLENGE = 2, | |
93 | + IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE = 3, | |
94 | + IEEE80211SOFTMAC_AUTH_SHARED_PASS = 4, | |
95 | +}; | |
96 | + | |
97 | +/* We should make these tunable | |
98 | + * AUTH_TIMEOUT seems really long, but that's what it is in BSD */ | |
99 | +#define IEEE80211SOFTMAC_AUTH_TIMEOUT (12 * HZ) | |
100 | +#define IEEE80211SOFTMAC_AUTH_RETRY_LIMIT 5 | |
101 | +#define IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT 3 | |
102 | + | |
103 | +struct ieee80211softmac_txrates { | |
104 | + /* The Bit-Rate to be used for multicast frames. */ | |
105 | + u8 mcast_rate; | |
106 | + /* The Bit-Rate to be used for multicast fallback | |
107 | + * (If the device supports fallback and hardware-retry) | |
108 | + */ | |
109 | + u8 mcast_fallback; | |
110 | + /* The Bit-Rate to be used for any other (normal) data packet. */ | |
111 | + u8 default_rate; | |
112 | + /* The Bit-Rate to be used for default fallback | |
113 | + * (If the device supports fallback and hardware-retry) | |
114 | + */ | |
115 | + u8 default_fallback; | |
116 | +}; | |
117 | + | |
118 | +/* Bits for txrates_change callback. */ | |
119 | +#define IEEE80211SOFTMAC_TXRATECHG_DEFAULT (1 << 0) /* default_rate */ | |
120 | +#define IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK (1 << 1) /* default_fallback */ | |
121 | +#define IEEE80211SOFTMAC_TXRATECHG_MCAST (1 << 2) /* mcast_rate */ | |
122 | +#define IEEE80211SOFTMAC_TXRATECHG_MCAST_FBACK (1 << 3) /* mcast_fallback */ | |
123 | + | |
124 | +struct ieee80211softmac_device { | |
125 | + /* 802.11 structure for data stuff */ | |
126 | + struct ieee80211_device *ieee; | |
127 | + struct net_device *dev; | |
128 | + | |
129 | + /* only valid if associated, then holds the Association ID */ | |
130 | + u16 association_id; | |
131 | + | |
132 | + /* the following methods are callbacks that the driver | |
133 | + * using this framework has to assign | |
134 | + */ | |
135 | + | |
136 | + /* always assign these */ | |
137 | + void (*set_bssid_filter)(struct net_device *dev, const u8 *bssid); | |
138 | + void (*set_channel)(struct net_device *dev, u8 channel); | |
139 | + | |
140 | + /* assign if you need it, informational only */ | |
141 | + void (*link_change)(struct net_device *dev); | |
142 | + | |
143 | + /* If the hardware can do scanning, assign _all_ three of these callbacks. | |
144 | + * When the scan finishes, call ieee80211softmac_scan_finished(). | |
145 | + */ | |
146 | + | |
147 | + /* when called, start_scan is guaranteed to not be called again | |
148 | + * until you call ieee80211softmac_scan_finished. | |
149 | + * Return 0 if scanning could start, error otherwise. | |
150 | + * SOFTMAC AUTHORS: don't call this, use ieee80211softmac_start_scan */ | |
151 | + int (*start_scan)(struct net_device *dev); | |
152 | + /* this should block until after ieee80211softmac_scan_finished was called | |
153 | + * SOFTMAC AUTHORS: don't call this, use ieee80211softmac_wait_for_scan */ | |
154 | + void (*wait_for_scan)(struct net_device *dev); | |
155 | + /* stop_scan aborts a scan, but is asynchronous. | |
156 | + * if you want to wait for it too, use wait_for_scan | |
157 | + * SOFTMAC AUTHORS: don't call this, use ieee80211softmac_stop_scan */ | |
158 | + void (*stop_scan)(struct net_device *dev); | |
159 | + | |
160 | + /* we'll need something about beacons here too, for AP or ad-hoc modes */ | |
161 | + | |
162 | + /* Transmission rates to be used by the driver. | |
163 | + * The SoftMAC figures out the best possible rates. | |
164 | + * The driver just needs to read them. | |
165 | + */ | |
166 | + struct ieee80211softmac_txrates txrates; | |
167 | + /* If the driver needs to do stuff on TX rate changes, assign this callback. */ | |
168 | + void (*txrates_change)(struct net_device *dev, | |
169 | + u32 changes, /* see IEEE80211SOFTMAC_TXRATECHG flags */ | |
170 | + const struct ieee80211softmac_txrates *rates_before_change); | |
171 | + | |
172 | + /* private stuff follows */ | |
173 | + /* this lock protects this structure */ | |
174 | + spinlock_t lock; | |
175 | + | |
176 | + /* couple of flags */ | |
177 | + u8 scanning:1, /* protects scanning from being done multiple times at once */ | |
178 | + associated:1; | |
179 | + | |
180 | + /* workquere for scannning, ... */ | |
181 | + struct workqueue_struct *workqueue; | |
182 | + | |
183 | + struct ieee80211softmac_scaninfo *scaninfo; | |
184 | + struct ieee80211softmac_assoc_info associnfo; | |
185 | + | |
186 | + struct list_head auth_queue; | |
187 | + struct list_head events; | |
188 | + | |
189 | + struct ieee80211softmac_ratesinfo ratesinfo; | |
190 | + int txrate_badness; | |
191 | + | |
192 | + /* WPA stuff */ | |
193 | + struct ieee80211softmac_wpa wpa; | |
194 | + | |
195 | + /* we need to keep a list of network structs we copied */ | |
196 | + struct list_head network_list; | |
197 | + | |
198 | + /* This must be the last item so that it points to the data | |
199 | + * allocated beyond this structure by alloc_ieee80211 */ | |
200 | + u8 priv[0]; | |
201 | +}; | |
202 | + | |
203 | +extern void ieee80211softmac_scan_finished(struct ieee80211softmac_device *sm); | |
204 | + | |
205 | +static inline void * ieee80211softmac_priv(struct net_device *dev) | |
206 | +{ | |
207 | + return ((struct ieee80211softmac_device *)ieee80211_priv(dev))->priv; | |
208 | +} | |
209 | + | |
210 | +extern struct net_device * alloc_ieee80211softmac(int sizeof_priv); | |
211 | +extern void free_ieee80211softmac(struct net_device *dev); | |
212 | + | |
213 | +/* Call this function if you detect a lost TX fragment. | |
214 | + * (If the device indicates failure of ACK RX, for example.) | |
215 | + * It is wise to call this function if you are able to detect lost packets, | |
216 | + * because it contributes to the TX Rates auto adjustment. | |
217 | + */ | |
218 | +extern void ieee80211softmac_fragment_lost(struct net_device *dev, | |
219 | + u16 wireless_sequence_number); | |
220 | +/* Call this function before _start to tell the softmac what rates | |
221 | + * the hw supports. The rates parameter is copied, so you can | |
222 | + * free it right after calling this function. | |
223 | + * Note that the rates need to be sorted. */ | |
224 | +extern void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates); | |
225 | + | |
226 | +/* Start the SoftMAC. Call this after you initialized the device | |
227 | + * and it is ready to run. | |
228 | + */ | |
229 | +extern void ieee80211softmac_start(struct net_device *dev); | |
230 | +/* Stop the SoftMAC. Call this before you shutdown the device. */ | |
231 | +extern void ieee80211softmac_stop(struct net_device *dev); | |
232 | + | |
233 | +/* | |
234 | + * Event system | |
235 | + */ | |
236 | + | |
237 | +/* valid event types */ | |
238 | +#define IEEE80211SOFTMAC_EVENT_ANY -1 /*private use only*/ | |
239 | +#define IEEE80211SOFTMAC_EVENT_SCAN_FINISHED 0 | |
240 | +#define IEEE80211SOFTMAC_EVENT_ASSOCIATED 1 | |
241 | +#define IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED 2 | |
242 | +#define IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT 3 | |
243 | +#define IEEE80211SOFTMAC_EVENT_AUTHENTICATED 4 | |
244 | +#define IEEE80211SOFTMAC_EVENT_AUTH_FAILED 5 | |
245 | +#define IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT 6 | |
246 | +#define IEEE80211SOFTMAC_EVENT_ASSOCIATE_NET_NOT_FOUND 7 | |
247 | +/* keep this updated! */ | |
248 | +#define IEEE80211SOFTMAC_EVENT_LAST 7 | |
249 | +/* | |
250 | + * If you want to be notified of certain events, you can call | |
251 | + * ieee80211softmac_notify[_atomic] with | |
252 | + * - event set to one of the constants below | |
253 | + * - fun set to a function pointer of the appropriate type | |
254 | + * - context set to the context data you want passed | |
255 | + * The return value is 0, or an error. | |
256 | + */ | |
257 | +typedef void (*notify_function_ptr)(struct net_device *dev, void *context); | |
258 | + | |
259 | +#define ieee80211softmac_notify(dev, event, fun, context) ieee80211softmac_notify_gfp(dev, event, fun, context, GFP_KERNEL); | |
260 | +#define ieee80211softmac_notify_atomic(dev, event, fun, context) ieee80211softmac_notify_gfp(dev, event, fun, context, GFP_ATOMIC); | |
261 | + | |
262 | +extern int ieee80211softmac_notify_gfp(struct net_device *dev, | |
263 | + int event, notify_function_ptr fun, void *context, gfp_t gfp_mask); | |
264 | + | |
265 | +/* To clear pending work (for ifconfig down, etc.) */ | |
266 | +extern void | |
267 | +ieee80211softmac_clear_pending_work(struct ieee80211softmac_device *sm); | |
268 | + | |
269 | +#endif /* IEEE80211SOFTMAC_H_ */ |
include/net/ieee80211softmac_wx.h
1 | +#ifndef _IEEE80211SOFTMAC_WX_H | |
2 | +#define _IEEE80211SOFTMAC_WX_H | |
3 | + | |
4 | +#include <net/ieee80211softmac.h> | |
5 | +#include <net/iw_handler.h> | |
6 | + | |
7 | +extern int | |
8 | +ieee80211softmac_wx_trigger_scan(struct net_device *net_dev, | |
9 | + struct iw_request_info *info, | |
10 | + union iwreq_data *data, | |
11 | + char *extra); | |
12 | + | |
13 | +extern int | |
14 | +ieee80211softmac_wx_get_scan_results(struct net_device *net_dev, | |
15 | + struct iw_request_info *info, | |
16 | + union iwreq_data *data, | |
17 | + char *extra); | |
18 | + | |
19 | +extern int | |
20 | +ieee80211softmac_wx_set_essid(struct net_device *net_dev, | |
21 | + struct iw_request_info *info, | |
22 | + union iwreq_data *data, | |
23 | + char *extra); | |
24 | + | |
25 | +extern int | |
26 | +ieee80211softmac_wx_get_essid(struct net_device *net_dev, | |
27 | + struct iw_request_info *info, | |
28 | + union iwreq_data *data, | |
29 | + char *extra); | |
30 | + | |
31 | +extern int | |
32 | +ieee80211softmac_wx_set_rate(struct net_device *net_dev, | |
33 | + struct iw_request_info *info, | |
34 | + union iwreq_data *data, | |
35 | + char *extra); | |
36 | + | |
37 | +extern int | |
38 | +ieee80211softmac_wx_get_rate(struct net_device *net_dev, | |
39 | + struct iw_request_info *info, | |
40 | + union iwreq_data *data, | |
41 | + char *extra); | |
42 | + | |
43 | +extern int | |
44 | +ieee80211softmac_wx_get_wap(struct net_device *net_dev, | |
45 | + struct iw_request_info *info, | |
46 | + union iwreq_data *data, | |
47 | + char *extra); | |
48 | + | |
49 | +extern int | |
50 | +ieee80211softmac_wx_set_wap(struct net_device *net_dev, | |
51 | + struct iw_request_info *info, | |
52 | + union iwreq_data *data, | |
53 | + char *extra); | |
54 | + | |
55 | +extern int | |
56 | +ieee80211softmac_wx_set_genie(struct net_device *dev, | |
57 | + struct iw_request_info *info, | |
58 | + union iwreq_data *wrqu, | |
59 | + char *extra); | |
60 | + | |
61 | +extern int | |
62 | +ieee80211softmac_wx_get_genie(struct net_device *dev, | |
63 | + struct iw_request_info *info, | |
64 | + union iwreq_data *wrqu, | |
65 | + char *extra); | |
66 | +#endif /* _IEEE80211SOFTMAC_WX */ |
net/ieee80211/Kconfig
net/ieee80211/softmac/Kconfig
1 | +config IEEE80211_SOFTMAC | |
2 | + tristate "Software MAC add-on to the IEEE 802.11 networking stack" | |
3 | + ---help--- | |
4 | + This option enables the hardware independent software MAC addon | |
5 | + for the IEEE 802.11 networking stack. | |
6 | + | |
7 | +config IEEE80211_SOFTMAC_DEBUG | |
8 | + bool "Enable full debugging output" | |
9 | + depends on IEEE80211_SOFTMAC |
net/ieee80211/softmac/Makefile
net/ieee80211/softmac/ieee80211softmac_assoc.c
1 | +#include "ieee80211softmac_priv.h" | |
2 | + | |
3 | +/* | |
4 | + * Overview | |
5 | + * | |
6 | + * Before you can associate, you have to authenticate. | |
7 | + * | |
8 | + */ | |
9 | + | |
10 | +/* Sends out an association request to the desired AP */ | |
11 | +static void | |
12 | +ieee80211softmac_assoc(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net) | |
13 | +{ | |
14 | + unsigned long flags; | |
15 | + function_enter(); | |
16 | + /* Switch to correct channel for this network */ | |
17 | + mac->set_channel(mac->dev, net->channel); | |
18 | + | |
19 | + /* Send association request */ | |
20 | + ieee80211softmac_send_mgt_frame(mac, net, IEEE80211_STYPE_ASSOC_REQ, 0); | |
21 | + | |
22 | + dprintk(KERN_INFO PFX "sent association request!\n"); | |
23 | + | |
24 | + /* Change the state to associating */ | |
25 | + spin_lock_irqsave(&mac->lock, flags); | |
26 | + mac->associnfo.associating = 1; | |
27 | + mac->associated = 0; /* just to make sure */ | |
28 | + spin_unlock_irqrestore(&mac->lock, flags); | |
29 | + | |
30 | + /* Set a timer for timeout */ | |
31 | + /* FIXME: make timeout configurable */ | |
32 | + queue_delayed_work(mac->workqueue, &mac->associnfo.timeout, 5 * HZ); | |
33 | +} | |
34 | + | |
35 | +void | |
36 | +ieee80211softmac_assoc_timeout(void *d) | |
37 | +{ | |
38 | + struct ieee80211softmac_device *mac = (struct ieee80211softmac_device *)d; | |
39 | + unsigned long flags; | |
40 | + | |
41 | + function_enter(); | |
42 | + | |
43 | + spin_lock_irqsave(&mac->lock, flags); | |
44 | + /* we might race against ieee80211softmac_handle_assoc_response, | |
45 | + * so make sure only one of us does something */ | |
46 | + if (!mac->associnfo.associating) { | |
47 | + spin_unlock_irqrestore(&mac->lock, flags); | |
48 | + return; | |
49 | + } | |
50 | + mac->associnfo.associating = 0; | |
51 | + mac->associnfo.bssvalid = 0; | |
52 | + mac->associated = 0; | |
53 | + spin_unlock_irqrestore(&mac->lock, flags); | |
54 | + | |
55 | + dprintk(KERN_INFO PFX "assoc request timed out!\n"); | |
56 | + /* FIXME: we need to know the network here. that requires a bit of restructuring */ | |
57 | + ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT, NULL); | |
58 | +} | |
59 | + | |
60 | +static void | |
61 | +ieee80211softmac_reassoc(struct ieee80211softmac_device *mac) | |
62 | +{ | |
63 | + function_enter(); | |
64 | +} | |
65 | + | |
66 | + | |
67 | +/* Sends out a disassociation request to the desired AP */ | |
68 | +static void | |
69 | +ieee80211softmac_disassoc(struct ieee80211softmac_device *mac, u16 reason) | |
70 | +{ | |
71 | + unsigned long flags; | |
72 | + struct ieee80211softmac_network *found; | |
73 | + function_enter(); | |
74 | + | |
75 | + if (mac->associnfo.bssvalid && mac->associated) { | |
76 | + found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid); | |
77 | + if (found) | |
78 | + ieee80211softmac_send_mgt_frame(mac, found, IEEE80211_STYPE_DISASSOC, reason); | |
79 | + } else if (mac->associnfo.associating) { | |
80 | + cancel_delayed_work(&mac->associnfo.timeout); | |
81 | + } | |
82 | + | |
83 | + /* Change our state */ | |
84 | + spin_lock_irqsave(&mac->lock, flags); | |
85 | + /* Do NOT clear bssvalid as that will break ieee80211softmac_assoc_work! */ | |
86 | + mac->associated = 0; | |
87 | + mac->associnfo.associating = 0; | |
88 | + spin_unlock_irqrestore(&mac->lock, flags); | |
89 | +} | |
90 | + | |
91 | +static inline int | |
92 | +we_support_all_basic_rates(struct ieee80211softmac_device *mac, u8 *from, u8 from_len) | |
93 | +{ | |
94 | + int idx, search, found; | |
95 | + u8 rate, search_rate; | |
96 | + | |
97 | + for (idx = 0; idx < (from_len); idx++) { | |
98 | + rate = (from)[idx]; | |
99 | + if (!(rate & IEEE80211_BASIC_RATE_MASK)) | |
100 | + continue; | |
101 | + found = 0; | |
102 | + rate &= ~IEEE80211_BASIC_RATE_MASK; | |
103 | + for (search = 0; search < mac->ratesinfo.count; search++) { | |
104 | + search_rate = mac->ratesinfo.rates[search]; | |
105 | + search_rate &= ~IEEE80211_BASIC_RATE_MASK; | |
106 | + if (rate == search_rate) { | |
107 | + found = 1; | |
108 | + break; | |
109 | + } | |
110 | + } | |
111 | + if (!found) | |
112 | + return 0; | |
113 | + } | |
114 | + return 1; | |
115 | +} | |
116 | + | |
117 | +static int | |
118 | +network_matches_request(struct ieee80211softmac_device *mac, struct ieee80211_network *net) | |
119 | +{ | |
120 | + /* we cannot associate to networks whose name we don't know */ | |
121 | + if (ieee80211_is_empty_essid(net->ssid, net->ssid_len)) | |
122 | + return 0; | |
123 | + /* do not associate to a network whose BSSBasicRateSet we cannot support */ | |
124 | + if (!we_support_all_basic_rates(mac, net->rates, net->rates_len)) | |
125 | + return 0; | |
126 | + /* do we really need to check the ex rates? */ | |
127 | + if (!we_support_all_basic_rates(mac, net->rates_ex, net->rates_ex_len)) | |
128 | + return 0; | |
129 | + | |
130 | + /* if 'ANY' network requested, take any that doesn't have privacy enabled */ | |
131 | + if (mac->associnfo.req_essid.len == 0 | |
132 | + && !(net->capability & WLAN_CAPABILITY_PRIVACY)) | |
133 | + return 1; | |
134 | + if (net->ssid_len != mac->associnfo.req_essid.len) | |
135 | + return 0; | |
136 | + if (!memcmp(net->ssid, mac->associnfo.req_essid.data, mac->associnfo.req_essid.len)) | |
137 | + return 1; | |
138 | + return 0; | |
139 | +} | |
140 | + | |
141 | +static void | |
142 | +ieee80211softmac_assoc_notify(struct net_device *dev, void *context) | |
143 | +{ | |
144 | + struct ieee80211softmac_device *mac = ieee80211_priv(dev); | |
145 | + ieee80211softmac_assoc_work((void*)mac); | |
146 | +} | |
147 | + | |
148 | +/* This function is called to handle userspace requests (asynchronously) */ | |
149 | +void | |
150 | +ieee80211softmac_assoc_work(void *d) | |
151 | +{ | |
152 | + struct ieee80211softmac_device *mac = (struct ieee80211softmac_device *)d; | |
153 | + struct ieee80211softmac_network *found = NULL; | |
154 | + struct ieee80211_network *net = NULL, *best = NULL; | |
155 | + unsigned long flags; | |
156 | + | |
157 | + function_enter(); | |
158 | + | |
159 | + /* meh */ | |
160 | + if (mac->associated) | |
161 | + ieee80211softmac_disassoc(mac, WLAN_REASON_DISASSOC_STA_HAS_LEFT); | |
162 | + | |
163 | + /* try to find the requested network in our list, if we found one already */ | |
164 | + if (mac->associnfo.bssvalid) | |
165 | + found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid); | |
166 | + | |
167 | + /* Search the ieee80211 networks for this network if we didn't find it */ | |
168 | + if (!found) | |
169 | + { | |
170 | + spin_lock_irqsave(&mac->ieee->lock, flags); | |
171 | + list_for_each_entry(net, &mac->ieee->network_list, list) { | |
172 | + /* we're supposed to find the network with | |
173 | + * the best signal here, as we're asked to join | |
174 | + * any network with a specific ESSID, and many | |
175 | + * different ones could have that. | |
176 | + * | |
177 | + * I'll for now implement just finding one at all | |
178 | + * | |
179 | + * We also should take into account the rateset | |
180 | + * here to find the best BSSID to try. | |
181 | + */ | |
182 | + if (network_matches_request(mac, net)) { | |
183 | + if (!best) { | |
184 | + best = net; | |
185 | + continue; | |
186 | + } | |
187 | + /* we already had a matching network, so | |
188 | + * compare their properties to get the | |
189 | + * better of the two ... (see above) | |
190 | + */ | |
191 | + /* TODO */ | |
192 | + /* for now, just */ | |
193 | + break; | |
194 | + } | |
195 | + } | |
196 | + /* if we unlock here, we might get interrupted and the `best' | |
197 | + * pointer could go stale */ | |
198 | + if (best) { | |
199 | + found = ieee80211softmac_create_network(mac, best); | |
200 | + /* if found is still NULL, then we got -ENOMEM somewhere */ | |
201 | + if (found) | |
202 | + ieee80211softmac_add_network(mac, found); | |
203 | + } | |
204 | + spin_unlock_irqrestore(&mac->ieee->lock, flags); | |
205 | + } | |
206 | + | |
207 | + if (!found) { | |
208 | + if (mac->associnfo.scan_retry > 0) { | |
209 | + spin_lock_irqsave(&mac->lock, flags); | |
210 | + mac->associnfo.scan_retry--; | |
211 | + spin_unlock_irqrestore(&mac->lock, flags); | |
212 | + | |
213 | + /* We know of no such network. Let's scan. | |
214 | + * NB: this also happens if we had no memory to copy the network info... | |
215 | + * Maybe we can hope to have more memory after scanning finishes ;) | |
216 | + */ | |
217 | + dprintk(KERN_INFO PFX "Associate: Network not known, trying to initiate scan: "); | |
218 | + ieee80211softmac_notify(mac->dev, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, ieee80211softmac_assoc_notify, NULL); | |
219 | + if (ieee80211softmac_start_scan(mac)) | |
220 | + dprintk("failed.\n"); | |
221 | + else | |
222 | + dprintk("ok.\n"); | |
223 | + return; | |
224 | + } | |
225 | + else { | |
226 | + spin_lock_irqsave(&mac->lock, flags); | |
227 | + mac->associnfo.associating = 0; | |
228 | + mac->associated = 0; | |
229 | + spin_unlock_irqrestore(&mac->lock, flags); | |
230 | + | |
231 | + dprintk(KERN_INFO PFX "Unable to find network after scan!\n"); | |
232 | + ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_NET_NOT_FOUND, NULL); | |
233 | + return; | |
234 | + } | |
235 | + } | |
236 | + | |
237 | + mac->associnfo.bssvalid = 1; | |
238 | + memcpy(mac->associnfo.bssid, found->bssid, ETH_ALEN); | |
239 | + /* copy the ESSID for displaying it */ | |
240 | + mac->associnfo.associate_essid.len = found->essid.len; | |
241 | + memcpy(mac->associnfo.associate_essid.data, found->essid.data, IW_ESSID_MAX_SIZE + 1); | |
242 | + | |
243 | + /* we found a network! authenticate (if necessary) and associate to it. */ | |
244 | + if (!found->authenticated) { | |
245 | + /* This relies on the fact that _auth_req only queues the work, | |
246 | + * otherwise adding the notification would be racy. */ | |
247 | + if (!ieee80211softmac_auth_req(mac, found)) { | |
248 | + dprintk(KERN_INFO PFX "cannot associate without being authenticated, requested authentication\n"); | |
249 | + ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify, NULL, GFP_KERNEL); | |
250 | + } else { | |
251 | + printkl(KERN_WARNING PFX "Not authenticated, but requesting authentication failed. Giving up to associate\n"); | |
252 | + ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, found); | |
253 | + } | |
254 | + return; | |
255 | + } | |
256 | + /* finally! now we can start associating */ | |
257 | + ieee80211softmac_assoc(mac, found); | |
258 | +} | |
259 | + | |
260 | +/* call this to do whatever is necessary when we're associated */ | |
261 | +static void | |
262 | +ieee80211softmac_associated(struct ieee80211softmac_device *mac, | |
263 | + struct ieee80211_assoc_response * resp, | |
264 | + struct ieee80211softmac_network *net) | |
265 | +{ | |
266 | + mac->associnfo.associating = 0; | |
267 | + mac->associated = 1; | |
268 | + if (mac->set_bssid_filter) | |
269 | + mac->set_bssid_filter(mac->dev, net->bssid); | |
270 | + memcpy(mac->ieee->bssid, net->bssid, ETH_ALEN); | |
271 | + mac->dev->flags |= IFF_RUNNING; | |
272 | + | |
273 | + mac->association_id = le16_to_cpup(&resp->aid); | |
274 | +} | |
275 | + | |
276 | +/* received frame handling functions */ | |
277 | +int | |
278 | +ieee80211softmac_handle_assoc_response(struct net_device * dev, | |
279 | + struct ieee80211_assoc_response * resp, | |
280 | + struct ieee80211_network * _ieee80211_network_do_not_use) | |
281 | +{ | |
282 | + /* NOTE: the network parameter has to be ignored by | |
283 | + * this code because it is the ieee80211's pointer | |
284 | + * to the struct, not ours (we made a copy) | |
285 | + */ | |
286 | + struct ieee80211softmac_device *mac = ieee80211_priv(dev); | |
287 | + u16 status = le16_to_cpup(&resp->status); | |
288 | + struct ieee80211softmac_network *network = NULL; | |
289 | + unsigned long flags; | |
290 | + | |
291 | + spin_lock_irqsave(&mac->lock, flags); | |
292 | + | |
293 | + if (!mac->associnfo.associating) { | |
294 | + /* we race against the timeout function, so make sure | |
295 | + * only one of us can do work */ | |
296 | + spin_unlock_irqrestore(&mac->lock, flags); | |
297 | + return 0; | |
298 | + } | |
299 | + network = ieee80211softmac_get_network_by_bssid_locked(mac, resp->header.addr3); | |
300 | + | |
301 | + /* someone sending us things without us knowing him? Ignore. */ | |
302 | + if (!network) { | |
303 | + dprintk(KERN_INFO PFX "Received unrequested assocation response from " MAC_FMT "\n", MAC_ARG(resp->header.addr3)); | |
304 | + spin_unlock_irqrestore(&mac->lock, flags); | |
305 | + return 0; | |
306 | + } | |
307 | + | |
308 | + /* now that we know it was for us, we can cancel the timeout */ | |
309 | + cancel_delayed_work(&mac->associnfo.timeout); | |
310 | + | |
311 | + switch (status) { | |
312 | + case 0: | |
313 | + dprintk(KERN_INFO PFX "associated!\n"); | |
314 | + ieee80211softmac_associated(mac, resp, network); | |
315 | + ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATED, network); | |
316 | + break; | |
317 | + case WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH: | |
318 | + if (!network->auth_desynced_once) { | |
319 | + /* there seem to be a few rare cases where our view of | |
320 | + * the world is obscured, or buggy APs that don't DEAUTH | |
321 | + * us properly. So we handle that, but allow it only once. | |
322 | + */ | |
323 | + printkl(KERN_INFO PFX "We were not authenticated during association, retrying...\n"); | |
324 | + network->authenticated = 0; | |
325 | + /* we don't want to do this more than once ... */ | |
326 | + network->auth_desynced_once = 1; | |
327 | + queue_work(mac->workqueue, &mac->associnfo.work); | |
328 | + break; | |
329 | + } | |
330 | + default: | |
331 | + dprintk(KERN_INFO PFX "associating failed (reason: 0x%x)!\n", status); | |
332 | + mac->associnfo.associating = 0; | |
333 | + mac->associnfo.bssvalid = 0; | |
334 | + mac->associated = 0; | |
335 | + ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, network); | |
336 | + } | |
337 | + | |
338 | + spin_unlock_irqrestore(&mac->lock, flags); | |
339 | + return 0; | |
340 | +} | |
341 | + | |
342 | +int | |
343 | +ieee80211softmac_handle_disassoc(struct net_device * dev, | |
344 | + struct ieee80211_disassoc *disassoc) | |
345 | +{ | |
346 | + struct ieee80211softmac_device *mac = ieee80211_priv(dev); | |
347 | + unsigned long flags; | |
348 | + dprintk(KERN_INFO PFX "got disassoc frame\n"); | |
349 | + | |
350 | + spin_lock_irqsave(&mac->lock, flags); | |
351 | + mac->associnfo.bssvalid = 0; | |
352 | + mac->associated = 0; | |
353 | + spin_unlock_irqrestore(&mac->lock, flags); | |
354 | + | |
355 | + return 0; | |
356 | +} |
net/ieee80211/softmac/ieee80211softmac_auth.c
1 | +#include "ieee80211softmac_priv.h" | |
2 | + | |
3 | +static void ieee80211softmac_auth_queue(void *data); | |
4 | + | |
5 | +/* Queues an auth request to the desired AP */ | |
6 | +int | |
7 | +ieee80211softmac_auth_req(struct ieee80211softmac_device *mac, | |
8 | + struct ieee80211softmac_network *net) | |
9 | +{ | |
10 | + struct ieee80211softmac_auth_queue_item *auth; | |
11 | + unsigned long flags; | |
12 | + | |
13 | + function_enter(); | |
14 | + | |
15 | + if (net->authenticating) | |
16 | + return 0; | |
17 | + | |
18 | + /* Add the network if it's not already added */ | |
19 | + ieee80211softmac_add_network(mac, net); | |
20 | + | |
21 | + dprintk(KERN_NOTICE PFX "Queueing Authentication Request to "MAC_FMT"\n", MAC_ARG(net->bssid)); | |
22 | + /* Queue the auth request */ | |
23 | + auth = (struct ieee80211softmac_auth_queue_item *) | |
24 | + kmalloc(sizeof(struct ieee80211softmac_auth_queue_item), GFP_KERNEL); | |
25 | + if(auth == NULL) | |
26 | + return -ENOMEM; | |
27 | + | |
28 | + auth->net = net; | |
29 | + auth->mac = mac; | |
30 | + auth->retry = IEEE80211SOFTMAC_AUTH_RETRY_LIMIT; | |
31 | + auth->state = IEEE80211SOFTMAC_AUTH_OPEN_REQUEST; | |
32 | + INIT_WORK(&auth->work, &ieee80211softmac_auth_queue, (void *)auth); | |
33 | + | |
34 | + /* Lock (for list) */ | |
35 | + spin_lock_irqsave(&mac->lock, flags); | |
36 | + | |
37 | + /* add to list */ | |
38 | + list_add_tail(&auth->list, &mac->auth_queue); | |
39 | + queue_work(mac->workqueue, &auth->work); | |
40 | + spin_unlock_irqrestore(&mac->lock, flags); | |
41 | + | |
42 | + return 0; | |
43 | +} | |
44 | + | |
45 | + | |
46 | +/* Sends an auth request to the desired AP and handles timeouts */ | |
47 | +static void | |
48 | +ieee80211softmac_auth_queue(void *data) | |
49 | +{ | |
50 | + struct ieee80211softmac_device *mac; | |
51 | + struct ieee80211softmac_auth_queue_item *auth; | |
52 | + struct ieee80211softmac_network *net; | |
53 | + unsigned long flags; | |
54 | + | |
55 | + function_enter(); | |
56 | + | |
57 | + auth = (struct ieee80211softmac_auth_queue_item *)data; | |
58 | + net = auth->net; | |
59 | + mac = auth->mac; | |
60 | + | |
61 | + if(auth->retry > 0) { | |
62 | + /* Switch to correct channel for this network */ | |
63 | + mac->set_channel(mac->dev, net->channel); | |
64 | + | |
65 | + /* Lock and set flags */ | |
66 | + spin_lock_irqsave(&mac->lock, flags); | |
67 | + net->authenticated = 0; | |
68 | + net->authenticating = 1; | |
69 | + /* add a timeout call so we eventually give up waiting for an auth reply */ | |
70 | + queue_delayed_work(mac->workqueue, &auth->work, IEEE80211SOFTMAC_AUTH_TIMEOUT); | |
71 | + auth->retry--; | |
72 | + spin_unlock_irqrestore(&mac->lock, flags); | |
73 | + if (ieee80211softmac_send_mgt_frame(mac, auth->net, IEEE80211_STYPE_AUTH, auth->state)) | |
74 | + dprintk(KERN_NOTICE PFX "Sending Authentication Request to "MAC_FMT" failed (this shouldn't happen, wait for the timeout).\n", MAC_ARG(net->bssid)); | |
75 | + else | |
76 | + dprintk(KERN_NOTICE PFX "Sent Authentication Request to "MAC_FMT".\n", MAC_ARG(net->bssid)); | |
77 | + return; | |
78 | + } | |
79 | + | |
80 | + printkl(KERN_WARNING PFX "Authentication timed out with "MAC_FMT"\n", MAC_ARG(net->bssid)); | |
81 | + /* Remove this item from the queue */ | |
82 | + spin_lock_irqsave(&mac->lock, flags); | |
83 | + ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT, net); | |
84 | + cancel_delayed_work(&auth->work); /* just to make sure... */ | |
85 | + list_del(&auth->list); | |
86 | + spin_unlock_irqrestore(&mac->lock, flags); | |
87 | + /* Free it */ | |
88 | + kfree(auth); | |
89 | +} | |
90 | + | |
91 | +/* Handle the auth response from the AP | |
92 | + * This should be registered with ieee80211 as handle_auth | |
93 | + */ | |
94 | +int | |
95 | +ieee80211softmac_auth_resp(struct net_device *dev, struct ieee80211_auth *auth) | |
96 | +{ | |
97 | + | |
98 | + struct list_head *list_ptr; | |
99 | + struct ieee80211softmac_device *mac = ieee80211_priv(dev); | |
100 | + struct ieee80211softmac_auth_queue_item *aq = NULL; | |
101 | + struct ieee80211softmac_network *net = NULL; | |
102 | + unsigned long flags; | |
103 | + u8 * data; | |
104 | + | |
105 | + function_enter(); | |
106 | + | |
107 | + /* Find correct auth queue item */ | |
108 | + spin_lock_irqsave(&mac->lock, flags); | |
109 | + list_for_each(list_ptr, &mac->auth_queue) { | |
110 | + aq = list_entry(list_ptr, struct ieee80211softmac_auth_queue_item, list); | |
111 | + net = aq->net; | |
112 | + if (!memcmp(net->bssid, auth->header.addr2, ETH_ALEN)) | |
113 | + break; | |
114 | + else | |
115 | + aq = NULL; | |
116 | + } | |
117 | + spin_unlock_irqrestore(&mac->lock, flags); | |
118 | + | |
119 | + /* Make sure that we've got an auth queue item for this request */ | |
120 | + if(aq == NULL) | |
121 | + { | |
122 | + printkl(KERN_DEBUG PFX "Authentication response received from "MAC_FMT" but no queue item exists.\n", MAC_ARG(auth->header.addr2)); | |
123 | + /* Error #? */ | |
124 | + return -1; | |
125 | + } | |
126 | + | |
127 | + /* Check for out of order authentication */ | |
128 | + if(!net->authenticating) | |
129 | + { | |
130 | + printkl(KERN_DEBUG PFX "Authentication response received from "MAC_FMT" but did not request authentication.\n",MAC_ARG(auth->header.addr2)); | |
131 | + return -1; | |
132 | + } | |
133 | + | |
134 | + /* Parse the auth packet */ | |
135 | + switch(auth->algorithm) { | |
136 | + case WLAN_AUTH_OPEN: | |
137 | + /* Check the status code of the response */ | |
138 | + | |
139 | + switch(auth->status) { | |
140 | + case WLAN_STATUS_SUCCESS: | |
141 | + /* Update the status to Authenticated */ | |
142 | + spin_lock_irqsave(&mac->lock, flags); | |
143 | + net->authenticating = 0; | |
144 | + net->authenticated = 1; | |
145 | + spin_unlock_irqrestore(&mac->lock, flags); | |
146 | + | |
147 | + /* Send event */ | |
148 | + printkl(KERN_NOTICE PFX "Open Authentication completed with "MAC_FMT"\n", MAC_ARG(net->bssid)); | |
149 | + ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_AUTHENTICATED, net); | |
150 | + break; | |
151 | + default: | |
152 | + /* Lock and reset flags */ | |
153 | + spin_lock_irqsave(&mac->lock, flags); | |
154 | + net->authenticated = 0; | |
155 | + net->authenticating = 0; | |
156 | + spin_unlock_irqrestore(&mac->lock, flags); | |
157 | + | |
158 | + printkl(KERN_NOTICE PFX "Open Authentication with "MAC_FMT" failed, error code: %i\n", | |
159 | + MAC_ARG(net->bssid), le16_to_cpup(&auth->status)); | |
160 | + /* Count the error? */ | |
161 | + break; | |
162 | + } | |
163 | + goto free_aq; | |
164 | + break; | |
165 | + case WLAN_AUTH_SHARED_KEY: | |
166 | + /* Figure out where we are in the process */ | |
167 | + switch(auth->transaction) { | |
168 | + case IEEE80211SOFTMAC_AUTH_SHARED_CHALLENGE: | |
169 | + /* Check to make sure we have a challenge IE */ | |
170 | + data = (u8 *)auth->info_element; | |
171 | + if(*data++ != MFIE_TYPE_CHALLENGE){ | |
172 | + printkl(KERN_NOTICE PFX "Shared Key Authentication failed due to a missing challenge.\n"); | |
173 | + break; | |
174 | + } | |
175 | + /* Save the challenge */ | |
176 | + spin_lock_irqsave(&mac->lock, flags); | |
177 | + net->challenge_len = *data++; | |
178 | + if(net->challenge_len > WLAN_AUTH_CHALLENGE_LEN) | |
179 | + net->challenge_len = WLAN_AUTH_CHALLENGE_LEN; | |
180 | + if(net->challenge != NULL) | |
181 | + kfree(net->challenge); | |
182 | + net->challenge = kmalloc(net->challenge_len, GFP_ATOMIC); | |
183 | + memcpy(net->challenge, data, net->challenge_len); | |
184 | + aq->state = IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE; | |
185 | + spin_unlock_irqrestore(&mac->lock, flags); | |
186 | + | |
187 | + /* Switch to correct channel for this network */ | |
188 | + mac->set_channel(mac->dev, net->channel); | |
189 | + | |
190 | + /* Send our response (How to encrypt?) */ | |
191 | + ieee80211softmac_send_mgt_frame(mac, aq->net, IEEE80211_STYPE_AUTH, aq->state); | |
192 | + break; | |
193 | + case IEEE80211SOFTMAC_AUTH_SHARED_PASS: | |
194 | + /* Check the status code of the response */ | |
195 | + switch(auth->status) { | |
196 | + case WLAN_STATUS_SUCCESS: | |
197 | + /* Update the status to Authenticated */ | |
198 | + spin_lock_irqsave(&mac->lock, flags); | |
199 | + net->authenticating = 0; | |
200 | + net->authenticated = 1; | |
201 | + spin_unlock_irqrestore(&mac->lock, flags); | |
202 | + printkl(KERN_NOTICE PFX "Shared Key Authentication completed with "MAC_FMT"\n", | |
203 | + MAC_ARG(net->bssid)); | |
204 | + break; | |
205 | + default: | |
206 | + printkl(KERN_NOTICE PFX "Shared Key Authentication with "MAC_FMT" failed, error code: %i\n", | |
207 | + MAC_ARG(net->bssid), le16_to_cpup(&auth->status)); | |
208 | + /* Lock and reset flags */ | |
209 | + spin_lock_irqsave(&mac->lock, flags); | |
210 | + net->authenticating = 0; | |
211 | + net->authenticated = 0; | |
212 | + spin_unlock_irqrestore(&mac->lock, flags); | |
213 | + /* Count the error? */ | |
214 | + break; | |
215 | + } | |
216 | + goto free_aq; | |
217 | + break; | |
218 | + default: | |
219 | + printkl(KERN_WARNING PFX "Unhandled Authentication Step: %i\n", auth->transaction); | |
220 | + break; | |
221 | + } | |
222 | + goto free_aq; | |
223 | + break; | |
224 | + default: | |
225 | + /* ERROR */ | |
226 | + goto free_aq; | |
227 | + break; | |
228 | + } | |
229 | + return 0; | |
230 | +free_aq: | |
231 | + /* Cancel the timeout */ | |
232 | + spin_lock_irqsave(&mac->lock, flags); | |
233 | + cancel_delayed_work(&aq->work); | |
234 | + /* Remove this item from the queue */ | |
235 | + list_del(&aq->list); | |
236 | + spin_unlock_irqrestore(&mac->lock, flags); | |
237 | + | |
238 | + /* Free it */ | |
239 | + kfree(aq); | |
240 | + return 0; | |
241 | +} | |
242 | + | |
243 | +/* | |
244 | + * Handle deauthorization | |
245 | + */ | |
246 | +void | |
247 | +ieee80211softmac_deauth_from_net(struct ieee80211softmac_device *mac, | |
248 | + struct ieee80211softmac_network *net) | |
249 | +{ | |
250 | + struct ieee80211softmac_auth_queue_item *aq = NULL; | |
251 | + struct list_head *list_ptr; | |
252 | + unsigned long flags; | |
253 | + | |
254 | + function_enter(); | |
255 | + | |
256 | + /* Lock and reset status flags */ | |
257 | + spin_lock_irqsave(&mac->lock, flags); | |
258 | + net->authenticating = 0; | |
259 | + net->authenticated = 0; | |
260 | + | |
261 | + /* Find correct auth queue item, if it exists */ | |
262 | + list_for_each(list_ptr, &mac->auth_queue) { | |
263 | + aq = list_entry(list_ptr, struct ieee80211softmac_auth_queue_item, list); | |
264 | + if (!memcmp(net->bssid, aq->net->bssid, ETH_ALEN)) | |
265 | + break; | |
266 | + else | |
267 | + aq = NULL; | |
268 | + } | |
269 | + | |
270 | + /* Cancel pending work */ | |
271 | + if(aq != NULL) | |
272 | + /* Not entirely safe? What about running work? */ | |
273 | + cancel_delayed_work(&aq->work); | |
274 | + | |
275 | + /* Free our network ref */ | |
276 | + ieee80211softmac_del_network_locked(mac, net); | |
277 | + if(net->challenge != NULL) | |
278 | + kfree(net->challenge); | |
279 | + kfree(net); | |
280 | + | |
281 | + /* let's try to re-associate */ | |
282 | + queue_work(mac->workqueue, &mac->associnfo.work); | |
283 | + spin_unlock_irqrestore(&mac->lock, flags); | |
284 | +} | |
285 | + | |
286 | +/* | |
287 | + * Sends a deauth request to the desired AP | |
288 | + */ | |
289 | +int | |
290 | +ieee80211softmac_deauth_req(struct ieee80211softmac_device *mac, | |
291 | + struct ieee80211softmac_network *net, int reason) | |
292 | +{ | |
293 | + int ret; | |
294 | + | |
295 | + function_enter(); | |
296 | + | |
297 | + /* Make sure the network is authenticated */ | |
298 | + if (!net->authenticated) | |
299 | + { | |
300 | + printkl(KERN_DEBUG PFX "Can't send deauthentication packet, network is not authenticated.\n"); | |
301 | + /* Error okay? */ | |
302 | + return -EPERM; | |
303 | + } | |
304 | + | |
305 | + /* Send the de-auth packet */ | |
306 | + if((ret = ieee80211softmac_send_mgt_frame(mac, net, IEEE80211_STYPE_DEAUTH, reason))) | |
307 | + return ret; | |
308 | + | |
309 | + ieee80211softmac_deauth_from_net(mac, net); | |
310 | + return 0; | |
311 | +} | |
312 | + | |
313 | +/* | |
314 | + * This should be registered with ieee80211 as handle_deauth | |
315 | + */ | |
316 | +int | |
317 | +ieee80211softmac_deauth_resp(struct net_device *dev, struct ieee80211_auth *auth) | |
318 | +{ | |
319 | + | |
320 | + struct ieee80211softmac_network *net = NULL; | |
321 | + struct ieee80211softmac_device *mac = ieee80211_priv(dev); | |
322 | + | |
323 | + function_enter(); | |
324 | + | |
325 | + if (!auth) { | |
326 | + dprintk("deauth without deauth packet. eek!\n"); | |
327 | + return 0; | |
328 | + } | |
329 | + | |
330 | + net = ieee80211softmac_get_network_by_bssid(mac, auth->header.addr2); | |
331 | + | |
332 | + if (net == NULL) { | |
333 | + printkl(KERN_DEBUG PFX "Recieved deauthentication packet from "MAC_FMT", but that network is unknown.\n", | |
334 | + MAC_ARG(auth->header.addr2)); | |
335 | + return 0; | |
336 | + } | |
337 | + | |
338 | + /* Make sure the network is authenticated */ | |
339 | + if(!net->authenticated) | |
340 | + { | |
341 | + printkl(KERN_DEBUG PFX "Can't perform deauthentication, network is not authenticated.\n"); | |
342 | + /* Error okay? */ | |
343 | + return -EPERM; | |
344 | + } | |
345 | + | |
346 | + ieee80211softmac_deauth_from_net(mac, net); | |
347 | + return 0; | |
348 | +} |
net/ieee80211/softmac/ieee80211softmac_event.c
1 | +#include "ieee80211softmac_priv.h" | |
2 | + | |
3 | +/* | |
4 | + * Event system | |
5 | + * Also see comments in public header file | |
6 | + * | |
7 | + * Each event has associated to it | |
8 | + * - an event type (see constants in public header) | |
9 | + * - an event context (see below) | |
10 | + * - the function to be called | |
11 | + * - a context (extra parameter to call the function with) | |
12 | + * - and the softmac struct | |
13 | + * | |
14 | + * The event context is private and can only be used from | |
15 | + * within this module. Its meaning varies with the event | |
16 | + * type: | |
17 | + * SCAN_FINISHED: no special meaning | |
18 | + * ASSOCIATED, | |
19 | + * ASSOCIATE_FAILED, | |
20 | + * ASSOCIATE_TIMEOUT, | |
21 | + * AUTHENTICATED, | |
22 | + * AUTH_FAILED, | |
23 | + * AUTH_TIMEOUT: a pointer to the network struct | |
24 | + * ... | |
25 | + * Code within this module can use the event context to be only | |
26 | + * called when the event is true for that specific context | |
27 | + * as per above table. | |
28 | + * If the event context is NULL, then the notification is always called, | |
29 | + * regardless of the event context. The event context is not passed to | |
30 | + * the callback, it is assumed that the context suffices. | |
31 | + * | |
32 | + * You can also use the event context only by setting the event type | |
33 | + * to -1 (private use only), in which case you'll be notified | |
34 | + * whenever the event context matches. | |
35 | + */ | |
36 | + | |
37 | +static char *event_descriptions[IEEE80211SOFTMAC_EVENT_LAST+1] = { | |
38 | + "scan finished", | |
39 | + "associated", | |
40 | + "associating failed", | |
41 | + "associating timed out", | |
42 | + "authenticated", | |
43 | + "authenticating failed", | |
44 | + "authenticating timed out", | |
45 | + "associating failed because no suitable network was found", | |
46 | +}; | |
47 | + | |
48 | + | |
49 | +static void | |
50 | +ieee80211softmac_notify_callback(void *d) | |
51 | +{ | |
52 | + struct ieee80211softmac_event event = *(struct ieee80211softmac_event*) d; | |
53 | + kfree(d); | |
54 | + | |
55 | + event.fun(event.mac->dev, event.context); | |
56 | +} | |
57 | + | |
58 | +int | |
59 | +ieee80211softmac_notify_internal(struct ieee80211softmac_device *mac, | |
60 | + int event, void *event_context, notify_function_ptr fun, void *context, gfp_t gfp_mask) | |
61 | +{ | |
62 | + struct ieee80211softmac_event *eventptr; | |
63 | + unsigned long flags; | |
64 | + | |
65 | + if (event < -1 || event > IEEE80211SOFTMAC_EVENT_LAST) | |
66 | + return -ENOSYS; | |
67 | + | |
68 | + if (!fun) | |
69 | + return -EINVAL; | |
70 | + | |
71 | + eventptr = kmalloc(sizeof(struct ieee80211softmac_event), gfp_mask); | |
72 | + if (!eventptr) | |
73 | + return -ENOMEM; | |
74 | + | |
75 | + eventptr->event_type = event; | |
76 | + INIT_WORK(&eventptr->work, ieee80211softmac_notify_callback, eventptr); | |
77 | + eventptr->fun = fun; | |
78 | + eventptr->context = context; | |
79 | + eventptr->mac = mac; | |
80 | + eventptr->event_context = event_context; | |
81 | + | |
82 | + spin_lock_irqsave(&mac->lock, flags); | |
83 | + list_add(&eventptr->list, &mac->events); | |
84 | + spin_unlock_irqrestore(&mac->lock, flags); | |
85 | + | |
86 | + return 0; | |
87 | +} | |
88 | + | |
89 | +int | |
90 | +ieee80211softmac_notify_gfp(struct net_device *dev, | |
91 | + int event, notify_function_ptr fun, void *context, gfp_t gfp_mask) | |
92 | +{ | |
93 | + struct ieee80211softmac_device *mac = ieee80211_priv(dev); | |
94 | + | |
95 | + if (event < 0 || event > IEEE80211SOFTMAC_EVENT_LAST) | |
96 | + return -ENOSYS; | |
97 | + | |
98 | + return ieee80211softmac_notify_internal(mac, event, NULL, fun, context, gfp_mask); | |
99 | +} | |
100 | +EXPORT_SYMBOL_GPL(ieee80211softmac_notify_gfp); | |
101 | + | |
102 | +/* private -- calling all callbacks that were specified */ | |
103 | +void | |
104 | +ieee80211softmac_call_events_locked(struct ieee80211softmac_device *mac, int event, void *event_ctx) | |
105 | +{ | |
106 | + struct ieee80211softmac_event *eventptr, *tmp; | |
107 | + union iwreq_data wrqu; | |
108 | + char *msg; | |
109 | + | |
110 | + if (event >= 0) { | |
111 | + msg = event_descriptions[event]; | |
112 | + wrqu.data.length = strlen(msg); | |
113 | + wireless_send_event(mac->dev, IWEVCUSTOM, &wrqu, msg); | |
114 | + } | |
115 | + | |
116 | + if (!list_empty(&mac->events)) | |
117 | + list_for_each_entry_safe(eventptr, tmp, &mac->events, list) { | |
118 | + if ((eventptr->event_type == event || eventptr->event_type == -1) | |
119 | + && (eventptr->event_context == NULL || eventptr->event_context == event_ctx)) { | |
120 | + list_del(&eventptr->list); | |
121 | + queue_work(mac->workqueue, &eventptr->work); | |
122 | + } | |
123 | + } | |
124 | +} | |
125 | + | |
126 | +void | |
127 | +ieee80211softmac_call_events(struct ieee80211softmac_device *mac, int event, void *event_ctx) | |
128 | +{ | |
129 | + unsigned long flags; | |
130 | + | |
131 | + spin_lock_irqsave(&mac->lock, flags); | |
132 | + ieee80211softmac_call_events_locked(mac, event, event_ctx); | |
133 | + | |
134 | + spin_unlock_irqrestore(&mac->lock, flags); | |
135 | +} |
net/ieee80211/softmac/ieee80211softmac_io.c
1 | +/* | |
2 | + * Some parts based on code from net80211 | |
3 | + * Copyright (c) 2001 Atsushi Onoe | |
4 | + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting | |
5 | + * All rights reserved. | |
6 | + * | |
7 | + * Redistribution and use in source and binary forms, with or without | |
8 | + * modification, are permitted provided that the following conditions | |
9 | + * are met: | |
10 | + * 1. Redistributions of source code must retain the above copyright | |
11 | + * notice, this list of conditions and the following disclaimer. | |
12 | + * 2. Redistributions in binary form must reproduce the above copyright | |
13 | + * notice, this list of conditions and the following disclaimer in the | |
14 | + * documentation and/or other materials provided with the distribution. | |
15 | + * 3. The name of the author may not be used to endorse or promote products | |
16 | + * derived from this software without specific prior written permission. | |
17 | + * | |
18 | + * Alternatively, this software may be distributed under the terms of the | |
19 | + * GNU General Public License ("GPL") version 2 as published by the Free | |
20 | + * Software Foundation. | |
21 | + * | |
22 | + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
23 | + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
24 | + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
25 | + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
26 | + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
27 | + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
28 | + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
29 | + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
30 | + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
31 | + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
32 | + * | |
33 | + */ | |
34 | + | |
35 | +#include "ieee80211softmac_priv.h" | |
36 | + | |
37 | +/* Helper functions for inserting data into the frames */ | |
38 | + | |
39 | +/* | |
40 | + * Adds an ESSID element to the frame | |
41 | + * | |
42 | + */ | |
43 | +static u8 * | |
44 | +ieee80211softmac_add_essid(u8 *dst, struct ieee80211softmac_essid *essid) | |
45 | +{ | |
46 | + if (essid) { | |
47 | + *dst++ = MFIE_TYPE_SSID; | |
48 | + *dst++ = essid->len; | |
49 | + memcpy(dst, essid->data, essid->len); | |
50 | + return dst+essid->len; | |
51 | + } else { | |
52 | + *dst++ = MFIE_TYPE_SSID; | |
53 | + *dst++ = 0; | |
54 | + return dst; | |
55 | + } | |
56 | +} | |
57 | + | |
58 | +/* Adds Supported Rates and if required Extended Rates Information Element | |
59 | + * to the frame, ASSUMES WE HAVE A SORTED LIST OF RATES */ | |
60 | +static u8 * | |
61 | +ieee80211softmac_frame_add_rates(u8 *dst, const struct ieee80211softmac_ratesinfo *r) | |
62 | +{ | |
63 | + int cck_len, ofdm_len; | |
64 | + *dst++ = MFIE_TYPE_RATES; | |
65 | + | |
66 | + for(cck_len=0; ieee80211_is_cck_rate(r->rates[cck_len]) && (cck_len < r->count);cck_len++); | |
67 | + | |
68 | + if(cck_len > IEEE80211SOFTMAC_MAX_RATES_LEN) | |
69 | + cck_len = IEEE80211SOFTMAC_MAX_RATES_LEN; | |
70 | + *dst++ = cck_len; | |
71 | + memcpy(dst, r->rates, cck_len); | |
72 | + dst += cck_len; | |
73 | + | |
74 | + if(cck_len < r->count){ | |
75 | + for (ofdm_len=0; ieee80211_is_ofdm_rate(r->rates[ofdm_len + cck_len]) && (ofdm_len + cck_len < r->count); ofdm_len++); | |
76 | + if (ofdm_len > 0) { | |
77 | + if (ofdm_len > IEEE80211SOFTMAC_MAX_EX_RATES_LEN) | |
78 | + ofdm_len = IEEE80211SOFTMAC_MAX_EX_RATES_LEN; | |
79 | + *dst++ = MFIE_TYPE_RATES_EX; | |
80 | + *dst++ = ofdm_len; | |
81 | + memcpy(dst, r->rates + cck_len, ofdm_len); | |
82 | + dst += ofdm_len; | |
83 | + } | |
84 | + } | |
85 | + return dst; | |
86 | +} | |
87 | + | |
88 | +/* Allocate a management frame */ | |
89 | +static u8 * | |
90 | +ieee80211softmac_alloc_mgt(u32 size) | |
91 | +{ | |
92 | + u8 * data; | |
93 | + | |
94 | + /* Add the header and FCS to the size */ | |
95 | + size = size + IEEE80211_3ADDR_LEN; | |
96 | + if(size > IEEE80211_DATA_LEN) | |
97 | + return NULL; | |
98 | + /* Allocate the frame */ | |
99 | + data = kmalloc(size, GFP_ATOMIC); | |
100 | + memset(data, 0, size); | |
101 | + return data; | |
102 | +} | |
103 | + | |
104 | +/* | |
105 | + * Add a 2 Address Header | |
106 | + */ | |
107 | +static void | |
108 | +ieee80211softmac_hdr_2addr(struct ieee80211softmac_device *mac, | |
109 | + struct ieee80211_hdr_2addr *header, u32 type, u8 *dest) | |
110 | +{ | |
111 | + /* Fill in the frame control flags */ | |
112 | + header->frame_ctl = cpu_to_le16(type); | |
113 | + /* Control packets always have WEP turned off */ | |
114 | + if(type > IEEE80211_STYPE_CFENDACK && type < IEEE80211_STYPE_PSPOLL) | |
115 | + header->frame_ctl |= mac->ieee->sec.level ? cpu_to_le16(IEEE80211_FCTL_PROTECTED) : 0; | |
116 | + | |
117 | + /* Fill in the duration */ | |
118 | + header->duration_id = 0; | |
119 | + /* FIXME: How do I find this? | |
120 | + * calculate. But most drivers just fill in 0 (except if it's a station id of course) */ | |
121 | + | |
122 | + /* Fill in the Destination Address */ | |
123 | + if(dest == NULL) | |
124 | + memset(header->addr1, 0xFF, ETH_ALEN); | |
125 | + else | |
126 | + memcpy(header->addr1, dest, ETH_ALEN); | |
127 | + /* Fill in the Source Address */ | |
128 | + memcpy(header->addr2, mac->ieee->dev->dev_addr, ETH_ALEN); | |
129 | + | |
130 | +} | |
131 | + | |
132 | + | |
133 | +/* Add a 3 Address Header */ | |
134 | +static void | |
135 | +ieee80211softmac_hdr_3addr(struct ieee80211softmac_device *mac, | |
136 | + struct ieee80211_hdr_3addr *header, u32 type, u8 *dest, u8 *bssid) | |
137 | +{ | |
138 | + /* This is common with 2addr, so use that instead */ | |
139 | + ieee80211softmac_hdr_2addr(mac, (struct ieee80211_hdr_2addr *)header, type, dest); | |
140 | + | |
141 | + /* Fill in the BSS ID */ | |
142 | + if(bssid == NULL) | |
143 | + memset(header->addr3, 0xFF, ETH_ALEN); | |
144 | + else | |
145 | + memcpy(header->addr3, bssid, ETH_ALEN); | |
146 | + | |
147 | + /* Fill in the sequence # */ | |
148 | + /* FIXME: I need to add this to the softmac struct | |
149 | + * shouldn't the sequence number be in ieee80211? */ | |
150 | +} | |
151 | + | |
152 | + | |
153 | +/***************************************************************************** | |
154 | + * Create Management packets | |
155 | + *****************************************************************************/ | |
156 | + | |
157 | +/* Creates an association request packet */ | |
158 | +u32 | |
159 | +ieee80211softmac_assoc_req(struct ieee80211_assoc_request **pkt, | |
160 | + struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net) | |
161 | +{ | |
162 | + u8 *data; | |
163 | + (*pkt) = (struct ieee80211_assoc_request *)ieee80211softmac_alloc_mgt( | |
164 | + 2 + /* Capability Info */ | |
165 | + 2 + /* Listen Interval */ | |
166 | + /* SSID IE */ | |
167 | + 1 + 1 + IW_ESSID_MAX_SIZE + | |
168 | + /* Rates IE */ | |
169 | + 1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN + | |
170 | + /* Extended Rates IE */ | |
171 | + 1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN + | |
172 | + /* WPA IE if present */ | |
173 | + mac->wpa.IElen | |
174 | + /* Other IE's? Optional? | |
175 | + * Yeah, probably need an extra IE parameter -- lots of vendors like to | |
176 | + * fill in their own IEs */ | |
177 | + ); | |
178 | + if (unlikely((*pkt) == NULL)) | |
179 | + return 0; | |
180 | + ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_ASSOC_REQ, net->bssid, net->bssid); | |
181 | + | |
182 | + /* Fill in capability Info */ | |
183 | + (*pkt)->capability = (mac->ieee->iw_mode == IW_MODE_MASTER) || (mac->ieee->iw_mode == IW_MODE_INFRA) ? | |
184 | + cpu_to_le16(WLAN_CAPABILITY_ESS) : | |
185 | + cpu_to_le16(WLAN_CAPABILITY_IBSS); | |
186 | + /* Need to add this | |
187 | + (*pkt)->capability |= mac->ieee->short_slot ? | |
188 | + cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME) : 0; | |
189 | + */ | |
190 | + (*pkt)->capability |= mac->ieee->sec.level ? cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0; | |
191 | + /* Fill in Listen Interval (?) */ | |
192 | + (*pkt)->listen_interval = cpu_to_le16(10); | |
193 | + | |
194 | + data = (u8 *)(*pkt)->info_element; | |
195 | + /* Add SSID */ | |
196 | + data = ieee80211softmac_add_essid(data, &net->essid); | |
197 | + /* Add Rates */ | |
198 | + data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo); | |
199 | + /* Add WPA IE */ | |
200 | + if (mac->wpa.IElen && mac->wpa.IE) { | |
201 | + memcpy(data, mac->wpa.IE, mac->wpa.IElen); | |
202 | + data += mac->wpa.IElen; | |
203 | + } | |
204 | + /* Return the number of used bytes */ | |
205 | + return (data - (u8*)(*pkt)); | |
206 | +} | |
207 | + | |
208 | +/* Create a reassociation request packet */ | |
209 | +u32 | |
210 | +ieee80211softmac_reassoc_req(struct ieee80211_reassoc_request **pkt, | |
211 | + struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net) | |
212 | +{ | |
213 | + u8 *data; | |
214 | + (*pkt) = (struct ieee80211_reassoc_request *)ieee80211softmac_alloc_mgt( | |
215 | + 2 + /* Capability Info */ | |
216 | + 2 + /* Listen Interval */ | |
217 | + ETH_ALEN + /* AP MAC */ | |
218 | + /* SSID IE */ | |
219 | + 1 + 1 + IW_ESSID_MAX_SIZE + | |
220 | + /* Rates IE */ | |
221 | + 1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN + | |
222 | + /* Extended Rates IE */ | |
223 | + 1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN | |
224 | + /* Other IE's? */ | |
225 | + ); | |
226 | + if (unlikely((*pkt) == NULL)) | |
227 | + return 0; | |
228 | + ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_REASSOC_REQ, net->bssid, net->bssid); | |
229 | + | |
230 | + /* Fill in capability Info */ | |
231 | + (*pkt)->capability = mac->ieee->iw_mode == IW_MODE_MASTER ? | |
232 | + cpu_to_le16(WLAN_CAPABILITY_ESS) : | |
233 | + cpu_to_le16(WLAN_CAPABILITY_IBSS); | |
234 | + /* | |
235 | + (*pkt)->capability |= mac->ieee->short_slot ? | |
236 | + cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME) : 0; | |
237 | + */ | |
238 | + (*pkt)->capability |= mac->ieee->sec.level ? | |
239 | + cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0; | |
240 | + | |
241 | + /* Fill in Listen Interval (?) */ | |
242 | + (*pkt)->listen_interval = cpu_to_le16(10); | |
243 | + /* Fill in the current AP MAC */ | |
244 | + memcpy((*pkt)->current_ap, mac->ieee->bssid, ETH_ALEN); | |
245 | + | |
246 | + data = (u8 *)(*pkt)->info_element; | |
247 | + /* Add SSID */ | |
248 | + data = ieee80211softmac_add_essid(data, &net->essid); | |
249 | + /* Add Rates */ | |
250 | + data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo); | |
251 | + /* Return packet size */ | |
252 | + return (data - (u8 *)(*pkt)); | |
253 | +} | |
254 | + | |
255 | +/* Create an authentication packet */ | |
256 | +u32 | |
257 | +ieee80211softmac_auth(struct ieee80211_auth **pkt, | |
258 | + struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net, | |
259 | + u16 transaction, u16 status) | |
260 | +{ | |
261 | + u8 *data; | |
262 | + /* Allocate Packet */ | |
263 | + (*pkt) = (struct ieee80211_auth *)ieee80211softmac_alloc_mgt( | |
264 | + 2 + /* Auth Algorithm */ | |
265 | + 2 + /* Auth Transaction Seq */ | |
266 | + 2 + /* Status Code */ | |
267 | + /* Challenge Text IE */ | |
268 | + mac->ieee->open_wep ? 0 : | |
269 | + 1 + 1 + WLAN_AUTH_CHALLENGE_LEN | |
270 | + ); | |
271 | + if (unlikely((*pkt) == NULL)) | |
272 | + return 0; | |
273 | + ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_AUTH, net->bssid, net->bssid); | |
274 | + | |
275 | + /* Algorithm */ | |
276 | + (*pkt)->algorithm = mac->ieee->open_wep ? | |
277 | + cpu_to_le16(WLAN_AUTH_OPEN) : | |
278 | + cpu_to_le16(WLAN_AUTH_SHARED_KEY); | |
279 | + /* Transaction */ | |
280 | + (*pkt)->transaction = cpu_to_le16(transaction); | |
281 | + /* Status */ | |
282 | + (*pkt)->status = cpu_to_le16(status); | |
283 | + | |
284 | + data = (u8 *)(*pkt)->info_element; | |
285 | + /* Challenge Text */ | |
286 | + if(!mac->ieee->open_wep){ | |
287 | + *data = MFIE_TYPE_CHALLENGE; | |
288 | + data++; | |
289 | + | |
290 | + /* Copy the challenge in */ | |
291 | + // *data = challenge length | |
292 | + // data += sizeof(u16); | |
293 | + // memcpy(data, challenge, challenge length); | |
294 | + // data += challenge length; | |
295 | + | |
296 | + /* Add the full size to the packet length */ | |
297 | + } | |
298 | + | |
299 | + /* Return the packet size */ | |
300 | + return (data - (u8 *)(*pkt)); | |
301 | +} | |
302 | + | |
303 | +/* Create a disassocation or deauthentication packet */ | |
304 | +u32 | |
305 | +ieee80211softmac_disassoc_deauth(struct ieee80211_disassoc **pkt, | |
306 | + struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net, | |
307 | + u16 type, u16 reason) | |
308 | +{ | |
309 | + /* Allocate Packet */ | |
310 | + (*pkt) = (struct ieee80211_disassoc *)ieee80211softmac_alloc_mgt(2); | |
311 | + if (unlikely(pkt == NULL)) | |
312 | + return 0; | |
313 | + ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), type, net->bssid, net->bssid); | |
314 | + /* Reason */ | |
315 | + (*pkt)->reason = cpu_to_le16(reason); | |
316 | + /* Return the packet size */ | |
317 | + return (2 + IEEE80211_3ADDR_LEN); | |
318 | +} | |
319 | + | |
320 | +/* Create a probe request packet */ | |
321 | +u32 | |
322 | +ieee80211softmac_probe_req(struct ieee80211_probe_request **pkt, | |
323 | + struct ieee80211softmac_device *mac, struct ieee80211softmac_essid *essid) | |
324 | +{ | |
325 | + u8 *data; | |
326 | + /* Allocate Packet */ | |
327 | + (*pkt) = (struct ieee80211_probe_request *)ieee80211softmac_alloc_mgt( | |
328 | + /* SSID of requested network */ | |
329 | + 1 + 1 + IW_ESSID_MAX_SIZE + | |
330 | + /* Rates IE */ | |
331 | + 1 + 1 + IEEE80211SOFTMAC_MAX_RATES_LEN + | |
332 | + /* Extended Rates IE */ | |
333 | + 1 + 1 + IEEE80211SOFTMAC_MAX_EX_RATES_LEN | |
334 | + ); | |
335 | + if (unlikely((*pkt) == NULL)) | |
336 | + return 0; | |
337 | + ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_PROBE_REQ, NULL, NULL); | |
338 | + | |
339 | + data = (u8 *)(*pkt)->info_element; | |
340 | + /* Add ESSID (can be NULL) */ | |
341 | + data = ieee80211softmac_add_essid(data, essid); | |
342 | + /* Add Rates */ | |
343 | + data = ieee80211softmac_frame_add_rates(data, &mac->ratesinfo); | |
344 | + /* Return packet size */ | |
345 | + return (data - (u8 *)(*pkt)); | |
346 | +} | |
347 | + | |
348 | +/* Create a probe response packet */ | |
349 | +/* FIXME: Not complete */ | |
350 | +u32 | |
351 | +ieee80211softmac_probe_resp(struct ieee80211_probe_response **pkt, | |
352 | + struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net) | |
353 | +{ | |
354 | + u8 *data; | |
355 | + /* Allocate Packet */ | |
356 | + (*pkt) = (struct ieee80211_probe_response *)ieee80211softmac_alloc_mgt( | |
357 | + 8 + /* Timestamp */ | |
358 | + 2 + /* Beacon Interval */ | |
359 | + 2 + /* Capability Info */ | |
360 | + /* SSID IE */ | |
361 | + 1 + 1 + IW_ESSID_MAX_SIZE + | |
362 | + 7 + /* FH Parameter Set */ | |
363 | + 2 + /* DS Parameter Set */ | |
364 | + 8 + /* CF Parameter Set */ | |
365 | + 4 /* IBSS Parameter Set */ | |
366 | + ); | |
367 | + if (unlikely((*pkt) == NULL)) | |
368 | + return 0; | |
369 | + ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_PROBE_RESP, net->bssid, net->bssid); | |
370 | + data = (u8 *)(*pkt)->info_element; | |
371 | + | |
372 | + /* Return the packet size */ | |
373 | + return (data - (u8 *)(*pkt)); | |
374 | +} | |
375 | + | |
376 | + | |
377 | +/* Sends a manangement packet | |
378 | + * FIXME: document the use of the arg parameter | |
379 | + * for _AUTH: (transaction #) | (status << 16) | |
380 | + */ | |
381 | +int | |
382 | +ieee80211softmac_send_mgt_frame(struct ieee80211softmac_device *mac, | |
383 | + void *ptrarg, u32 type, u32 arg) | |
384 | +{ | |
385 | + void *pkt = NULL; | |
386 | + u32 pkt_size = 0; | |
387 | + | |
388 | + switch(type) { | |
389 | + case IEEE80211_STYPE_ASSOC_REQ: | |
390 | + pkt_size = ieee80211softmac_assoc_req((struct ieee80211_assoc_request **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg); | |
391 | + break; | |
392 | + case IEEE80211_STYPE_REASSOC_REQ: | |
393 | + pkt_size = ieee80211softmac_reassoc_req((struct ieee80211_reassoc_request **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg); | |
394 | + break; | |
395 | + case IEEE80211_STYPE_AUTH: | |
396 | + pkt_size = ieee80211softmac_auth((struct ieee80211_auth **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg, (u16)(arg & 0xFFFF), (u16) (arg >> 16)); | |
397 | + break; | |
398 | + case IEEE80211_STYPE_DISASSOC: | |
399 | + case IEEE80211_STYPE_DEAUTH: | |
400 | + pkt_size = ieee80211softmac_disassoc_deauth((struct ieee80211_disassoc **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg, type, (u16)(arg & 0xFFFF)); | |
401 | + break; | |
402 | + case IEEE80211_STYPE_PROBE_REQ: | |
403 | + pkt_size = ieee80211softmac_probe_req((struct ieee80211_probe_request **)(&pkt), mac, (struct ieee80211softmac_essid *)ptrarg); | |
404 | + break; | |
405 | + case IEEE80211_STYPE_PROBE_RESP: | |
406 | + pkt_size = ieee80211softmac_probe_resp((struct ieee80211_probe_response **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg); | |
407 | + break; | |
408 | + default: | |
409 | + printkl(KERN_DEBUG PFX "Unsupported Management Frame type: %i\n", type); | |
410 | + return -EINVAL; | |
411 | + }; | |
412 | + | |
413 | + if(pkt_size == 0 || pkt == NULL) { | |
414 | + printkl(KERN_DEBUG PFX "Error, packet is nonexistant or 0 length\n"); | |
415 | + return -ENOMEM; | |
416 | + } | |
417 | + | |
418 | + /* Send the packet to the ieee80211 layer for tx */ | |
419 | + /* we defined softmac->mgmt_xmit for this. Should we keep it | |
420 | + * as it is (that means we'd need to wrap this into a txb), | |
421 | + * modify the prototype (so it matches this function), | |
422 | + * or get rid of it alltogether? | |
423 | + * Does this work for you now? | |
424 | + */ | |
425 | + ieee80211_tx_frame(mac->ieee, (struct ieee80211_hdr *)pkt, pkt_size); | |
426 | + | |
427 | + kfree(pkt); | |
428 | + return 0; | |
429 | +} | |
430 | + | |
431 | + | |
432 | +/* Create an rts/cts frame */ | |
433 | +u32 | |
434 | +ieee80211softmac_rts_cts(struct ieee80211_hdr_2addr **pkt, | |
435 | + struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net, | |
436 | + u32 type) | |
437 | +{ | |
438 | + /* Allocate Packet */ | |
439 | + (*pkt) = kmalloc(IEEE80211_2ADDR_LEN, GFP_ATOMIC); | |
440 | + memset(*pkt, 0, IEEE80211_2ADDR_LEN); | |
441 | + if((*pkt) == NULL) | |
442 | + return 0; | |
443 | + ieee80211softmac_hdr_2addr(mac, (*pkt), type, net->bssid); | |
444 | + return IEEE80211_2ADDR_LEN; | |
445 | +} | |
446 | + | |
447 | + | |
448 | +/* Sends a control packet */ | |
449 | +static int | |
450 | +ieee80211softmac_send_ctl_frame(struct ieee80211softmac_device *mac, | |
451 | + struct ieee80211softmac_network *net, u32 type, u32 arg) | |
452 | +{ | |
453 | + void *pkt = NULL; | |
454 | + u32 pkt_size = 0; | |
455 | + | |
456 | + switch(type) { | |
457 | + case IEEE80211_STYPE_RTS: | |
458 | + case IEEE80211_STYPE_CTS: | |
459 | + pkt_size = ieee80211softmac_rts_cts((struct ieee80211_hdr_2addr **)(&pkt), mac, net, type); | |
460 | + break; | |
461 | + default: | |
462 | + printkl(KERN_DEBUG PFX "Unsupported Control Frame type: %i\n", type); | |
463 | + return -EINVAL; | |
464 | + } | |
465 | + | |
466 | + if(pkt_size == 0) | |
467 | + return -ENOMEM; | |
468 | + | |
469 | + /* Send the packet to the ieee80211 layer for tx */ | |
470 | + ieee80211_tx_frame(mac->ieee, (struct ieee80211_hdr *) pkt, pkt_size); | |
471 | + | |
472 | + kfree(pkt); | |
473 | + return 0; | |
474 | +} |
net/ieee80211/softmac/ieee80211softmac_module.c
1 | +#include "ieee80211softmac_priv.h" | |
2 | +#include <linux/sort.h> | |
3 | + | |
4 | +struct net_device *alloc_ieee80211softmac(int sizeof_priv) | |
5 | +{ | |
6 | + struct ieee80211softmac_device *softmac; | |
7 | + struct net_device *dev; | |
8 | + | |
9 | + dev = alloc_ieee80211(sizeof(struct ieee80211softmac_device) + sizeof_priv); | |
10 | + softmac = ieee80211_priv(dev); | |
11 | + softmac->dev = dev; | |
12 | + softmac->ieee = netdev_priv(dev); | |
13 | + spin_lock_init(&softmac->lock); | |
14 | + | |
15 | + softmac->ieee->handle_auth = ieee80211softmac_auth_resp; | |
16 | + softmac->ieee->handle_deauth = ieee80211softmac_deauth_resp; | |
17 | + softmac->ieee->handle_assoc_response = ieee80211softmac_handle_assoc_response; | |
18 | + softmac->ieee->handle_disassoc = ieee80211softmac_handle_disassoc; | |
19 | + softmac->scaninfo = NULL; | |
20 | + | |
21 | + /* TODO: initialise all the other callbacks in the ieee struct | |
22 | + * (once they're written) | |
23 | + */ | |
24 | + | |
25 | + softmac->workqueue = create_workqueue("80211softmac"); | |
26 | + if (!softmac->workqueue) | |
27 | + goto err_free_ieee80211; | |
28 | + | |
29 | + INIT_LIST_HEAD(&softmac->auth_queue); | |
30 | + INIT_LIST_HEAD(&softmac->network_list); | |
31 | + INIT_LIST_HEAD(&softmac->events); | |
32 | + | |
33 | + INIT_WORK(&softmac->associnfo.work, ieee80211softmac_assoc_work, softmac); | |
34 | + INIT_WORK(&softmac->associnfo.timeout, ieee80211softmac_assoc_timeout, softmac); | |
35 | + softmac->start_scan = ieee80211softmac_start_scan_implementation; | |
36 | + softmac->wait_for_scan = ieee80211softmac_wait_for_scan_implementation; | |
37 | + softmac->stop_scan = ieee80211softmac_stop_scan_implementation; | |
38 | + | |
39 | + //TODO: The mcast rate has to be assigned dynamically somewhere (in scanning, association. Not sure...) | |
40 | + // It has to be set to the highest rate all stations in the current network can handle. | |
41 | + softmac->txrates.mcast_rate = IEEE80211_CCK_RATE_1MB; | |
42 | + softmac->txrates.mcast_fallback = IEEE80211_CCK_RATE_1MB; | |
43 | + /* This is reassigned in ieee80211softmac_start to sane values. */ | |
44 | + softmac->txrates.default_rate = IEEE80211_CCK_RATE_1MB; | |
45 | + softmac->txrates.default_fallback = IEEE80211_CCK_RATE_1MB; | |
46 | + | |
47 | + /* should we also assign softmac->mgmt_xmit here so | |
48 | + * that it is always valid? If so, we probably want | |
49 | + * to define a new function for that which just | |
50 | + * wraps ieee80211_tx_frame | |
51 | + */ | |
52 | + | |
53 | + /* until associated, we're not ready */ | |
54 | + dev->flags &= ~IFF_RUNNING; | |
55 | + | |
56 | + return dev; | |
57 | + | |
58 | +err_free_ieee80211: | |
59 | + free_ieee80211(dev); | |
60 | + | |
61 | + return NULL; | |
62 | +} | |
63 | + | |
64 | +/* Clears the pending work queue items, stops all scans, etc. */ | |
65 | +void | |
66 | +ieee80211softmac_clear_pending_work(struct ieee80211softmac_device *sm) | |
67 | +{ | |
68 | + unsigned long flags; | |
69 | + struct ieee80211softmac_event *eventptr, *eventtmp; | |
70 | + struct ieee80211softmac_auth_queue_item *authptr, *authtmp; | |
71 | + struct ieee80211softmac_network *netptr, *nettmp; | |
72 | + | |
73 | + ieee80211softmac_stop_scan(sm); | |
74 | + ieee80211softmac_wait_for_scan(sm); | |
75 | + | |
76 | + spin_lock_irqsave(&sm->lock, flags); | |
77 | + /* Free all pending assoc work items */ | |
78 | + cancel_delayed_work(&sm->associnfo.work); | |
79 | + | |
80 | + /* Free all pending scan work items */ | |
81 | + if(sm->scaninfo != NULL) | |
82 | + cancel_delayed_work(&sm->scaninfo->softmac_scan); | |
83 | + | |
84 | + /* Free all pending auth work items */ | |
85 | + list_for_each_entry(authptr, &sm->auth_queue, list) | |
86 | + cancel_delayed_work(&authptr->work); | |
87 | + | |
88 | + /* delete all pending event calls and work items */ | |
89 | + list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list) | |
90 | + cancel_delayed_work(&eventptr->work); | |
91 | + | |
92 | + spin_unlock_irqrestore(&sm->lock, flags); | |
93 | + flush_workqueue(sm->workqueue); | |
94 | + | |
95 | + // now we should be save and no longer need locking... | |
96 | + spin_lock_irqsave(&sm->lock, flags); | |
97 | + /* Free all pending auth work items */ | |
98 | + list_for_each_entry_safe(authptr, authtmp, &sm->auth_queue, list) { | |
99 | + list_del(&authptr->list); | |
100 | + kfree(authptr); | |
101 | + } | |
102 | + | |
103 | + /* delete all pending event calls and work items */ | |
104 | + list_for_each_entry_safe(eventptr, eventtmp, &sm->events, list) { | |
105 | + list_del(&eventptr->list); | |
106 | + kfree(eventptr); | |
107 | + } | |
108 | + | |
109 | + /* Free all networks */ | |
110 | + list_for_each_entry_safe(netptr, nettmp, &sm->network_list, list) { | |
111 | + ieee80211softmac_del_network_locked(sm, netptr); | |
112 | + if(netptr->challenge != NULL) | |
113 | + kfree(netptr->challenge); | |
114 | + kfree(netptr); | |
115 | + } | |
116 | + | |
117 | + spin_unlock_irqrestore(&sm->lock, flags); | |
118 | +} | |
119 | + | |
120 | +void free_ieee80211softmac(struct net_device *dev) | |
121 | +{ | |
122 | + struct ieee80211softmac_device *sm = ieee80211_priv(dev); | |
123 | + ieee80211softmac_clear_pending_work(sm); | |
124 | + destroy_workqueue(sm->workqueue); | |
125 | + kfree(sm->scaninfo); | |
126 | + kfree(sm->wpa.IE); | |
127 | + free_ieee80211(dev); | |
128 | +} | |
129 | + | |
130 | +static void ieee80211softmac_start_check_rates(struct ieee80211softmac_device *mac) | |
131 | +{ | |
132 | + struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo; | |
133 | + /* I took out the sorting check, we're seperating by modulation now. */ | |
134 | + if (ri->count) | |
135 | + return; | |
136 | + /* otherwise assume we hav'em all! */ | |
137 | + if (mac->ieee->modulation & IEEE80211_CCK_MODULATION) { | |
138 | + ri->rates[ri->count++] = IEEE80211_CCK_RATE_1MB; | |
139 | + ri->rates[ri->count++] = IEEE80211_CCK_RATE_2MB; | |
140 | + ri->rates[ri->count++] = IEEE80211_CCK_RATE_5MB; | |
141 | + ri->rates[ri->count++] = IEEE80211_CCK_RATE_11MB; | |
142 | + } | |
143 | + if (mac->ieee->modulation & IEEE80211_OFDM_MODULATION) { | |
144 | + ri->rates[ri->count++] = IEEE80211_OFDM_RATE_6MB; | |
145 | + ri->rates[ri->count++] = IEEE80211_OFDM_RATE_9MB; | |
146 | + ri->rates[ri->count++] = IEEE80211_OFDM_RATE_12MB; | |
147 | + ri->rates[ri->count++] = IEEE80211_OFDM_RATE_18MB; | |
148 | + ri->rates[ri->count++] = IEEE80211_OFDM_RATE_24MB; | |
149 | + ri->rates[ri->count++] = IEEE80211_OFDM_RATE_36MB; | |
150 | + ri->rates[ri->count++] = IEEE80211_OFDM_RATE_48MB; | |
151 | + ri->rates[ri->count++] = IEEE80211_OFDM_RATE_54MB; | |
152 | + } | |
153 | +} | |
154 | + | |
155 | +void ieee80211softmac_start(struct net_device *dev) | |
156 | +{ | |
157 | + struct ieee80211softmac_device *mac = ieee80211_priv(dev); | |
158 | + struct ieee80211_device *ieee = mac->ieee; | |
159 | + u32 change = 0; | |
160 | + struct ieee80211softmac_txrates oldrates; | |
161 | + | |
162 | + ieee80211softmac_start_check_rates(mac); | |
163 | + | |
164 | + /* TODO: We need some kind of state machine to lower the default rates | |
165 | + * if we loose too many packets. | |
166 | + */ | |
167 | + /* Change the default txrate to the highest possible value. | |
168 | + * The txrate machine will lower it, if it is too high. | |
169 | + */ | |
170 | + if (mac->txrates_change) | |
171 | + oldrates = mac->txrates; | |
172 | + if (ieee->modulation & IEEE80211_OFDM_MODULATION) { | |
173 | + mac->txrates.default_rate = IEEE80211_OFDM_RATE_54MB; | |
174 | + change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT; | |
175 | + mac->txrates.default_fallback = IEEE80211_OFDM_RATE_24MB; | |
176 | + change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK; | |
177 | + } else if (ieee->modulation & IEEE80211_CCK_MODULATION) { | |
178 | + mac->txrates.default_rate = IEEE80211_CCK_RATE_11MB; | |
179 | + change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT; | |
180 | + mac->txrates.default_fallback = IEEE80211_CCK_RATE_5MB; | |
181 | + change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK; | |
182 | + } else | |
183 | + assert(0); | |
184 | + if (mac->txrates_change) | |
185 | + mac->txrates_change(dev, change, &oldrates); | |
186 | +} | |
187 | + | |
188 | +void ieee80211softmac_stop(struct net_device *dev) | |
189 | +{ | |
190 | + struct ieee80211softmac_device *mac = ieee80211_priv(dev); | |
191 | + | |
192 | + ieee80211softmac_clear_pending_work(mac); | |
193 | +} | |
194 | + | |
195 | +void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates) | |
196 | +{ | |
197 | + struct ieee80211softmac_device *mac = ieee80211_priv(dev); | |
198 | + unsigned long flags; | |
199 | + | |
200 | + spin_lock_irqsave(&mac->lock, flags); | |
201 | + memcpy(mac->ratesinfo.rates, rates, count); | |
202 | + mac->ratesinfo.count = count; | |
203 | + spin_unlock_irqrestore(&mac->lock, flags); | |
204 | +} | |
205 | + | |
206 | +static u8 raise_rate(struct ieee80211softmac_device *mac, u8 rate) | |
207 | +{ | |
208 | + int i; | |
209 | + struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo; | |
210 | + | |
211 | + for (i=0; i<ri->count-1; i++) { | |
212 | + if (ri->rates[i] == rate) | |
213 | + return ri->rates[i+1]; | |
214 | + } | |
215 | + /* I guess we can't go any higher... */ | |
216 | + return ri->rates[ri->count]; | |
217 | +} | |
218 | + | |
219 | +u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta) | |
220 | +{ | |
221 | + int i; | |
222 | + struct ieee80211softmac_ratesinfo *ri = &mac->ratesinfo; | |
223 | + | |
224 | + for (i=delta; i<ri->count; i++) { | |
225 | + if (ri->rates[i] == rate) | |
226 | + return ri->rates[i-delta]; | |
227 | + } | |
228 | + /* I guess we can't go any lower... */ | |
229 | + return ri->rates[0]; | |
230 | +} | |
231 | + | |
232 | +static void ieee80211softmac_add_txrates_badness(struct ieee80211softmac_device *mac, | |
233 | + int amount) | |
234 | +{ | |
235 | + struct ieee80211softmac_txrates oldrates; | |
236 | + u8 default_rate = mac->txrates.default_rate; | |
237 | + u8 default_fallback = mac->txrates.default_fallback; | |
238 | + u32 changes = 0; | |
239 | + | |
240 | + //TODO: This is highly experimental code. | |
241 | + // Maybe the dynamic rate selection does not work | |
242 | + // and it has to be removed again. | |
243 | + | |
244 | +printk("badness %d\n", mac->txrate_badness); | |
245 | + mac->txrate_badness += amount; | |
246 | + if (mac->txrate_badness <= -1000) { | |
247 | + /* Very small badness. Try a faster bitrate. */ | |
248 | + if (mac->txrates_change) | |
249 | + memcpy(&oldrates, &mac->txrates, sizeof(oldrates)); | |
250 | + default_rate = raise_rate(mac, default_rate); | |
251 | + changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT; | |
252 | + default_fallback = get_fallback_rate(mac, default_rate); | |
253 | + changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK; | |
254 | + mac->txrate_badness = 0; | |
255 | +printk("Bitrate raised to %u\n", default_rate); | |
256 | + } else if (mac->txrate_badness >= 10000) { | |
257 | + /* Very high badness. Try a slower bitrate. */ | |
258 | + if (mac->txrates_change) | |
259 | + memcpy(&oldrates, &mac->txrates, sizeof(oldrates)); | |
260 | + default_rate = lower_rate(mac, default_rate); | |
261 | + changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT; | |
262 | + default_fallback = get_fallback_rate(mac, default_rate); | |
263 | + changes |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK; | |
264 | + mac->txrate_badness = 0; | |
265 | +printk("Bitrate lowered to %u\n", default_rate); | |
266 | + } | |
267 | + | |
268 | + mac->txrates.default_rate = default_rate; | |
269 | + mac->txrates.default_fallback = default_fallback; | |
270 | + | |
271 | + if (changes && mac->txrates_change) | |
272 | + mac->txrates_change(mac->dev, changes, &oldrates); | |
273 | +} | |
274 | + | |
275 | +void ieee80211softmac_fragment_lost(struct net_device *dev, | |
276 | + u16 wl_seq) | |
277 | +{ | |
278 | + struct ieee80211softmac_device *mac = ieee80211_priv(dev); | |
279 | + unsigned long flags; | |
280 | + | |
281 | + spin_lock_irqsave(&mac->lock, flags); | |
282 | + ieee80211softmac_add_txrates_badness(mac, 1000); | |
283 | + //TODO | |
284 | + | |
285 | + spin_unlock_irqrestore(&mac->lock, flags); | |
286 | +} | |
287 | + | |
288 | +static int rate_cmp(const void *a_, const void *b_) { | |
289 | + u8 *a, *b; | |
290 | + a = (u8*)a_; | |
291 | + b = (u8*)b_; | |
292 | + return ((*a & ~IEEE80211_BASIC_RATE_MASK) - (*b & ~IEEE80211_BASIC_RATE_MASK)); | |
293 | +} | |
294 | + | |
295 | +/* Allocate a softmac network struct and fill it from a network */ | |
296 | +struct ieee80211softmac_network * | |
297 | +ieee80211softmac_create_network(struct ieee80211softmac_device *mac, | |
298 | + struct ieee80211_network *net) | |
299 | +{ | |
300 | + struct ieee80211softmac_network *softnet; | |
301 | + softnet = kzalloc(sizeof(struct ieee80211softmac_network), GFP_ATOMIC); | |
302 | + if(softnet == NULL) | |
303 | + return NULL; | |
304 | + memcpy(softnet->bssid, net->bssid, ETH_ALEN); | |
305 | + softnet->channel = net->channel; | |
306 | + softnet->essid.len = net->ssid_len; | |
307 | + memcpy(softnet->essid.data, net->ssid, softnet->essid.len); | |
308 | + | |
309 | + /* copy rates over */ | |
310 | + softnet->supported_rates.count = net->rates_len; | |
311 | + memcpy(&softnet->supported_rates.rates[0], net->rates, net->rates_len); | |
312 | + memcpy(&softnet->supported_rates.rates[softnet->supported_rates.count], net->rates_ex, net->rates_ex_len); | |
313 | + softnet->supported_rates.count += net->rates_ex_len; | |
314 | + sort(softnet->supported_rates.rates, softnet->supported_rates.count, sizeof(softnet->supported_rates.rates[0]), rate_cmp, NULL); | |
315 | + | |
316 | + softnet->capabilities = net->capability; | |
317 | + return softnet; | |
318 | +} | |
319 | + | |
320 | + | |
321 | +/* Add a network to the list, while locked */ | |
322 | +void | |
323 | +ieee80211softmac_add_network_locked(struct ieee80211softmac_device *mac, | |
324 | + struct ieee80211softmac_network *add_net) | |
325 | +{ | |
326 | + struct list_head *list_ptr; | |
327 | + struct ieee80211softmac_network *softmac_net = NULL; | |
328 | + | |
329 | + list_for_each(list_ptr, &mac->network_list) { | |
330 | + softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list); | |
331 | + if(!memcmp(softmac_net->bssid, add_net->bssid, ETH_ALEN)) | |
332 | + break; | |
333 | + else | |
334 | + softmac_net = NULL; | |
335 | + } | |
336 | + if(softmac_net == NULL) | |
337 | + list_add(&(add_net->list), &mac->network_list); | |
338 | +} | |
339 | + | |
340 | +/* Add a network to the list, with locking */ | |
341 | +void | |
342 | +ieee80211softmac_add_network(struct ieee80211softmac_device *mac, | |
343 | + struct ieee80211softmac_network *add_net) | |
344 | +{ | |
345 | + unsigned long flags; | |
346 | + spin_lock_irqsave(&mac->lock, flags); | |
347 | + ieee80211softmac_add_network_locked(mac, add_net); | |
348 | + spin_unlock_irqrestore(&mac->lock, flags); | |
349 | +} | |
350 | + | |
351 | + | |
352 | +/* Delete a network from the list, while locked*/ | |
353 | +void | |
354 | +ieee80211softmac_del_network_locked(struct ieee80211softmac_device *mac, | |
355 | + struct ieee80211softmac_network *del_net) | |
356 | +{ | |
357 | + list_del(&(del_net->list)); | |
358 | +} | |
359 | + | |
360 | +/* Delete a network from the list with locking */ | |
361 | +void | |
362 | +ieee80211softmac_del_network(struct ieee80211softmac_device *mac, | |
363 | + struct ieee80211softmac_network *del_net) | |
364 | +{ | |
365 | + unsigned long flags; | |
366 | + spin_lock_irqsave(&mac->lock, flags); | |
367 | + ieee80211softmac_del_network_locked(mac, del_net); | |
368 | + spin_unlock_irqrestore(&mac->lock, flags); | |
369 | +} | |
370 | + | |
371 | +/* Get a network from the list by MAC while locked */ | |
372 | +struct ieee80211softmac_network * | |
373 | +ieee80211softmac_get_network_by_bssid_locked(struct ieee80211softmac_device *mac, | |
374 | + u8 *bssid) | |
375 | +{ | |
376 | + struct list_head *list_ptr; | |
377 | + struct ieee80211softmac_network *softmac_net = NULL; | |
378 | + list_for_each(list_ptr, &mac->network_list) { | |
379 | + softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list); | |
380 | + if(!memcmp(softmac_net->bssid, bssid, ETH_ALEN)) | |
381 | + break; | |
382 | + else | |
383 | + softmac_net = NULL; | |
384 | + } | |
385 | + return softmac_net; | |
386 | +} | |
387 | + | |
388 | +/* Get a network from the list by BSSID with locking */ | |
389 | +struct ieee80211softmac_network * | |
390 | +ieee80211softmac_get_network_by_bssid(struct ieee80211softmac_device *mac, | |
391 | + u8 *bssid) | |
392 | +{ | |
393 | + unsigned long flags; | |
394 | + struct ieee80211softmac_network *softmac_net; | |
395 | + | |
396 | + spin_lock_irqsave(&mac->lock, flags); | |
397 | + softmac_net = ieee80211softmac_get_network_by_bssid_locked(mac, bssid); | |
398 | + spin_unlock_irqrestore(&mac->lock, flags); | |
399 | + return softmac_net; | |
400 | +} | |
401 | + | |
402 | +/* Get a network from the list by ESSID while locked */ | |
403 | +struct ieee80211softmac_network * | |
404 | +ieee80211softmac_get_network_by_essid_locked(struct ieee80211softmac_device *mac, | |
405 | + struct ieee80211softmac_essid *essid) | |
406 | +{ | |
407 | + struct list_head *list_ptr; | |
408 | + struct ieee80211softmac_network *softmac_net = NULL; | |
409 | + | |
410 | + list_for_each(list_ptr, &mac->network_list) { | |
411 | + softmac_net = list_entry(list_ptr, struct ieee80211softmac_network, list); | |
412 | + if (softmac_net->essid.len == essid->len && | |
413 | + !memcmp(softmac_net->essid.data, essid->data, essid->len)) | |
414 | + return softmac_net; | |
415 | + } | |
416 | + return NULL; | |
417 | +} | |
418 | + | |
419 | +/* Get a network from the list by ESSID with locking */ | |
420 | +struct ieee80211softmac_network * | |
421 | +ieee80211softmac_get_network_by_essid(struct ieee80211softmac_device *mac, | |
422 | + struct ieee80211softmac_essid *essid) | |
423 | +{ | |
424 | + unsigned long flags; | |
425 | + struct ieee80211softmac_network *softmac_net = NULL; | |
426 | + | |
427 | + spin_lock_irqsave(&mac->lock, flags); | |
428 | + softmac_net = ieee80211softmac_get_network_by_essid_locked(mac, essid); | |
429 | + spin_unlock_irqrestore(&mac->lock, flags); | |
430 | + return softmac_net; | |
431 | +} | |
432 | + | |
433 | +MODULE_LICENSE("GPL"); | |
434 | + | |
435 | +EXPORT_SYMBOL_GPL(alloc_ieee80211softmac); | |
436 | +EXPORT_SYMBOL_GPL(free_ieee80211softmac); | |
437 | +EXPORT_SYMBOL_GPL(ieee80211softmac_set_rates); | |
438 | +EXPORT_SYMBOL_GPL(ieee80211softmac_start); | |
439 | +EXPORT_SYMBOL_GPL(ieee80211softmac_stop); | |
440 | +EXPORT_SYMBOL_GPL(ieee80211softmac_fragment_lost); | |
441 | +EXPORT_SYMBOL_GPL(ieee80211softmac_clear_pending_work); |
net/ieee80211/softmac/ieee80211softmac_priv.h
1 | +#ifndef IEEE80211SOFTMAC_PRIV_H_ | |
2 | +#define IEEE80211SOFTMAC_PRIV_H_ | |
3 | + | |
4 | +#include <net/ieee80211softmac.h> | |
5 | +#include <net/ieee80211softmac_wx.h> | |
6 | +#include <linux/kernel.h> | |
7 | +#include <linux/stringify.h> | |
8 | + | |
9 | + | |
10 | +#define PFX "SoftMAC: " | |
11 | + | |
12 | +#ifdef assert | |
13 | +# undef assert | |
14 | +#endif | |
15 | +#ifdef CONFIG_IEEE80211_SOFTMAC_DEBUG | |
16 | +#define assert(expr) \ | |
17 | + do { \ | |
18 | + if (unlikely(!(expr))) { \ | |
19 | + printkl(KERN_ERR PFX "ASSERTION FAILED (%s) at: %s:%d:%s()\n", #expr, \ | |
20 | + __FILE__, __LINE__, __FUNCTION__); \ | |
21 | + } \ | |
22 | + } while (0) | |
23 | +#else | |
24 | +#define assert(expr) do {} while (0) | |
25 | +#endif | |
26 | + | |
27 | +/* rate limited printk(). */ | |
28 | +#ifdef printkl | |
29 | +# undef printkl | |
30 | +#endif | |
31 | +#define printkl(f, x...) do { if (printk_ratelimit()) printk(f ,##x); } while (0) | |
32 | +/* rate limited printk() for debugging */ | |
33 | +#ifdef dprintkl | |
34 | +# undef dprintkl | |
35 | +#endif | |
36 | +#ifdef CONFIG_IEEE80211_SOFTMAC_DEBUG | |
37 | +# define dprintkl printkl | |
38 | +#else | |
39 | +# define dprintkl(f, x...) do { /* nothing */ } while (0) | |
40 | +#endif | |
41 | + | |
42 | +/* debugging printk() */ | |
43 | +#ifdef dprintk | |
44 | +# undef dprintk | |
45 | +#endif | |
46 | +#ifdef CONFIG_IEEE80211_SOFTMAC_DEBUG | |
47 | +# define dprintk(f, x...) do { printk(f ,##x); } while (0) | |
48 | +#else | |
49 | +# define dprintk(f, x...) do { /* nothing */ } while (0) | |
50 | +#endif | |
51 | + | |
52 | +#ifdef function_enter | |
53 | +# undef function_enter | |
54 | +#endif | |
55 | +#ifdef CONFIG_IEEE80211_SOFTMAC_DEBUG | |
56 | +# define function_enter() do { printk(KERN_DEBUG PFX "%s:%d:%s()\n", __FILE__, __LINE__, __FUNCTION__); } while (0) | |
57 | +#else | |
58 | +# define function_enter() do { /* nothing */ } while (0) | |
59 | +#endif | |
60 | + | |
61 | +/* private definitions and prototypes */ | |
62 | + | |
63 | +/*** prototypes from _scan.c */ | |
64 | +void ieee80211softmac_scan(void *sm); | |
65 | +/* for internal use if scanning is needed */ | |
66 | +int ieee80211softmac_start_scan(struct ieee80211softmac_device *mac); | |
67 | +void ieee80211softmac_stop_scan(struct ieee80211softmac_device *mac); | |
68 | +void ieee80211softmac_wait_for_scan(struct ieee80211softmac_device *mac); | |
69 | + | |
70 | +/* for use by _module.c to assign to the callbacks */ | |
71 | +int ieee80211softmac_start_scan_implementation(struct net_device *dev); | |
72 | +void ieee80211softmac_stop_scan_implementation(struct net_device *dev); | |
73 | +void ieee80211softmac_wait_for_scan_implementation(struct net_device *dev); | |
74 | + | |
75 | +/*** Network prototypes from _module.c */ | |
76 | +struct ieee80211softmac_network * ieee80211softmac_create_network( | |
77 | + struct ieee80211softmac_device *mac, struct ieee80211_network *net); | |
78 | +void ieee80211softmac_add_network_locked(struct ieee80211softmac_device *mac, | |
79 | + struct ieee80211softmac_network *net); | |
80 | +void ieee80211softmac_add_network(struct ieee80211softmac_device *mac, | |
81 | + struct ieee80211softmac_network *net); | |
82 | +void ieee80211softmac_del_network_locked(struct ieee80211softmac_device *mac, | |
83 | + struct ieee80211softmac_network *net); | |
84 | +void ieee80211softmac_del_network(struct ieee80211softmac_device *mac, | |
85 | + struct ieee80211softmac_network *net); | |
86 | +struct ieee80211softmac_network * ieee80211softmac_get_network_by_bssid_locked( | |
87 | + struct ieee80211softmac_device *mac, u8 *ea); | |
88 | +struct ieee80211softmac_network * ieee80211softmac_get_network_by_bssid( | |
89 | + struct ieee80211softmac_device *mac, u8 *ea); | |
90 | +struct ieee80211softmac_network * ieee80211softmac_get_network_by_ssid_locked( | |
91 | + struct ieee80211softmac_device *mac, u8 *ssid, u8 ssid_len); | |
92 | +struct ieee80211softmac_network * ieee80211softmac_get_network_by_ssid( | |
93 | + struct ieee80211softmac_device *mac, u8 *ssid, u8 ssid_len); | |
94 | + | |
95 | + | |
96 | +/* Rates related */ | |
97 | +u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta); | |
98 | +static inline u8 lower_rate(struct ieee80211softmac_device *mac, u8 rate) { | |
99 | + return ieee80211softmac_lower_rate_delta(mac, rate, 1); | |
100 | +} | |
101 | + | |
102 | +static inline u8 get_fallback_rate(struct ieee80211softmac_device *mac, u8 rate) | |
103 | +{ | |
104 | + return ieee80211softmac_lower_rate_delta(mac, rate, 2); | |
105 | +} | |
106 | + | |
107 | + | |
108 | +/*** prototypes from _io.c */ | |
109 | +int ieee80211softmac_send_mgt_frame(struct ieee80211softmac_device *mac, | |
110 | + void* ptrarg, u32 type, u32 arg); | |
111 | + | |
112 | +/*** prototypes from _auth.c */ | |
113 | +/* do these have to go into the public header? */ | |
114 | +int ieee80211softmac_auth_req(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net); | |
115 | +int ieee80211softmac_deauth_req(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net, int reason); | |
116 | + | |
117 | +/* for use by _module.c to assign to the callbacks */ | |
118 | +int ieee80211softmac_auth_resp(struct net_device *dev, struct ieee80211_auth *auth); | |
119 | +int ieee80211softmac_deauth_resp(struct net_device *dev, struct ieee80211_auth *auth); | |
120 | + | |
121 | +/*** prototypes from _assoc.c */ | |
122 | +void ieee80211softmac_assoc_work(void *d); | |
123 | +int ieee80211softmac_handle_assoc_response(struct net_device * dev, | |
124 | + struct ieee80211_assoc_response * resp, | |
125 | + struct ieee80211_network * network); | |
126 | +int ieee80211softmac_handle_disassoc(struct net_device * dev, | |
127 | + struct ieee80211_disassoc * disassoc); | |
128 | +void ieee80211softmac_assoc_timeout(void *d); | |
129 | + | |
130 | +/* some helper functions */ | |
131 | +static inline int ieee80211softmac_scan_handlers_check_self(struct ieee80211softmac_device *sm) | |
132 | +{ | |
133 | + return (sm->start_scan == ieee80211softmac_start_scan_implementation) && | |
134 | + (sm->stop_scan == ieee80211softmac_stop_scan_implementation) && | |
135 | + (sm->wait_for_scan == ieee80211softmac_wait_for_scan_implementation); | |
136 | +} | |
137 | + | |
138 | +static inline int ieee80211softmac_scan_sanity_check(struct ieee80211softmac_device *sm) | |
139 | +{ | |
140 | + return ((sm->start_scan != ieee80211softmac_start_scan_implementation) && | |
141 | + (sm->stop_scan != ieee80211softmac_stop_scan_implementation) && | |
142 | + (sm->wait_for_scan != ieee80211softmac_wait_for_scan_implementation) | |
143 | + ) || ieee80211softmac_scan_handlers_check_self(sm); | |
144 | +} | |
145 | + | |
146 | +#define IEEE80211SOFTMAC_PROBE_DELAY HZ/2 | |
147 | +#define IEEE80211SOFTMAC_WORKQUEUE_NAME_LEN (17 + IFNAMSIZ) | |
148 | + | |
149 | +struct ieee80211softmac_network { | |
150 | + struct list_head list; /* List */ | |
151 | + /* Network information copied from ieee80211_network */ | |
152 | + u8 bssid[ETH_ALEN]; | |
153 | + u8 channel; | |
154 | + struct ieee80211softmac_essid essid; | |
155 | + | |
156 | + struct ieee80211softmac_ratesinfo supported_rates; | |
157 | + | |
158 | + /* SoftMAC specific */ | |
159 | + u16 authenticating:1, /* Status Flags */ | |
160 | + authenticated:1, | |
161 | + auth_desynced_once:1; | |
162 | + | |
163 | + u16 capabilities; /* Capabilities bitfield */ | |
164 | + u8 challenge_len; /* Auth Challenge length */ | |
165 | + char *challenge; /* Challenge Text */ | |
166 | +}; | |
167 | + | |
168 | +/* structure used to keep track of networks we're auth'ing to */ | |
169 | +struct ieee80211softmac_auth_queue_item { | |
170 | + struct list_head list; /* List head */ | |
171 | + struct ieee80211softmac_network *net; /* Network to auth */ | |
172 | + struct ieee80211softmac_device *mac; /* SoftMAC device */ | |
173 | + u8 retry; /* Retry limit */ | |
174 | + u8 state; /* Auth State */ | |
175 | + struct work_struct work; /* Work queue */ | |
176 | +}; | |
177 | + | |
178 | +/* scanning information */ | |
179 | +struct ieee80211softmac_scaninfo { | |
180 | + u8 current_channel_idx, | |
181 | + number_channels; | |
182 | + struct ieee80211_channel *channels; | |
183 | + u8 started:1, | |
184 | + stop:1; | |
185 | + u8 skip_flags; | |
186 | + struct completion finished; | |
187 | + struct work_struct softmac_scan; | |
188 | +}; | |
189 | + | |
190 | +/* private event struct */ | |
191 | +struct ieee80211softmac_event { | |
192 | + struct list_head list; | |
193 | + int event_type; | |
194 | + void *event_context; | |
195 | + struct work_struct work; | |
196 | + notify_function_ptr fun; | |
197 | + void *context; | |
198 | + struct ieee80211softmac_device *mac; | |
199 | +}; | |
200 | + | |
201 | +void ieee80211softmac_call_events(struct ieee80211softmac_device *mac, int event, void *event_context); | |
202 | +void ieee80211softmac_call_events_locked(struct ieee80211softmac_device *mac, int event, void *event_context); | |
203 | +int ieee80211softmac_notify_internal(struct ieee80211softmac_device *mac, | |
204 | + int event, void *event_context, notify_function_ptr fun, void *context, gfp_t gfp_mask); | |
205 | + | |
206 | +#endif /* IEEE80211SOFTMAC_PRIV_H_ */ |
net/ieee80211/softmac/ieee80211softmac_scan.c
1 | +/* | |
2 | + * Scanning routines. | |
3 | + * | |
4 | + * These are not exported because they're assigned to the function pointers. | |
5 | + */ | |
6 | + | |
7 | +#include <linux/completion.h> | |
8 | +#include "ieee80211softmac_priv.h" | |
9 | + | |
10 | +/* internal, use to trigger scanning if needed. | |
11 | + * Returns -EBUSY if already scanning, | |
12 | + * result of start_scan otherwise */ | |
13 | +int | |
14 | +ieee80211softmac_start_scan(struct ieee80211softmac_device *sm) | |
15 | +{ | |
16 | + unsigned long flags; | |
17 | + int ret; | |
18 | + | |
19 | + spin_lock_irqsave(&sm->lock, flags); | |
20 | + if (sm->scanning) | |
21 | + { | |
22 | + spin_unlock_irqrestore(&sm->lock, flags); | |
23 | + return -EINPROGRESS; | |
24 | + } | |
25 | + sm->scanning = 1; | |
26 | + spin_unlock_irqrestore(&sm->lock, flags); | |
27 | + | |
28 | + ret = sm->start_scan(sm->dev); | |
29 | + if (ret) { | |
30 | + spin_lock_irqsave(&sm->lock, flags); | |
31 | + sm->scanning = 0; | |
32 | + spin_unlock_irqrestore(&sm->lock, flags); | |
33 | + } | |
34 | + return ret; | |
35 | +} | |
36 | + | |
37 | +void | |
38 | +ieee80211softmac_stop_scan(struct ieee80211softmac_device *sm) | |
39 | +{ | |
40 | + unsigned long flags; | |
41 | + | |
42 | + spin_lock_irqsave(&sm->lock, flags); | |
43 | + | |
44 | + if (!sm->scanning) { | |
45 | + spin_unlock_irqrestore(&sm->lock, flags); | |
46 | + return; | |
47 | + } | |
48 | + | |
49 | + spin_unlock_irqrestore(&sm->lock, flags); | |
50 | + sm->stop_scan(sm->dev); | |
51 | +} | |
52 | + | |
53 | +void | |
54 | +ieee80211softmac_wait_for_scan(struct ieee80211softmac_device *sm) | |
55 | +{ | |
56 | + unsigned long flags; | |
57 | + | |
58 | + spin_lock_irqsave(&sm->lock, flags); | |
59 | + | |
60 | + if (!sm->scanning) { | |
61 | + spin_unlock_irqrestore(&sm->lock, flags); | |
62 | + return; | |
63 | + } | |
64 | + | |
65 | + spin_unlock_irqrestore(&sm->lock, flags); | |
66 | + sm->wait_for_scan(sm->dev); | |
67 | +} | |
68 | + | |
69 | + | |
70 | +/* internal scanning implementation follows */ | |
71 | +void ieee80211softmac_scan(void *d) | |
72 | +{ | |
73 | + int invalid_channel; | |
74 | + u8 current_channel_idx; | |
75 | + struct ieee80211softmac_device *sm = (struct ieee80211softmac_device *)d; | |
76 | + struct ieee80211softmac_scaninfo *si = sm->scaninfo; | |
77 | + unsigned long flags; | |
78 | + | |
79 | + while (!(si->stop) && (si->current_channel_idx < si->number_channels)) { | |
80 | + current_channel_idx = si->current_channel_idx; | |
81 | + si->current_channel_idx++; /* go to the next channel */ | |
82 | + | |
83 | + invalid_channel = (si->skip_flags & si->channels[current_channel_idx].flags); | |
84 | + | |
85 | + if (!invalid_channel) { | |
86 | + sm->set_channel(sm->dev, si->channels[current_channel_idx].channel); | |
87 | + //TODO: Probe the channel | |
88 | + // FIXME make this user configurable (active/passive) | |
89 | + if(ieee80211softmac_send_mgt_frame(sm, NULL, IEEE80211_STYPE_PROBE_REQ, 0)) | |
90 | + printkl(KERN_DEBUG PFX "Sending Probe Request Failed\n"); | |
91 | + | |
92 | + /* also send directed management frame for the network we're looking for */ | |
93 | + // TODO: is this if correct, or should we do this only if scanning from assoc request? | |
94 | + if (sm->associnfo.req_essid.len) | |
95 | + ieee80211softmac_send_mgt_frame(sm, &sm->associnfo.req_essid, IEEE80211_STYPE_PROBE_REQ, 0); | |
96 | + queue_delayed_work(sm->workqueue, &si->softmac_scan, IEEE80211SOFTMAC_PROBE_DELAY); | |
97 | + return; | |
98 | + } else { | |
99 | + dprintk(PFX "Not probing Channel %d (not allowed here)\n", si->channels[current_channel_idx].channel); | |
100 | + } | |
101 | + } | |
102 | + | |
103 | + spin_lock_irqsave(&sm->lock, flags); | |
104 | + cancel_delayed_work(&si->softmac_scan); | |
105 | + si->started = 0; | |
106 | + spin_unlock_irqrestore(&sm->lock, flags); | |
107 | + | |
108 | + dprintk(PFX "Scanning finished\n"); | |
109 | + ieee80211softmac_scan_finished(sm); | |
110 | + complete_all(&sm->scaninfo->finished); | |
111 | +} | |
112 | + | |
113 | +static inline struct ieee80211softmac_scaninfo *allocate_scaninfo(struct ieee80211softmac_device *mac) | |
114 | +{ | |
115 | + /* ugh. can we call this without having the spinlock held? */ | |
116 | + struct ieee80211softmac_scaninfo *info = kmalloc(sizeof(struct ieee80211softmac_scaninfo), GFP_ATOMIC); | |
117 | + if (unlikely(!info)) | |
118 | + return NULL; | |
119 | + INIT_WORK(&info->softmac_scan, ieee80211softmac_scan, mac); | |
120 | + init_completion(&info->finished); | |
121 | + return info; | |
122 | +} | |
123 | + | |
124 | +int ieee80211softmac_start_scan_implementation(struct net_device *dev) | |
125 | +{ | |
126 | + struct ieee80211softmac_device *sm = ieee80211_priv(dev); | |
127 | + unsigned long flags; | |
128 | + | |
129 | + if (!(dev->flags & IFF_UP)) | |
130 | + return -ENODEV; | |
131 | + | |
132 | + assert(ieee80211softmac_scan_handlers_check_self(sm)); | |
133 | + if (!ieee80211softmac_scan_handlers_check_self(sm)) | |
134 | + return -EINVAL; | |
135 | + | |
136 | + spin_lock_irqsave(&sm->lock, flags); | |
137 | + /* it looks like we need to hold the lock here | |
138 | + * to make sure we don't allocate two of these... */ | |
139 | + if (unlikely(!sm->scaninfo)) | |
140 | + sm->scaninfo = allocate_scaninfo(sm); | |
141 | + if (unlikely(!sm->scaninfo)) { | |
142 | + spin_unlock_irqrestore(&sm->lock, flags); | |
143 | + return -ENOMEM; | |
144 | + } | |
145 | + | |
146 | + sm->scaninfo->skip_flags = IEEE80211_CH_INVALID; | |
147 | + if (0 /* not scanning in IEEE802.11b */)//TODO | |
148 | + sm->scaninfo->skip_flags |= IEEE80211_CH_B_ONLY; | |
149 | + if (0 /* IEEE802.11a */) {//TODO | |
150 | + sm->scaninfo->channels = sm->ieee->geo.a; | |
151 | + sm->scaninfo->number_channels = sm->ieee->geo.a_channels; | |
152 | + } else { | |
153 | + sm->scaninfo->channels = sm->ieee->geo.bg; | |
154 | + sm->scaninfo->number_channels = sm->ieee->geo.bg_channels; | |
155 | + } | |
156 | + dprintk(PFX "Start scanning with channel: %d\n", sm->scaninfo->channels[0].channel); | |
157 | + dprintk(PFX "Scanning %d channels\n", sm->scaninfo->number_channels); | |
158 | + sm->scaninfo->current_channel_idx = 0; | |
159 | + sm->scaninfo->started = 1; | |
160 | + INIT_COMPLETION(sm->scaninfo->finished); | |
161 | + queue_work(sm->workqueue, &sm->scaninfo->softmac_scan); | |
162 | + spin_unlock_irqrestore(&sm->lock, flags); | |
163 | + return 0; | |
164 | +} | |
165 | + | |
166 | +void ieee80211softmac_stop_scan_implementation(struct net_device *dev) | |
167 | +{ | |
168 | + struct ieee80211softmac_device *sm = ieee80211_priv(dev); | |
169 | + unsigned long flags; | |
170 | + | |
171 | + assert(ieee80211softmac_scan_handlers_check_self(sm)); | |
172 | + if (!ieee80211softmac_scan_handlers_check_self(sm)) | |
173 | + return; | |
174 | + | |
175 | + spin_lock_irqsave(&sm->lock, flags); | |
176 | + assert(sm->scaninfo != NULL); | |
177 | + if (sm->scaninfo) { | |
178 | + if (sm->scaninfo->started) | |
179 | + sm->scaninfo->stop = 1; | |
180 | + else | |
181 | + complete_all(&sm->scaninfo->finished); | |
182 | + } | |
183 | + spin_unlock_irqrestore(&sm->lock, flags); | |
184 | +} | |
185 | + | |
186 | +void ieee80211softmac_wait_for_scan_implementation(struct net_device *dev) | |
187 | +{ | |
188 | + struct ieee80211softmac_device *sm = ieee80211_priv(dev); | |
189 | + unsigned long flags; | |
190 | + | |
191 | + assert(ieee80211softmac_scan_handlers_check_self(sm)); | |
192 | + if (!ieee80211softmac_scan_handlers_check_self(sm)) | |
193 | + return; | |
194 | + | |
195 | + spin_lock_irqsave(&sm->lock, flags); | |
196 | + if (!sm->scaninfo->started) { | |
197 | + spin_unlock_irqrestore(&sm->lock, flags); | |
198 | + return; | |
199 | + } | |
200 | + spin_unlock_irqrestore(&sm->lock, flags); | |
201 | + wait_for_completion(&sm->scaninfo->finished); | |
202 | +} | |
203 | + | |
204 | +/* this is what drivers (that do scanning) call when they're done */ | |
205 | +void ieee80211softmac_scan_finished(struct ieee80211softmac_device *sm) | |
206 | +{ | |
207 | + unsigned long flags; | |
208 | + | |
209 | + spin_lock_irqsave(&sm->lock, flags); | |
210 | + sm->scanning = 0; | |
211 | + spin_unlock_irqrestore(&sm->lock, flags); | |
212 | + | |
213 | + ieee80211softmac_call_events(sm, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, NULL); | |
214 | +} | |
215 | + | |
216 | +EXPORT_SYMBOL_GPL(ieee80211softmac_scan_finished); |
net/ieee80211/softmac/ieee80211softmac_wx.c
1 | +/* | |
2 | + * This file contains our _wx handlers. Make sure you EXPORT_SYMBOL_GPL them | |
3 | + */ | |
4 | + | |
5 | +#include "ieee80211softmac_priv.h" | |
6 | + | |
7 | +#include <net/iw_handler.h> | |
8 | + | |
9 | + | |
10 | +int | |
11 | +ieee80211softmac_wx_trigger_scan(struct net_device *net_dev, | |
12 | + struct iw_request_info *info, | |
13 | + union iwreq_data *data, | |
14 | + char *extra) | |
15 | +{ | |
16 | + struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); | |
17 | + return ieee80211softmac_start_scan(sm); | |
18 | +} | |
19 | +EXPORT_SYMBOL_GPL(ieee80211softmac_wx_trigger_scan); | |
20 | + | |
21 | + | |
22 | +int | |
23 | +ieee80211softmac_wx_get_scan_results(struct net_device *net_dev, | |
24 | + struct iw_request_info *info, | |
25 | + union iwreq_data *data, | |
26 | + char *extra) | |
27 | +{ | |
28 | + struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); | |
29 | + return ieee80211_wx_get_scan(sm->ieee, info, data, extra); | |
30 | +} | |
31 | +EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_scan_results); | |
32 | + | |
33 | +int | |
34 | +ieee80211softmac_wx_set_essid(struct net_device *net_dev, | |
35 | + struct iw_request_info *info, | |
36 | + union iwreq_data *data, | |
37 | + char *extra) | |
38 | +{ | |
39 | + struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); | |
40 | + int length = 0; | |
41 | + unsigned long flags; | |
42 | + | |
43 | + spin_lock_irqsave(&sm->lock, flags); | |
44 | + | |
45 | + sm->associnfo.static_essid = 0; | |
46 | + | |
47 | + if (data->essid.flags && data->essid.length && extra /*required?*/) { | |
48 | + length = min(data->essid.length - 1, IW_ESSID_MAX_SIZE); | |
49 | + if (length) { | |
50 | + memcpy(sm->associnfo.req_essid.data, extra, length); | |
51 | + sm->associnfo.static_essid = 1; | |
52 | + } | |
53 | + } | |
54 | + sm->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT; | |
55 | + | |
56 | + /* set our requested ESSID length. | |
57 | + * If applicable, we have already copied the data in */ | |
58 | + sm->associnfo.req_essid.len = length; | |
59 | + | |
60 | + /* queue lower level code to do work (if necessary) */ | |
61 | + queue_work(sm->workqueue, &sm->associnfo.work); | |
62 | + | |
63 | + spin_unlock_irqrestore(&sm->lock, flags); | |
64 | + return 0; | |
65 | +} | |
66 | +EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_essid); | |
67 | + | |
68 | +int | |
69 | +ieee80211softmac_wx_get_essid(struct net_device *net_dev, | |
70 | + struct iw_request_info *info, | |
71 | + union iwreq_data *data, | |
72 | + char *extra) | |
73 | +{ | |
74 | + struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); | |
75 | + unsigned long flags; | |
76 | + | |
77 | + /* avoid getting inconsistent information */ | |
78 | + spin_lock_irqsave(&sm->lock, flags); | |
79 | + /* If all fails, return ANY (empty) */ | |
80 | + data->essid.length = 0; | |
81 | + data->essid.flags = 0; /* active */ | |
82 | + | |
83 | + /* If we have a statically configured ESSID then return it */ | |
84 | + if (sm->associnfo.static_essid) { | |
85 | + data->essid.length = sm->associnfo.req_essid.len; | |
86 | + data->essid.flags = 1; /* active */ | |
87 | + memcpy(extra, sm->associnfo.req_essid.data, sm->associnfo.req_essid.len); | |
88 | + } | |
89 | + | |
90 | + /* If we're associating/associated, return that */ | |
91 | + if (sm->associated || sm->associnfo.associating) { | |
92 | + data->essid.length = sm->associnfo.associate_essid.len; | |
93 | + data->essid.flags = 1; /* active */ | |
94 | + memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len); | |
95 | + } | |
96 | + spin_unlock_irqrestore(&sm->lock, flags); | |
97 | + return 0; | |
98 | +} | |
99 | +EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_essid); | |
100 | + | |
101 | +int | |
102 | +ieee80211softmac_wx_set_rate(struct net_device *net_dev, | |
103 | + struct iw_request_info *info, | |
104 | + union iwreq_data *data, | |
105 | + char *extra) | |
106 | +{ | |
107 | + struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); | |
108 | + struct ieee80211_device *ieee = mac->ieee; | |
109 | + unsigned long flags; | |
110 | + s32 in_rate = data->bitrate.value; | |
111 | + u8 rate; | |
112 | + int is_ofdm = 0; | |
113 | + int err = -EINVAL; | |
114 | + | |
115 | + if (in_rate == -1) { | |
116 | + /* automatic detect */ | |
117 | + if (ieee->modulation & IEEE80211_OFDM_MODULATION) | |
118 | + in_rate = 54000000; | |
119 | + else | |
120 | + in_rate = 11000000; | |
121 | + } | |
122 | + | |
123 | + switch (in_rate) { | |
124 | + case 1000000: | |
125 | + rate = IEEE80211_CCK_RATE_1MB; | |
126 | + break; | |
127 | + case 2000000: | |
128 | + rate = IEEE80211_CCK_RATE_2MB; | |
129 | + break; | |
130 | + case 5500000: | |
131 | + rate = IEEE80211_CCK_RATE_5MB; | |
132 | + break; | |
133 | + case 11000000: | |
134 | + rate = IEEE80211_CCK_RATE_11MB; | |
135 | + break; | |
136 | + case 6000000: | |
137 | + rate = IEEE80211_OFDM_RATE_6MB; | |
138 | + is_ofdm = 1; | |
139 | + break; | |
140 | + case 9000000: | |
141 | + rate = IEEE80211_OFDM_RATE_9MB; | |
142 | + is_ofdm = 1; | |
143 | + break; | |
144 | + case 12000000: | |
145 | + rate = IEEE80211_OFDM_RATE_12MB; | |
146 | + is_ofdm = 1; | |
147 | + break; | |
148 | + case 18000000: | |
149 | + rate = IEEE80211_OFDM_RATE_18MB; | |
150 | + is_ofdm = 1; | |
151 | + break; | |
152 | + case 24000000: | |
153 | + rate = IEEE80211_OFDM_RATE_24MB; | |
154 | + is_ofdm = 1; | |
155 | + break; | |
156 | + case 36000000: | |
157 | + rate = IEEE80211_OFDM_RATE_36MB; | |
158 | + is_ofdm = 1; | |
159 | + break; | |
160 | + case 48000000: | |
161 | + rate = IEEE80211_OFDM_RATE_48MB; | |
162 | + is_ofdm = 1; | |
163 | + break; | |
164 | + case 54000000: | |
165 | + rate = IEEE80211_OFDM_RATE_54MB; | |
166 | + is_ofdm = 1; | |
167 | + break; | |
168 | + default: | |
169 | + goto out; | |
170 | + } | |
171 | + | |
172 | + spin_lock_irqsave(&mac->lock, flags); | |
173 | + | |
174 | + /* Check if correct modulation for this PHY. */ | |
175 | + if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION)) | |
176 | + goto out_unlock; | |
177 | + | |
178 | + mac->txrates.default_rate = rate; | |
179 | + mac->txrates.default_fallback = lower_rate(mac, rate); | |
180 | + err = 0; | |
181 | + | |
182 | +out_unlock: | |
183 | + spin_unlock_irqrestore(&mac->lock, flags); | |
184 | +out: | |
185 | + return err; | |
186 | +} | |
187 | +EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_rate); | |
188 | + | |
189 | +int | |
190 | +ieee80211softmac_wx_get_rate(struct net_device *net_dev, | |
191 | + struct iw_request_info *info, | |
192 | + union iwreq_data *data, | |
193 | + char *extra) | |
194 | +{ | |
195 | + struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); | |
196 | + unsigned long flags; | |
197 | + int err = -EINVAL; | |
198 | + | |
199 | + spin_lock_irqsave(&mac->lock, flags); | |
200 | + switch (mac->txrates.default_rate) { | |
201 | + case IEEE80211_CCK_RATE_1MB: | |
202 | + data->bitrate.value = 1000000; | |
203 | + break; | |
204 | + case IEEE80211_CCK_RATE_2MB: | |
205 | + data->bitrate.value = 2000000; | |
206 | + break; | |
207 | + case IEEE80211_CCK_RATE_5MB: | |
208 | + data->bitrate.value = 5500000; | |
209 | + break; | |
210 | + case IEEE80211_CCK_RATE_11MB: | |
211 | + data->bitrate.value = 11000000; | |
212 | + break; | |
213 | + case IEEE80211_OFDM_RATE_6MB: | |
214 | + data->bitrate.value = 6000000; | |
215 | + break; | |
216 | + case IEEE80211_OFDM_RATE_9MB: | |
217 | + data->bitrate.value = 9000000; | |
218 | + break; | |
219 | + case IEEE80211_OFDM_RATE_12MB: | |
220 | + data->bitrate.value = 12000000; | |
221 | + break; | |
222 | + case IEEE80211_OFDM_RATE_18MB: | |
223 | + data->bitrate.value = 18000000; | |
224 | + break; | |
225 | + case IEEE80211_OFDM_RATE_24MB: | |
226 | + data->bitrate.value = 24000000; | |
227 | + break; | |
228 | + case IEEE80211_OFDM_RATE_36MB: | |
229 | + data->bitrate.value = 36000000; | |
230 | + break; | |
231 | + case IEEE80211_OFDM_RATE_48MB: | |
232 | + data->bitrate.value = 48000000; | |
233 | + break; | |
234 | + case IEEE80211_OFDM_RATE_54MB: | |
235 | + data->bitrate.value = 54000000; | |
236 | + break; | |
237 | + default: | |
238 | + assert(0); | |
239 | + goto out_unlock; | |
240 | + } | |
241 | + err = 0; | |
242 | +out_unlock: | |
243 | + spin_unlock_irqrestore(&mac->lock, flags); | |
244 | + | |
245 | + return err; | |
246 | +} | |
247 | +EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_rate); | |
248 | + | |
249 | +int | |
250 | +ieee80211softmac_wx_get_wap(struct net_device *net_dev, | |
251 | + struct iw_request_info *info, | |
252 | + union iwreq_data *data, | |
253 | + char *extra) | |
254 | +{ | |
255 | + struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); | |
256 | + int err = 0; | |
257 | + unsigned long flags; | |
258 | + | |
259 | + spin_lock_irqsave(&mac->lock, flags); | |
260 | + if (mac->associnfo.bssvalid) | |
261 | + memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN); | |
262 | + else | |
263 | + memset(data->ap_addr.sa_data, 0xff, ETH_ALEN); | |
264 | + data->ap_addr.sa_family = ARPHRD_ETHER; | |
265 | + spin_unlock_irqrestore(&mac->lock, flags); | |
266 | + return err; | |
267 | +} | |
268 | +EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap); | |
269 | + | |
270 | +int | |
271 | +ieee80211softmac_wx_set_wap(struct net_device *net_dev, | |
272 | + struct iw_request_info *info, | |
273 | + union iwreq_data *data, | |
274 | + char *extra) | |
275 | +{ | |
276 | + struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); | |
277 | + static const unsigned char any[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | |
278 | + static const unsigned char off[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | |
279 | + unsigned long flags; | |
280 | + | |
281 | + /* sanity check */ | |
282 | + if (data->ap_addr.sa_family != ARPHRD_ETHER) { | |
283 | + return -EINVAL; | |
284 | + } | |
285 | + | |
286 | + spin_lock_irqsave(&mac->lock, flags); | |
287 | + if (!memcmp(any, data->ap_addr.sa_data, ETH_ALEN) || | |
288 | + !memcmp(off, data->ap_addr.sa_data, ETH_ALEN)) { | |
289 | + queue_work(mac->workqueue, &mac->associnfo.work); | |
290 | + goto out; | |
291 | + } else { | |
292 | + if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) { | |
293 | + if (mac->associnfo.associating || mac->associated) { | |
294 | + /* bssid unchanged and associated or associating - just return */ | |
295 | + goto out; | |
296 | + } | |
297 | + } else { | |
298 | + /* copy new value in data->ap_addr.sa_data to bssid */ | |
299 | + memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN); | |
300 | + } | |
301 | + /* queue associate if new bssid or (old one again and not associated) */ | |
302 | + queue_work(mac->workqueue,&mac->associnfo.work); | |
303 | + } | |
304 | + | |
305 | +out: | |
306 | + spin_unlock_irqrestore(&mac->lock, flags); | |
307 | + return 0; | |
308 | +} | |
309 | +EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap); | |
310 | + | |
311 | +int | |
312 | +ieee80211softmac_wx_set_genie(struct net_device *dev, | |
313 | + struct iw_request_info *info, | |
314 | + union iwreq_data *wrqu, | |
315 | + char *extra) | |
316 | +{ | |
317 | + struct ieee80211softmac_device *mac = ieee80211_priv(dev); | |
318 | + unsigned long flags; | |
319 | + int err = 0; | |
320 | + char *buf; | |
321 | + int i; | |
322 | + | |
323 | + spin_lock_irqsave(&mac->lock, flags); | |
324 | + /* bleh. shouldn't be locked for that kmalloc... */ | |
325 | + | |
326 | + if (wrqu->data.length) { | |
327 | + if ((wrqu->data.length < 2) || (extra[1]+2 != wrqu->data.length)) { | |
328 | + /* this is an IE, so the length must be | |
329 | + * correct. Is it possible though that | |
330 | + * more than one IE is passed in? | |
331 | + */ | |
332 | + err = -EINVAL; | |
333 | + goto out; | |
334 | + } | |
335 | + if (mac->wpa.IEbuflen <= wrqu->data.length) { | |
336 | + buf = kmalloc(wrqu->data.length, GFP_ATOMIC); | |
337 | + if (!buf) { | |
338 | + err = -ENOMEM; | |
339 | + goto out; | |
340 | + } | |
341 | + kfree(mac->wpa.IE); | |
342 | + mac->wpa.IE = buf; | |
343 | + mac->wpa.IEbuflen = wrqu->data.length; | |
344 | + } | |
345 | + memcpy(mac->wpa.IE, extra, wrqu->data.length); | |
346 | + dprintk(KERN_INFO PFX "generic IE set to "); | |
347 | + for (i=0;i<wrqu->data.length;i++) | |
348 | + dprintk("%.2x", mac->wpa.IE[i]); | |
349 | + dprintk("\n"); | |
350 | + mac->wpa.IElen = wrqu->data.length; | |
351 | + } else { | |
352 | + kfree(mac->wpa.IE); | |
353 | + mac->wpa.IE = NULL; | |
354 | + mac->wpa.IElen = 0; | |
355 | + mac->wpa.IEbuflen = 0; | |
356 | + } | |
357 | + | |
358 | + out: | |
359 | + spin_unlock_irqrestore(&mac->lock, flags); | |
360 | + return err; | |
361 | +} | |
362 | +EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie); | |
363 | + | |
364 | +int | |
365 | +ieee80211softmac_wx_get_genie(struct net_device *dev, | |
366 | + struct iw_request_info *info, | |
367 | + union iwreq_data *wrqu, | |
368 | + char *extra) | |
369 | +{ | |
370 | + struct ieee80211softmac_device *mac = ieee80211_priv(dev); | |
371 | + unsigned long flags; | |
372 | + int err = 0; | |
373 | + int space = wrqu->data.length; | |
374 | + | |
375 | + spin_lock_irqsave(&mac->lock, flags); | |
376 | + | |
377 | + wrqu->data.length = 0; | |
378 | + | |
379 | + if (mac->wpa.IE && mac->wpa.IElen) { | |
380 | + wrqu->data.length = mac->wpa.IElen; | |
381 | + if (mac->wpa.IElen <= space) | |
382 | + memcpy(extra, mac->wpa.IE, mac->wpa.IElen); | |
383 | + else | |
384 | + err = -E2BIG; | |
385 | + } | |
386 | + spin_unlock_irqrestore(&mac->lock, flags); | |
387 | + return err; | |
388 | +} | |
389 | +EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie); |