]>
Commit | Line | Data |
---|---|---|
876c9d3a MT |
1 | /** |
2 | * This file contains ioctl functions | |
3 | */ | |
4 | #include <linux/ctype.h> | |
5 | #include <linux/delay.h> | |
6 | #include <linux/if.h> | |
7 | #include <linux/if_arp.h> | |
8 | #include <linux/wireless.h> | |
9 | #include <linux/bitops.h> | |
10 | ||
7e272fcf | 11 | #include <net/lib80211.h> |
876c9d3a MT |
12 | #include <net/iw_handler.h> |
13 | ||
14 | #include "host.h" | |
15 | #include "radiotap.h" | |
16 | #include "decl.h" | |
17 | #include "defs.h" | |
18 | #include "dev.h" | |
876c9d3a | 19 | #include "wext.h" |
245bf20f | 20 | #include "scan.h" |
876c9d3a | 21 | #include "assoc.h" |
8e3c91bb | 22 | #include "cmd.h" |
876c9d3a MT |
23 | |
24 | ||
69f9032d | 25 | static inline void lbs_postpone_association_work(struct lbs_private *priv) |
9f9dac28 | 26 | { |
aa21c004 | 27 | if (priv->surpriseremoved) |
9f9dac28 HS |
28 | return; |
29 | cancel_delayed_work(&priv->assoc_work); | |
30 | queue_delayed_work(priv->work_thread, &priv->assoc_work, HZ / 2); | |
31 | } | |
32 | ||
9c31fd63 JC |
33 | static inline void lbs_do_association_work(struct lbs_private *priv) |
34 | { | |
35 | if (priv->surpriseremoved) | |
36 | return; | |
37 | cancel_delayed_work(&priv->assoc_work); | |
38 | queue_delayed_work(priv->work_thread, &priv->assoc_work, 0); | |
39 | } | |
40 | ||
69f9032d | 41 | static inline void lbs_cancel_association_work(struct lbs_private *priv) |
9f9dac28 HS |
42 | { |
43 | cancel_delayed_work(&priv->assoc_work); | |
aa21c004 DW |
44 | kfree(priv->pending_assoc_req); |
45 | priv->pending_assoc_req = NULL; | |
9f9dac28 HS |
46 | } |
47 | ||
fea2b8ec HS |
48 | void lbs_send_disconnect_notification(struct lbs_private *priv) |
49 | { | |
50 | union iwreq_data wrqu; | |
51 | ||
52 | memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); | |
53 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | |
54 | wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); | |
55 | } | |
56 | ||
560c6338 | 57 | static void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str) |
6e85e0b7 HS |
58 | { |
59 | union iwreq_data iwrq; | |
60 | u8 buf[50]; | |
61 | ||
62 | lbs_deb_enter(LBS_DEB_WEXT); | |
63 | ||
64 | memset(&iwrq, 0, sizeof(union iwreq_data)); | |
65 | memset(buf, 0, sizeof(buf)); | |
66 | ||
67 | snprintf(buf, sizeof(buf) - 1, "%s", str); | |
68 | ||
69 | iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN; | |
70 | ||
71 | /* Send Event to upper layer */ | |
72 | lbs_deb_wext("event indication string %s\n", (char *)buf); | |
73 | lbs_deb_wext("event indication length %d\n", iwrq.data.length); | |
74 | lbs_deb_wext("sending wireless event IWEVCUSTOM for %s\n", str); | |
75 | ||
76 | wireless_send_event(priv->dev, IWEVCUSTOM, &iwrq, buf); | |
77 | ||
78 | lbs_deb_leave(LBS_DEB_WEXT); | |
79 | } | |
80 | ||
560c6338 HS |
81 | /** |
82 | * @brief This function handles MIC failure event. | |
83 | * | |
84 | * @param priv A pointer to struct lbs_private structure | |
85 | * @para event the event id | |
86 | * @return n/a | |
87 | */ | |
88 | void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event) | |
89 | { | |
90 | char buf[50]; | |
91 | ||
92 | lbs_deb_enter(LBS_DEB_CMD); | |
93 | memset(buf, 0, sizeof(buf)); | |
94 | ||
95 | sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication "); | |
96 | ||
97 | if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) | |
98 | strcat(buf, "unicast "); | |
99 | else | |
100 | strcat(buf, "multicast "); | |
101 | ||
102 | lbs_send_iwevcustom_event(priv, buf); | |
103 | lbs_deb_leave(LBS_DEB_CMD); | |
104 | } | |
105 | ||
876c9d3a MT |
106 | /** |
107 | * @brief Find the channel frequency power info with specific channel | |
108 | * | |
aa21c004 | 109 | * @param priv A pointer to struct lbs_private structure |
876c9d3a MT |
110 | * @param band it can be BAND_A, BAND_G or BAND_B |
111 | * @param channel the channel for looking | |
112 | * @return A pointer to struct chan_freq_power structure or NULL if not find. | |
113 | */ | |
69f9032d | 114 | struct chan_freq_power *lbs_find_cfp_by_band_and_channel( |
aa21c004 | 115 | struct lbs_private *priv, |
69f9032d HS |
116 | u8 band, |
117 | u16 channel) | |
876c9d3a MT |
118 | { |
119 | struct chan_freq_power *cfp = NULL; | |
120 | struct region_channel *rc; | |
876c9d3a MT |
121 | int i, j; |
122 | ||
aa21c004 DW |
123 | for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) { |
124 | rc = &priv->region_channel[j]; | |
876c9d3a | 125 | |
876c9d3a MT |
126 | if (!rc->valid || !rc->CFP) |
127 | continue; | |
128 | if (rc->band != band) | |
129 | continue; | |
130 | for (i = 0; i < rc->nrcfp; i++) { | |
131 | if (rc->CFP[i].channel == channel) { | |
132 | cfp = &rc->CFP[i]; | |
133 | break; | |
134 | } | |
135 | } | |
136 | } | |
137 | ||
138 | if (!cfp && channel) | |
10078321 | 139 | lbs_deb_wext("lbs_find_cfp_by_band_and_channel: can't find " |
9012b28a | 140 | "cfp by band %d / channel %d\n", band, channel); |
876c9d3a MT |
141 | |
142 | return cfp; | |
143 | } | |
144 | ||
145 | /** | |
146 | * @brief Find the channel frequency power info with specific frequency | |
147 | * | |
aa21c004 | 148 | * @param priv A pointer to struct lbs_private structure |
876c9d3a MT |
149 | * @param band it can be BAND_A, BAND_G or BAND_B |
150 | * @param freq the frequency for looking | |
151 | * @return A pointer to struct chan_freq_power structure or NULL if not find. | |
152 | */ | |
69f9032d | 153 | static struct chan_freq_power *find_cfp_by_band_and_freq( |
aa21c004 | 154 | struct lbs_private *priv, |
69f9032d HS |
155 | u8 band, |
156 | u32 freq) | |
876c9d3a MT |
157 | { |
158 | struct chan_freq_power *cfp = NULL; | |
159 | struct region_channel *rc; | |
876c9d3a MT |
160 | int i, j; |
161 | ||
aa21c004 DW |
162 | for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) { |
163 | rc = &priv->region_channel[j]; | |
876c9d3a | 164 | |
876c9d3a MT |
165 | if (!rc->valid || !rc->CFP) |
166 | continue; | |
167 | if (rc->band != band) | |
168 | continue; | |
169 | for (i = 0; i < rc->nrcfp; i++) { | |
170 | if (rc->CFP[i].freq == freq) { | |
171 | cfp = &rc->CFP[i]; | |
172 | break; | |
173 | } | |
174 | } | |
175 | } | |
176 | ||
177 | if (!cfp && freq) | |
9012b28a HS |
178 | lbs_deb_wext("find_cfp_by_band_and_freql: can't find cfp by " |
179 | "band %d / freq %d\n", band, freq); | |
876c9d3a MT |
180 | |
181 | return cfp; | |
182 | } | |
183 | ||
876c9d3a | 184 | /** |
8c512765 | 185 | * @brief Copy active data rates based on adapter mode and status |
876c9d3a | 186 | * |
aa21c004 | 187 | * @param priv A pointer to struct lbs_private structure |
876c9d3a | 188 | * @param rate The buf to return the active rates |
876c9d3a | 189 | */ |
aa21c004 | 190 | static void copy_active_data_rates(struct lbs_private *priv, u8 *rates) |
876c9d3a | 191 | { |
9012b28a | 192 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 193 | |
aa21c004 DW |
194 | if ((priv->connect_status != LBS_CONNECTED) && |
195 | (priv->mesh_connect_status != LBS_CONNECTED)) | |
10078321 | 196 | memcpy(rates, lbs_bg_rates, MAX_RATES); |
8c512765 | 197 | else |
aa21c004 | 198 | memcpy(rates, priv->curbssparams.rates, MAX_RATES); |
876c9d3a | 199 | |
8c512765 | 200 | lbs_deb_leave(LBS_DEB_WEXT); |
876c9d3a MT |
201 | } |
202 | ||
10078321 | 203 | static int lbs_get_name(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
204 | char *cwrq, char *extra) |
205 | { | |
876c9d3a | 206 | |
9012b28a | 207 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 208 | |
9483f031 JT |
209 | /* We could add support for 802.11n here as needed. Jean II */ |
210 | snprintf(cwrq, IFNAMSIZ, "IEEE 802.11b/g"); | |
876c9d3a | 211 | |
9012b28a | 212 | lbs_deb_leave(LBS_DEB_WEXT); |
876c9d3a MT |
213 | return 0; |
214 | } | |
215 | ||
10078321 | 216 | static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
217 | struct iw_freq *fwrq, char *extra) |
218 | { | |
ab65f649 | 219 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a MT |
220 | struct chan_freq_power *cfp; |
221 | ||
9012b28a | 222 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 223 | |
aa21c004 | 224 | cfp = lbs_find_cfp_by_band_and_channel(priv, 0, |
c14951fe | 225 | priv->channel); |
876c9d3a MT |
226 | |
227 | if (!cfp) { | |
c14951fe | 228 | if (priv->channel) |
9012b28a | 229 | lbs_deb_wext("invalid channel %d\n", |
c14951fe | 230 | priv->channel); |
876c9d3a MT |
231 | return -EINVAL; |
232 | } | |
233 | ||
234 | fwrq->m = (long)cfp->freq * 100000; | |
235 | fwrq->e = 1; | |
236 | ||
9012b28a HS |
237 | lbs_deb_wext("freq %u\n", fwrq->m); |
238 | lbs_deb_leave(LBS_DEB_WEXT); | |
876c9d3a MT |
239 | return 0; |
240 | } | |
241 | ||
10078321 | 242 | static int lbs_get_wap(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
243 | struct sockaddr *awrq, char *extra) |
244 | { | |
ab65f649 | 245 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a | 246 | |
9012b28a | 247 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 248 | |
aa21c004 DW |
249 | if (priv->connect_status == LBS_CONNECTED) { |
250 | memcpy(awrq->sa_data, priv->curbssparams.bssid, ETH_ALEN); | |
876c9d3a MT |
251 | } else { |
252 | memset(awrq->sa_data, 0, ETH_ALEN); | |
253 | } | |
254 | awrq->sa_family = ARPHRD_ETHER; | |
255 | ||
9012b28a | 256 | lbs_deb_leave(LBS_DEB_WEXT); |
876c9d3a MT |
257 | return 0; |
258 | } | |
259 | ||
10078321 | 260 | static int lbs_set_nick(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
261 | struct iw_point *dwrq, char *extra) |
262 | { | |
ab65f649 | 263 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a | 264 | |
9012b28a | 265 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a MT |
266 | |
267 | /* | |
268 | * Check the size of the string | |
269 | */ | |
270 | ||
271 | if (dwrq->length > 16) { | |
272 | return -E2BIG; | |
273 | } | |
274 | ||
aa21c004 DW |
275 | mutex_lock(&priv->lock); |
276 | memset(priv->nodename, 0, sizeof(priv->nodename)); | |
277 | memcpy(priv->nodename, extra, dwrq->length); | |
278 | mutex_unlock(&priv->lock); | |
876c9d3a | 279 | |
9012b28a | 280 | lbs_deb_leave(LBS_DEB_WEXT); |
876c9d3a MT |
281 | return 0; |
282 | } | |
283 | ||
10078321 | 284 | static int lbs_get_nick(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
285 | struct iw_point *dwrq, char *extra) |
286 | { | |
ab65f649 | 287 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a | 288 | |
9012b28a | 289 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 290 | |
aa21c004 DW |
291 | dwrq->length = strlen(priv->nodename); |
292 | memcpy(extra, priv->nodename, dwrq->length); | |
04799fae | 293 | extra[dwrq->length] = '\0'; |
876c9d3a | 294 | |
04799fae | 295 | dwrq->flags = 1; /* active */ |
876c9d3a | 296 | |
9012b28a | 297 | lbs_deb_leave(LBS_DEB_WEXT); |
876c9d3a MT |
298 | return 0; |
299 | } | |
300 | ||
f5e05b69 LCCR |
301 | static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info, |
302 | struct iw_point *dwrq, char *extra) | |
303 | { | |
ab65f649 | 304 | struct lbs_private *priv = dev->ml_priv; |
f5e05b69 LCCR |
305 | |
306 | lbs_deb_enter(LBS_DEB_WEXT); | |
307 | ||
308 | /* Use nickname to indicate that mesh is on */ | |
309 | ||
aa21c004 | 310 | if (priv->mesh_connect_status == LBS_CONNECTED) { |
f5e05b69 LCCR |
311 | strncpy(extra, "Mesh", 12); |
312 | extra[12] = '\0'; | |
9483f031 | 313 | dwrq->length = strlen(extra); |
f5e05b69 LCCR |
314 | } |
315 | ||
316 | else { | |
317 | extra[0] = '\0'; | |
9483f031 | 318 | dwrq->length = 0; |
f5e05b69 LCCR |
319 | } |
320 | ||
321 | lbs_deb_leave(LBS_DEB_WEXT); | |
322 | return 0; | |
323 | } | |
04799fae | 324 | |
10078321 | 325 | static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
326 | struct iw_param *vwrq, char *extra) |
327 | { | |
328 | int ret = 0; | |
ab65f649 | 329 | struct lbs_private *priv = dev->ml_priv; |
39fcf7a3 | 330 | u32 val = vwrq->value; |
876c9d3a | 331 | |
9012b28a | 332 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 333 | |
39fcf7a3 DW |
334 | if (vwrq->disabled) |
335 | val = MRVDRV_RTS_MAX_VALUE; | |
876c9d3a | 336 | |
375da53b | 337 | if (val > MRVDRV_RTS_MAX_VALUE) /* min rts value is 0 */ |
39fcf7a3 DW |
338 | return -EINVAL; |
339 | ||
340 | ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, (u16) val); | |
876c9d3a | 341 | |
9012b28a | 342 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); |
876c9d3a MT |
343 | return ret; |
344 | } | |
345 | ||
10078321 | 346 | static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
347 | struct iw_param *vwrq, char *extra) |
348 | { | |
ab65f649 | 349 | struct lbs_private *priv = dev->ml_priv; |
39fcf7a3 DW |
350 | int ret = 0; |
351 | u16 val = 0; | |
876c9d3a | 352 | |
9012b28a | 353 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 354 | |
39fcf7a3 | 355 | ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, &val); |
9012b28a HS |
356 | if (ret) |
357 | goto out; | |
876c9d3a | 358 | |
39fcf7a3 | 359 | vwrq->value = val; |
375da53b | 360 | vwrq->disabled = val > MRVDRV_RTS_MAX_VALUE; /* min rts value is 0 */ |
876c9d3a MT |
361 | vwrq->fixed = 1; |
362 | ||
9012b28a HS |
363 | out: |
364 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); | |
365 | return ret; | |
876c9d3a MT |
366 | } |
367 | ||
10078321 | 368 | static int lbs_set_frag(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
369 | struct iw_param *vwrq, char *extra) |
370 | { | |
ab65f649 | 371 | struct lbs_private *priv = dev->ml_priv; |
39fcf7a3 DW |
372 | int ret = 0; |
373 | u32 val = vwrq->value; | |
876c9d3a | 374 | |
9012b28a | 375 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 376 | |
39fcf7a3 DW |
377 | if (vwrq->disabled) |
378 | val = MRVDRV_FRAG_MAX_VALUE; | |
379 | ||
380 | if (val < MRVDRV_FRAG_MIN_VALUE || val > MRVDRV_FRAG_MAX_VALUE) | |
381 | return -EINVAL; | |
876c9d3a | 382 | |
39fcf7a3 | 383 | ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, (u16) val); |
9012b28a HS |
384 | |
385 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); | |
876c9d3a MT |
386 | return ret; |
387 | } | |
388 | ||
10078321 | 389 | static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
390 | struct iw_param *vwrq, char *extra) |
391 | { | |
ab65f649 | 392 | struct lbs_private *priv = dev->ml_priv; |
39fcf7a3 DW |
393 | int ret = 0; |
394 | u16 val = 0; | |
876c9d3a | 395 | |
9012b28a | 396 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 397 | |
39fcf7a3 | 398 | ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, &val); |
9012b28a HS |
399 | if (ret) |
400 | goto out; | |
876c9d3a | 401 | |
39fcf7a3 DW |
402 | vwrq->value = val; |
403 | vwrq->disabled = ((val < MRVDRV_FRAG_MIN_VALUE) | |
404 | || (val > MRVDRV_FRAG_MAX_VALUE)); | |
876c9d3a MT |
405 | vwrq->fixed = 1; |
406 | ||
9012b28a HS |
407 | out: |
408 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); | |
876c9d3a MT |
409 | return ret; |
410 | } | |
411 | ||
10078321 | 412 | static int lbs_get_mode(struct net_device *dev, |
876c9d3a MT |
413 | struct iw_request_info *info, u32 * uwrq, char *extra) |
414 | { | |
ab65f649 | 415 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a | 416 | |
9012b28a | 417 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 418 | |
aa21c004 | 419 | *uwrq = priv->mode; |
876c9d3a | 420 | |
9012b28a | 421 | lbs_deb_leave(LBS_DEB_WEXT); |
876c9d3a MT |
422 | return 0; |
423 | } | |
424 | ||
f5e05b69 LCCR |
425 | static int mesh_wlan_get_mode(struct net_device *dev, |
426 | struct iw_request_info *info, u32 * uwrq, | |
427 | char *extra) | |
428 | { | |
429 | lbs_deb_enter(LBS_DEB_WEXT); | |
430 | ||
39fcf7a3 | 431 | *uwrq = IW_MODE_REPEAT; |
f5e05b69 LCCR |
432 | |
433 | lbs_deb_leave(LBS_DEB_WEXT); | |
434 | return 0; | |
435 | } | |
436 | ||
10078321 | 437 | static int lbs_get_txpow(struct net_device *dev, |
876c9d3a MT |
438 | struct iw_request_info *info, |
439 | struct iw_param *vwrq, char *extra) | |
440 | { | |
ab65f649 | 441 | struct lbs_private *priv = dev->ml_priv; |
87c8c72d | 442 | s16 curlevel = 0; |
d5db2dfa | 443 | int ret = 0; |
876c9d3a | 444 | |
9012b28a | 445 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 446 | |
d5db2dfa DW |
447 | if (!priv->radio_on) { |
448 | lbs_deb_wext("tx power off\n"); | |
449 | vwrq->value = 0; | |
450 | vwrq->disabled = 1; | |
451 | goto out; | |
452 | } | |
453 | ||
87c8c72d | 454 | ret = lbs_get_tx_power(priv, &curlevel, NULL, NULL); |
9012b28a HS |
455 | if (ret) |
456 | goto out; | |
876c9d3a | 457 | |
87c8c72d | 458 | lbs_deb_wext("tx power level %d dbm\n", curlevel); |
87c8c72d | 459 | priv->txpower_cur = curlevel; |
d5db2dfa | 460 | |
87c8c72d | 461 | vwrq->value = curlevel; |
876c9d3a | 462 | vwrq->fixed = 1; |
d5db2dfa DW |
463 | vwrq->disabled = 0; |
464 | vwrq->flags = IW_TXPOW_DBM; | |
876c9d3a | 465 | |
9012b28a HS |
466 | out: |
467 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); | |
468 | return ret; | |
876c9d3a MT |
469 | } |
470 | ||
10078321 | 471 | static int lbs_set_retry(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
472 | struct iw_param *vwrq, char *extra) |
473 | { | |
ab65f649 | 474 | struct lbs_private *priv = dev->ml_priv; |
39fcf7a3 DW |
475 | int ret = 0; |
476 | u16 slimit = 0, llimit = 0; | |
876c9d3a | 477 | |
9012b28a | 478 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 479 | |
39fcf7a3 DW |
480 | if ((vwrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) |
481 | return -EOPNOTSUPP; | |
482 | ||
483 | /* The MAC has a 4-bit Total_Tx_Count register | |
484 | Total_Tx_Count = 1 + Tx_Retry_Count */ | |
876c9d3a MT |
485 | #define TX_RETRY_MIN 0 |
486 | #define TX_RETRY_MAX 14 | |
39fcf7a3 DW |
487 | if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX) |
488 | return -EINVAL; | |
876c9d3a | 489 | |
39fcf7a3 DW |
490 | /* Add 1 to convert retry count to try count */ |
491 | if (vwrq->flags & IW_RETRY_SHORT) | |
492 | slimit = (u16) (vwrq->value + 1); | |
493 | else if (vwrq->flags & IW_RETRY_LONG) | |
494 | llimit = (u16) (vwrq->value + 1); | |
495 | else | |
496 | slimit = llimit = (u16) (vwrq->value + 1); /* set both */ | |
876c9d3a | 497 | |
39fcf7a3 DW |
498 | if (llimit) { |
499 | ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_LONG_RETRY_LIMIT, | |
500 | llimit); | |
501 | if (ret) | |
502 | goto out; | |
503 | } | |
876c9d3a | 504 | |
39fcf7a3 DW |
505 | if (slimit) { |
506 | /* txretrycount follows the short retry limit */ | |
507 | priv->txretrycount = slimit; | |
508 | ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_SHORT_RETRY_LIMIT, | |
509 | slimit); | |
9012b28a HS |
510 | if (ret) |
511 | goto out; | |
876c9d3a MT |
512 | } |
513 | ||
9012b28a HS |
514 | out: |
515 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); | |
516 | return ret; | |
876c9d3a MT |
517 | } |
518 | ||
10078321 | 519 | static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
520 | struct iw_param *vwrq, char *extra) |
521 | { | |
ab65f649 | 522 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a | 523 | int ret = 0; |
39fcf7a3 | 524 | u16 val = 0; |
876c9d3a | 525 | |
9012b28a HS |
526 | lbs_deb_enter(LBS_DEB_WEXT); |
527 | ||
876c9d3a | 528 | vwrq->disabled = 0; |
39fcf7a3 DW |
529 | |
530 | if (vwrq->flags & IW_RETRY_LONG) { | |
531 | ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_LONG_RETRY_LIMIT, &val); | |
532 | if (ret) | |
533 | goto out; | |
534 | ||
535 | /* Subtract 1 to convert try count to retry count */ | |
536 | vwrq->value = val - 1; | |
537 | vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; | |
538 | } else { | |
539 | ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_SHORT_RETRY_LIMIT, &val); | |
540 | if (ret) | |
541 | goto out; | |
542 | ||
543 | /* txretry count follows the short retry limit */ | |
544 | priv->txretrycount = val; | |
876c9d3a | 545 | /* Subtract 1 to convert try count to retry count */ |
39fcf7a3 DW |
546 | vwrq->value = val - 1; |
547 | vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT; | |
876c9d3a MT |
548 | } |
549 | ||
9012b28a HS |
550 | out: |
551 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); | |
552 | return ret; | |
876c9d3a MT |
553 | } |
554 | ||
555 | static inline void sort_channels(struct iw_freq *freq, int num) | |
556 | { | |
557 | int i, j; | |
558 | struct iw_freq temp; | |
559 | ||
560 | for (i = 0; i < num; i++) | |
561 | for (j = i + 1; j < num; j++) | |
562 | if (freq[i].i > freq[j].i) { | |
563 | temp.i = freq[i].i; | |
564 | temp.m = freq[i].m; | |
565 | ||
566 | freq[i].i = freq[j].i; | |
567 | freq[i].m = freq[j].m; | |
568 | ||
569 | freq[j].i = temp.i; | |
570 | freq[j].m = temp.m; | |
571 | } | |
572 | } | |
573 | ||
574 | /* data rate listing | |
575 | MULTI_BANDS: | |
576 | abg a b b/g | |
577 | Infra G(12) A(8) B(4) G(12) | |
578 | Adhoc A+B(12) A(8) B(4) B(4) | |
579 | ||
580 | non-MULTI_BANDS: | |
581 | b b/g | |
582 | Infra B(4) G(12) | |
583 | Adhoc B(4) B(4) | |
584 | */ | |
585 | /** | |
586 | * @brief Get Range Info | |
587 | * | |
588 | * @param dev A pointer to net_device structure | |
589 | * @param info A pointer to iw_request_info structure | |
590 | * @param vwrq A pointer to iw_param structure | |
591 | * @param extra A pointer to extra data buf | |
592 | * @return 0 --success, otherwise fail | |
593 | */ | |
10078321 | 594 | static int lbs_get_range(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
595 | struct iw_point *dwrq, char *extra) |
596 | { | |
597 | int i, j; | |
ab65f649 | 598 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a MT |
599 | struct iw_range *range = (struct iw_range *)extra; |
600 | struct chan_freq_power *cfp; | |
8c512765 | 601 | u8 rates[MAX_RATES + 1]; |
876c9d3a | 602 | |
9012b28a | 603 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a MT |
604 | |
605 | dwrq->length = sizeof(struct iw_range); | |
606 | memset(range, 0, sizeof(struct iw_range)); | |
607 | ||
608 | range->min_nwid = 0; | |
609 | range->max_nwid = 0; | |
610 | ||
611 | memset(rates, 0, sizeof(rates)); | |
aa21c004 | 612 | copy_active_data_rates(priv, rates); |
8c512765 DW |
613 | range->num_bitrates = strnlen(rates, IW_MAX_BITRATES); |
614 | for (i = 0; i < range->num_bitrates; i++) | |
615 | range->bitrate[i] = rates[i] * 500000; | |
876c9d3a | 616 | range->num_bitrates = i; |
9012b28a | 617 | lbs_deb_wext("IW_MAX_BITRATES %d, num_bitrates %d\n", IW_MAX_BITRATES, |
876c9d3a MT |
618 | range->num_bitrates); |
619 | ||
620 | range->num_frequency = 0; | |
52933d81 HS |
621 | |
622 | range->scan_capa = IW_SCAN_CAPA_ESSID; | |
623 | ||
d37b4fdd HS |
624 | for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES) |
625 | && (j < ARRAY_SIZE(priv->region_channel)); j++) { | |
626 | cfp = priv->region_channel[j].CFP; | |
876c9d3a | 627 | for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES) |
d37b4fdd HS |
628 | && priv->region_channel[j].valid |
629 | && cfp | |
630 | && (i < priv->region_channel[j].nrcfp); i++) { | |
631 | range->freq[range->num_frequency].i = | |
632 | (long)cfp->channel; | |
876c9d3a | 633 | range->freq[range->num_frequency].m = |
d37b4fdd | 634 | (long)cfp->freq * 100000; |
876c9d3a | 635 | range->freq[range->num_frequency].e = 1; |
d37b4fdd | 636 | cfp++; |
876c9d3a MT |
637 | range->num_frequency++; |
638 | } | |
876c9d3a MT |
639 | } |
640 | ||
9012b28a | 641 | lbs_deb_wext("IW_MAX_FREQUENCIES %d, num_frequency %d\n", |
876c9d3a MT |
642 | IW_MAX_FREQUENCIES, range->num_frequency); |
643 | ||
644 | range->num_channels = range->num_frequency; | |
645 | ||
646 | sort_channels(&range->freq[0], range->num_frequency); | |
647 | ||
648 | /* | |
649 | * Set an indication of the max TCP throughput in bit/s that we can | |
650 | * expect using this interface | |
651 | */ | |
652 | if (i > 2) | |
653 | range->throughput = 5000 * 1000; | |
654 | else | |
655 | range->throughput = 1500 * 1000; | |
656 | ||
657 | range->min_rts = MRVDRV_RTS_MIN_VALUE; | |
658 | range->max_rts = MRVDRV_RTS_MAX_VALUE; | |
659 | range->min_frag = MRVDRV_FRAG_MIN_VALUE; | |
660 | range->max_frag = MRVDRV_FRAG_MAX_VALUE; | |
661 | ||
662 | range->encoding_size[0] = 5; | |
663 | range->encoding_size[1] = 13; | |
664 | range->num_encoding_sizes = 2; | |
665 | range->max_encoding_tokens = 4; | |
666 | ||
d4ff0ef6 HS |
667 | /* |
668 | * Right now we support only "iwconfig ethX power on|off" | |
669 | */ | |
670 | range->pm_capa = IW_POWER_ON; | |
876c9d3a MT |
671 | |
672 | /* | |
673 | * Minimum version we recommend | |
674 | */ | |
675 | range->we_version_source = 15; | |
676 | ||
677 | /* | |
678 | * Version we are compiled with | |
679 | */ | |
680 | range->we_version_compiled = WIRELESS_EXT; | |
681 | ||
682 | range->retry_capa = IW_RETRY_LIMIT; | |
683 | range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX; | |
684 | ||
685 | range->min_retry = TX_RETRY_MIN; | |
686 | range->max_retry = TX_RETRY_MAX; | |
687 | ||
688 | /* | |
689 | * Set the qual, level and noise range values | |
690 | */ | |
691 | range->max_qual.qual = 100; | |
692 | range->max_qual.level = 0; | |
693 | range->max_qual.noise = 0; | |
694 | range->max_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; | |
695 | ||
696 | range->avg_qual.qual = 70; | |
697 | /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ | |
698 | range->avg_qual.level = 0; | |
699 | range->avg_qual.noise = 0; | |
700 | range->avg_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; | |
701 | ||
702 | range->sensitivity = 0; | |
703 | ||
87c8c72d | 704 | /* Setup the supported power level ranges */ |
876c9d3a | 705 | memset(range->txpower, 0, sizeof(range->txpower)); |
87c8c72d DW |
706 | range->txpower_capa = IW_TXPOW_DBM | IW_TXPOW_RANGE; |
707 | range->txpower[0] = priv->txpower_min; | |
708 | range->txpower[1] = priv->txpower_max; | |
709 | range->num_txpower = 2; | |
876c9d3a MT |
710 | |
711 | range->event_capa[0] = (IW_EVENT_CAPA_K_0 | | |
712 | IW_EVENT_CAPA_MASK(SIOCGIWAP) | | |
713 | IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); | |
714 | range->event_capa[1] = IW_EVENT_CAPA_K_1; | |
715 | ||
aa21c004 | 716 | if (priv->fwcapinfo & FW_CAPINFO_WPA) { |
876c9d3a MT |
717 | range->enc_capa = IW_ENC_CAPA_WPA |
718 | | IW_ENC_CAPA_WPA2 | |
719 | | IW_ENC_CAPA_CIPHER_TKIP | |
720 | | IW_ENC_CAPA_CIPHER_CCMP; | |
721 | } | |
722 | ||
9012b28a | 723 | lbs_deb_leave(LBS_DEB_WEXT); |
876c9d3a MT |
724 | return 0; |
725 | } | |
726 | ||
10078321 | 727 | static int lbs_set_power(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
728 | struct iw_param *vwrq, char *extra) |
729 | { | |
ab65f649 | 730 | struct lbs_private *priv = dev->ml_priv; |
49125454 | 731 | int ret = 0; |
876c9d3a | 732 | |
9012b28a | 733 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 734 | |
e0d6133c | 735 | if (!(priv->fwcapinfo & FW_CAPINFO_PS)) { |
b2c57eee DW |
736 | if (vwrq->disabled) |
737 | return 0; | |
738 | else | |
739 | return -EINVAL; | |
740 | } | |
741 | ||
876c9d3a MT |
742 | /* PS is currently supported only in Infrastructure mode |
743 | * Remove this check if it is to be supported in IBSS mode also | |
744 | */ | |
745 | ||
746 | if (vwrq->disabled) { | |
aa21c004 DW |
747 | priv->psmode = LBS802_11POWERMODECAM; |
748 | if (priv->psstate != PS_STATE_FULL_POWER) { | |
10078321 | 749 | lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP); |
876c9d3a MT |
750 | } |
751 | ||
752 | return 0; | |
753 | } | |
754 | ||
755 | if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { | |
9012b28a HS |
756 | lbs_deb_wext( |
757 | "setting power timeout is not supported\n"); | |
876c9d3a MT |
758 | return -EINVAL; |
759 | } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { | |
49125454 AK |
760 | vwrq->value = vwrq->value / 1000; |
761 | if (!priv->enter_deep_sleep) { | |
762 | lbs_pr_err("deep sleep feature is not implemented " | |
763 | "for this interface driver\n"); | |
764 | return -EINVAL; | |
765 | } | |
766 | ||
767 | if (priv->connect_status == LBS_CONNECTED) { | |
768 | if ((priv->is_auto_deep_sleep_enabled) && | |
769 | (vwrq->value == -1000)) { | |
770 | lbs_exit_auto_deep_sleep(priv); | |
771 | return 0; | |
772 | } else { | |
773 | lbs_pr_err("can't use deep sleep cmd in " | |
774 | "connected state\n"); | |
775 | return -EINVAL; | |
776 | } | |
777 | } | |
778 | ||
779 | if ((vwrq->value < 0) && (vwrq->value != -1000)) { | |
780 | lbs_pr_err("unknown option\n"); | |
781 | return -EINVAL; | |
782 | } | |
783 | ||
784 | if (vwrq->value > 0) { | |
785 | if (!priv->is_auto_deep_sleep_enabled) { | |
786 | priv->is_activity_detected = 0; | |
787 | priv->auto_deep_sleep_timeout = vwrq->value; | |
788 | lbs_enter_auto_deep_sleep(priv); | |
789 | } else { | |
790 | priv->auto_deep_sleep_timeout = vwrq->value; | |
791 | lbs_deb_debugfs("auto deep sleep: " | |
792 | "already enabled\n"); | |
793 | } | |
794 | return 0; | |
795 | } else { | |
796 | if (priv->is_auto_deep_sleep_enabled) { | |
797 | lbs_exit_auto_deep_sleep(priv); | |
798 | /* Try to exit deep sleep if auto */ | |
799 | /*deep sleep disabled */ | |
800 | ret = lbs_set_deep_sleep(priv, 0); | |
801 | } | |
802 | if (vwrq->value == 0) | |
803 | ret = lbs_set_deep_sleep(priv, 1); | |
804 | else if (vwrq->value == -1000) | |
805 | ret = lbs_set_deep_sleep(priv, 0); | |
806 | return ret; | |
807 | } | |
876c9d3a MT |
808 | } |
809 | ||
aa21c004 | 810 | if (priv->psmode != LBS802_11POWERMODECAM) { |
876c9d3a MT |
811 | return 0; |
812 | } | |
813 | ||
aa21c004 | 814 | priv->psmode = LBS802_11POWERMODEMAX_PSP; |
876c9d3a | 815 | |
aa21c004 | 816 | if (priv->connect_status == LBS_CONNECTED) { |
10078321 | 817 | lbs_ps_sleep(priv, CMD_OPTION_WAITFORRSP); |
876c9d3a MT |
818 | } |
819 | ||
9012b28a | 820 | lbs_deb_leave(LBS_DEB_WEXT); |
49125454 | 821 | |
876c9d3a MT |
822 | return 0; |
823 | } | |
824 | ||
10078321 | 825 | static int lbs_get_power(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
826 | struct iw_param *vwrq, char *extra) |
827 | { | |
ab65f649 | 828 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a | 829 | |
9012b28a | 830 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 831 | |
876c9d3a | 832 | vwrq->value = 0; |
d4ff0ef6 HS |
833 | vwrq->flags = 0; |
834 | vwrq->disabled = priv->psmode == LBS802_11POWERMODECAM | |
835 | || priv->connect_status == LBS_DISCONNECTED; | |
876c9d3a | 836 | |
9012b28a | 837 | lbs_deb_leave(LBS_DEB_WEXT); |
876c9d3a MT |
838 | return 0; |
839 | } | |
840 | ||
10078321 | 841 | static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev) |
876c9d3a MT |
842 | { |
843 | enum { | |
844 | POOR = 30, | |
845 | FAIR = 60, | |
846 | GOOD = 80, | |
847 | VERY_GOOD = 90, | |
848 | EXCELLENT = 95, | |
849 | PERFECT = 100 | |
850 | }; | |
ab65f649 | 851 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a MT |
852 | u32 rssi_qual; |
853 | u32 tx_qual; | |
854 | u32 quality = 0; | |
c0bbd576 | 855 | int ret, stats_valid = 0; |
876c9d3a MT |
856 | u8 rssi; |
857 | u32 tx_retries; | |
c49c3b77 | 858 | struct cmd_ds_802_11_get_log log; |
876c9d3a | 859 | |
9012b28a | 860 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 861 | |
aa21c004 | 862 | priv->wstats.status = priv->mode; |
876c9d3a MT |
863 | |
864 | /* If we're not associated, all quality values are meaningless */ | |
aa21c004 DW |
865 | if ((priv->connect_status != LBS_CONNECTED) && |
866 | (priv->mesh_connect_status != LBS_CONNECTED)) | |
876c9d3a MT |
867 | goto out; |
868 | ||
869 | /* Quality by RSSI */ | |
870 | priv->wstats.qual.level = | |
aa21c004 DW |
871 | CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG], |
872 | priv->NF[TYPE_BEACON][TYPE_NOAVG]); | |
876c9d3a | 873 | |
aa21c004 | 874 | if (priv->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { |
876c9d3a MT |
875 | priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; |
876 | } else { | |
877 | priv->wstats.qual.noise = | |
aa21c004 | 878 | CAL_NF(priv->NF[TYPE_BEACON][TYPE_NOAVG]); |
876c9d3a MT |
879 | } |
880 | ||
9012b28a HS |
881 | lbs_deb_wext("signal level %#x\n", priv->wstats.qual.level); |
882 | lbs_deb_wext("noise %#x\n", priv->wstats.qual.noise); | |
876c9d3a MT |
883 | |
884 | rssi = priv->wstats.qual.level - priv->wstats.qual.noise; | |
885 | if (rssi < 15) | |
886 | rssi_qual = rssi * POOR / 10; | |
887 | else if (rssi < 20) | |
888 | rssi_qual = (rssi - 15) * (FAIR - POOR) / 5 + POOR; | |
889 | else if (rssi < 30) | |
890 | rssi_qual = (rssi - 20) * (GOOD - FAIR) / 5 + FAIR; | |
891 | else if (rssi < 40) | |
892 | rssi_qual = (rssi - 30) * (VERY_GOOD - GOOD) / | |
893 | 10 + GOOD; | |
894 | else | |
895 | rssi_qual = (rssi - 40) * (PERFECT - VERY_GOOD) / | |
896 | 10 + VERY_GOOD; | |
897 | quality = rssi_qual; | |
898 | ||
899 | /* Quality by TX errors */ | |
bbfc6b78 | 900 | priv->wstats.discard.retries = dev->stats.tx_errors; |
876c9d3a | 901 | |
c49c3b77 HS |
902 | memset(&log, 0, sizeof(log)); |
903 | log.hdr.size = cpu_to_le16(sizeof(log)); | |
c0bbd576 AK |
904 | ret = lbs_cmd_with_response(priv, CMD_802_11_GET_LOG, &log); |
905 | if (ret) | |
906 | goto out; | |
c49c3b77 HS |
907 | |
908 | tx_retries = le32_to_cpu(log.retry); | |
876c9d3a MT |
909 | |
910 | if (tx_retries > 75) | |
911 | tx_qual = (90 - tx_retries) * POOR / 15; | |
912 | else if (tx_retries > 70) | |
913 | tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR; | |
914 | else if (tx_retries > 65) | |
915 | tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR; | |
916 | else if (tx_retries > 50) | |
917 | tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) / | |
918 | 15 + GOOD; | |
919 | else | |
920 | tx_qual = (50 - tx_retries) * | |
921 | (PERFECT - VERY_GOOD) / 50 + VERY_GOOD; | |
922 | quality = min(quality, tx_qual); | |
923 | ||
c49c3b77 | 924 | priv->wstats.discard.code = le32_to_cpu(log.wepundecryptable); |
876c9d3a | 925 | priv->wstats.discard.retries = tx_retries; |
c49c3b77 | 926 | priv->wstats.discard.misc = le32_to_cpu(log.ackfailure); |
876c9d3a MT |
927 | |
928 | /* Calculate quality */ | |
cad9d9b1 | 929 | priv->wstats.qual.qual = min_t(u8, quality, 100); |
876c9d3a MT |
930 | priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; |
931 | stats_valid = 1; | |
932 | ||
933 | /* update stats asynchronously for future calls */ | |
c0bbd576 | 934 | ret = lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0, |
876c9d3a | 935 | 0, 0, NULL); |
c0bbd576 AK |
936 | if (ret) |
937 | lbs_pr_err("RSSI command failed\n"); | |
876c9d3a MT |
938 | out: |
939 | if (!stats_valid) { | |
940 | priv->wstats.miss.beacon = 0; | |
941 | priv->wstats.discard.retries = 0; | |
942 | priv->wstats.qual.qual = 0; | |
943 | priv->wstats.qual.level = 0; | |
944 | priv->wstats.qual.noise = 0; | |
945 | priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED; | |
946 | priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID | | |
947 | IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; | |
948 | } | |
949 | ||
9012b28a | 950 | lbs_deb_leave(LBS_DEB_WEXT); |
876c9d3a MT |
951 | return &priv->wstats; |
952 | ||
953 | ||
954 | } | |
955 | ||
10078321 | 956 | static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
957 | struct iw_freq *fwrq, char *extra) |
958 | { | |
ef9a264b | 959 | int ret = -EINVAL; |
ab65f649 | 960 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a | 961 | struct chan_freq_power *cfp; |
ef9a264b | 962 | struct assoc_request * assoc_req; |
876c9d3a | 963 | |
9012b28a | 964 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 965 | |
aa21c004 DW |
966 | mutex_lock(&priv->lock); |
967 | assoc_req = lbs_get_association_request(priv); | |
ef9a264b DW |
968 | if (!assoc_req) { |
969 | ret = -ENOMEM; | |
970 | goto out; | |
971 | } | |
876c9d3a | 972 | |
ef9a264b DW |
973 | /* If setting by frequency, convert to a channel */ |
974 | if (fwrq->e == 1) { | |
876c9d3a | 975 | long f = fwrq->m / 100000; |
876c9d3a | 976 | |
aa21c004 | 977 | cfp = find_cfp_by_band_and_freq(priv, 0, f); |
876c9d3a | 978 | if (!cfp) { |
9012b28a | 979 | lbs_deb_wext("invalid freq %ld\n", f); |
ef9a264b | 980 | goto out; |
876c9d3a MT |
981 | } |
982 | ||
876c9d3a | 983 | fwrq->e = 0; |
ef9a264b | 984 | fwrq->m = (int) cfp->channel; |
876c9d3a MT |
985 | } |
986 | ||
ef9a264b | 987 | /* Setting by channel number */ |
876c9d3a | 988 | if (fwrq->m > 1000 || fwrq->e > 0) { |
ef9a264b DW |
989 | goto out; |
990 | } | |
876c9d3a | 991 | |
aa21c004 | 992 | cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m); |
ef9a264b DW |
993 | if (!cfp) { |
994 | goto out; | |
876c9d3a MT |
995 | } |
996 | ||
ef9a264b DW |
997 | assoc_req->channel = fwrq->m; |
998 | ret = 0; | |
999 | ||
9012b28a | 1000 | out: |
ef9a264b DW |
1001 | if (ret == 0) { |
1002 | set_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags); | |
10078321 | 1003 | lbs_postpone_association_work(priv); |
ef9a264b | 1004 | } else { |
10078321 | 1005 | lbs_cancel_association_work(priv); |
ef9a264b | 1006 | } |
aa21c004 | 1007 | mutex_unlock(&priv->lock); |
ef9a264b DW |
1008 | |
1009 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); | |
1010 | return ret; | |
876c9d3a MT |
1011 | } |
1012 | ||
823eaa2c DW |
1013 | static int lbs_mesh_set_freq(struct net_device *dev, |
1014 | struct iw_request_info *info, | |
1015 | struct iw_freq *fwrq, char *extra) | |
1016 | { | |
ab65f649 | 1017 | struct lbs_private *priv = dev->ml_priv; |
823eaa2c DW |
1018 | struct chan_freq_power *cfp; |
1019 | int ret = -EINVAL; | |
1020 | ||
1021 | lbs_deb_enter(LBS_DEB_WEXT); | |
1022 | ||
1023 | /* If setting by frequency, convert to a channel */ | |
1024 | if (fwrq->e == 1) { | |
1025 | long f = fwrq->m / 100000; | |
1026 | ||
1027 | cfp = find_cfp_by_band_and_freq(priv, 0, f); | |
1028 | if (!cfp) { | |
1029 | lbs_deb_wext("invalid freq %ld\n", f); | |
1030 | goto out; | |
1031 | } | |
1032 | ||
1033 | fwrq->e = 0; | |
1034 | fwrq->m = (int) cfp->channel; | |
1035 | } | |
1036 | ||
1037 | /* Setting by channel number */ | |
1038 | if (fwrq->m > 1000 || fwrq->e > 0) { | |
1039 | goto out; | |
1040 | } | |
1041 | ||
1042 | cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m); | |
1043 | if (!cfp) { | |
1044 | goto out; | |
1045 | } | |
1046 | ||
c14951fe | 1047 | if (fwrq->m != priv->channel) { |
823eaa2c DW |
1048 | lbs_deb_wext("mesh channel change forces eth disconnect\n"); |
1049 | if (priv->mode == IW_MODE_INFRA) | |
191bb40e DW |
1050 | lbs_cmd_80211_deauthenticate(priv, |
1051 | priv->curbssparams.bssid, | |
1052 | WLAN_REASON_DEAUTH_LEAVING); | |
823eaa2c | 1053 | else if (priv->mode == IW_MODE_ADHOC) |
f5fe1fda | 1054 | lbs_adhoc_stop(priv); |
823eaa2c | 1055 | } |
edaea5ce | 1056 | lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, fwrq->m); |
86062134 | 1057 | lbs_update_channel(priv); |
823eaa2c DW |
1058 | ret = 0; |
1059 | ||
1060 | out: | |
1061 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); | |
1062 | return ret; | |
1063 | } | |
1064 | ||
10078321 | 1065 | static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
1066 | struct iw_param *vwrq, char *extra) |
1067 | { | |
ab65f649 | 1068 | struct lbs_private *priv = dev->ml_priv; |
8e3c91bb | 1069 | u8 new_rate = 0; |
8c512765 DW |
1070 | int ret = -EINVAL; |
1071 | u8 rates[MAX_RATES + 1]; | |
876c9d3a | 1072 | |
9012b28a | 1073 | lbs_deb_enter(LBS_DEB_WEXT); |
49125454 | 1074 | |
9012b28a | 1075 | lbs_deb_wext("vwrq->value %d\n", vwrq->value); |
85319f93 JC |
1076 | lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed); |
1077 | ||
1078 | if (vwrq->fixed && vwrq->value == -1) | |
1079 | goto out; | |
876c9d3a | 1080 | |
8c512765 | 1081 | /* Auto rate? */ |
85319f93 JC |
1082 | priv->enablehwauto = !vwrq->fixed; |
1083 | ||
1084 | if (vwrq->value == -1) | |
aa21c004 | 1085 | priv->cur_rate = 0; |
85319f93 | 1086 | else { |
8c512765 DW |
1087 | if (vwrq->value % 100000) |
1088 | goto out; | |
876c9d3a | 1089 | |
85319f93 JC |
1090 | new_rate = vwrq->value / 500000; |
1091 | priv->cur_rate = new_rate; | |
1092 | /* the rest is only needed for lbs_set_data_rate() */ | |
876c9d3a | 1093 | memset(rates, 0, sizeof(rates)); |
aa21c004 | 1094 | copy_active_data_rates(priv, rates); |
8c512765 DW |
1095 | if (!memchr(rates, new_rate, sizeof(rates))) { |
1096 | lbs_pr_alert("fixed data rate 0x%X out of range\n", | |
1097 | new_rate); | |
1098 | goto out; | |
876c9d3a | 1099 | } |
3ed6e080 AN |
1100 | if (priv->fwrelease < 0x09000000) { |
1101 | ret = lbs_set_power_adapt_cfg(priv, 0, | |
1102 | POW_ADAPT_DEFAULT_P0, | |
1103 | POW_ADAPT_DEFAULT_P1, | |
1104 | POW_ADAPT_DEFAULT_P2); | |
1105 | if (ret) | |
1106 | goto out; | |
1107 | } | |
1108 | ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1, | |
1109 | TPC_DEFAULT_P2, 1); | |
1110 | if (ret) | |
1111 | goto out; | |
876c9d3a MT |
1112 | } |
1113 | ||
85319f93 JC |
1114 | /* Try the newer command first (Firmware Spec 5.1 and above) */ |
1115 | ret = lbs_cmd_802_11_rate_adapt_rateset(priv, CMD_ACT_SET); | |
1116 | ||
1117 | /* Fallback to older version */ | |
1118 | if (ret) | |
1119 | ret = lbs_set_data_rate(priv, new_rate); | |
876c9d3a | 1120 | |
8c512765 | 1121 | out: |
9012b28a | 1122 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); |
876c9d3a MT |
1123 | return ret; |
1124 | } | |
1125 | ||
10078321 | 1126 | static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
1127 | struct iw_param *vwrq, char *extra) |
1128 | { | |
ab65f649 | 1129 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a | 1130 | |
9012b28a | 1131 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 1132 | |
aa21c004 DW |
1133 | if (priv->connect_status == LBS_CONNECTED) { |
1134 | vwrq->value = priv->cur_rate * 500000; | |
8c512765 | 1135 | |
85319f93 | 1136 | if (priv->enablehwauto) |
8c512765 DW |
1137 | vwrq->fixed = 0; |
1138 | else | |
1139 | vwrq->fixed = 1; | |
1140 | ||
876c9d3a | 1141 | } else { |
8c512765 DW |
1142 | vwrq->fixed = 0; |
1143 | vwrq->value = 0; | |
876c9d3a MT |
1144 | } |
1145 | ||
9012b28a | 1146 | lbs_deb_leave(LBS_DEB_WEXT); |
876c9d3a MT |
1147 | return 0; |
1148 | } | |
1149 | ||
10078321 | 1150 | static int lbs_set_mode(struct net_device *dev, |
876c9d3a MT |
1151 | struct iw_request_info *info, u32 * uwrq, char *extra) |
1152 | { | |
1153 | int ret = 0; | |
ab65f649 | 1154 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a | 1155 | struct assoc_request * assoc_req; |
876c9d3a | 1156 | |
9012b28a | 1157 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 1158 | |
0dc5a290 DW |
1159 | if ( (*uwrq != IW_MODE_ADHOC) |
1160 | && (*uwrq != IW_MODE_INFRA) | |
1161 | && (*uwrq != IW_MODE_AUTO)) { | |
9012b28a | 1162 | lbs_deb_wext("Invalid mode: 0x%x\n", *uwrq); |
0dc5a290 DW |
1163 | ret = -EINVAL; |
1164 | goto out; | |
876c9d3a MT |
1165 | } |
1166 | ||
aa21c004 DW |
1167 | mutex_lock(&priv->lock); |
1168 | assoc_req = lbs_get_association_request(priv); | |
876c9d3a MT |
1169 | if (!assoc_req) { |
1170 | ret = -ENOMEM; | |
10078321 | 1171 | lbs_cancel_association_work(priv); |
876c9d3a | 1172 | } else { |
0dc5a290 | 1173 | assoc_req->mode = *uwrq; |
876c9d3a | 1174 | set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); |
10078321 | 1175 | lbs_postpone_association_work(priv); |
9012b28a | 1176 | lbs_deb_wext("Switching to mode: 0x%x\n", *uwrq); |
876c9d3a | 1177 | } |
aa21c004 | 1178 | mutex_unlock(&priv->lock); |
876c9d3a | 1179 | |
0dc5a290 | 1180 | out: |
9012b28a | 1181 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); |
876c9d3a MT |
1182 | return ret; |
1183 | } | |
1184 | ||
1185 | ||
1186 | /** | |
1187 | * @brief Get Encryption key | |
1188 | * | |
1189 | * @param dev A pointer to net_device structure | |
1190 | * @param info A pointer to iw_request_info structure | |
1191 | * @param vwrq A pointer to iw_param structure | |
1192 | * @param extra A pointer to extra data buf | |
1193 | * @return 0 --success, otherwise fail | |
1194 | */ | |
10078321 | 1195 | static int lbs_get_encode(struct net_device *dev, |
876c9d3a MT |
1196 | struct iw_request_info *info, |
1197 | struct iw_point *dwrq, u8 * extra) | |
1198 | { | |
ab65f649 | 1199 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a MT |
1200 | int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; |
1201 | ||
9012b28a | 1202 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 1203 | |
9012b28a | 1204 | lbs_deb_wext("flags 0x%x, index %d, length %d, wep_tx_keyidx %d\n", |
aa21c004 | 1205 | dwrq->flags, index, dwrq->length, priv->wep_tx_keyidx); |
876c9d3a MT |
1206 | |
1207 | dwrq->flags = 0; | |
1208 | ||
1209 | /* Authentication method */ | |
aa21c004 | 1210 | switch (priv->secinfo.auth_mode) { |
6affe785 | 1211 | case IW_AUTH_ALG_OPEN_SYSTEM: |
876c9d3a MT |
1212 | dwrq->flags = IW_ENCODE_OPEN; |
1213 | break; | |
1214 | ||
6affe785 DW |
1215 | case IW_AUTH_ALG_SHARED_KEY: |
1216 | case IW_AUTH_ALG_LEAP: | |
876c9d3a MT |
1217 | dwrq->flags = IW_ENCODE_RESTRICTED; |
1218 | break; | |
1219 | default: | |
1220 | dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN; | |
1221 | break; | |
1222 | } | |
1223 | ||
876c9d3a MT |
1224 | memset(extra, 0, 16); |
1225 | ||
aa21c004 | 1226 | mutex_lock(&priv->lock); |
876c9d3a MT |
1227 | |
1228 | /* Default to returning current transmit key */ | |
1229 | if (index < 0) | |
aa21c004 | 1230 | index = priv->wep_tx_keyidx; |
876c9d3a | 1231 | |
aa21c004 DW |
1232 | if ((priv->wep_keys[index].len) && priv->secinfo.wep_enabled) { |
1233 | memcpy(extra, priv->wep_keys[index].key, | |
1234 | priv->wep_keys[index].len); | |
1235 | dwrq->length = priv->wep_keys[index].len; | |
876c9d3a MT |
1236 | |
1237 | dwrq->flags |= (index + 1); | |
1238 | /* Return WEP enabled */ | |
1239 | dwrq->flags &= ~IW_ENCODE_DISABLED; | |
aa21c004 DW |
1240 | } else if ((priv->secinfo.WPAenabled) |
1241 | || (priv->secinfo.WPA2enabled)) { | |
876c9d3a MT |
1242 | /* return WPA enabled */ |
1243 | dwrq->flags &= ~IW_ENCODE_DISABLED; | |
c12bdc45 | 1244 | dwrq->flags |= IW_ENCODE_NOKEY; |
876c9d3a MT |
1245 | } else { |
1246 | dwrq->flags |= IW_ENCODE_DISABLED; | |
1247 | } | |
1248 | ||
aa21c004 | 1249 | mutex_unlock(&priv->lock); |
876c9d3a | 1250 | |
0795af57 | 1251 | lbs_deb_wext("key: %02x:%02x:%02x:%02x:%02x:%02x, keylen %d\n", |
876c9d3a MT |
1252 | extra[0], extra[1], extra[2], |
1253 | extra[3], extra[4], extra[5], dwrq->length); | |
1254 | ||
9012b28a | 1255 | lbs_deb_wext("return flags 0x%x\n", dwrq->flags); |
876c9d3a | 1256 | |
9012b28a | 1257 | lbs_deb_leave(LBS_DEB_WEXT); |
876c9d3a MT |
1258 | return 0; |
1259 | } | |
1260 | ||
1261 | /** | |
1262 | * @brief Set Encryption key (internal) | |
1263 | * | |
1264 | * @param priv A pointer to private card structure | |
1265 | * @param key_material A pointer to key material | |
1266 | * @param key_length length of key material | |
1267 | * @param index key index to set | |
1268 | * @param set_tx_key Force set TX key (1 = yes, 0 = no) | |
1269 | * @return 0 --success, otherwise fail | |
1270 | */ | |
10078321 | 1271 | static int lbs_set_wep_key(struct assoc_request *assoc_req, |
876c9d3a MT |
1272 | const char *key_material, |
1273 | u16 key_length, | |
1274 | u16 index, | |
1275 | int set_tx_key) | |
1276 | { | |
9012b28a | 1277 | int ret = 0; |
1443b653 | 1278 | struct enc_key *pkey; |
876c9d3a | 1279 | |
9012b28a | 1280 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a MT |
1281 | |
1282 | /* Paranoid validation of key index */ | |
1283 | if (index > 3) { | |
9012b28a HS |
1284 | ret = -EINVAL; |
1285 | goto out; | |
876c9d3a MT |
1286 | } |
1287 | ||
1288 | /* validate max key length */ | |
1289 | if (key_length > KEY_LEN_WEP_104) { | |
9012b28a HS |
1290 | ret = -EINVAL; |
1291 | goto out; | |
876c9d3a MT |
1292 | } |
1293 | ||
1294 | pkey = &assoc_req->wep_keys[index]; | |
1295 | ||
1296 | if (key_length > 0) { | |
1443b653 | 1297 | memset(pkey, 0, sizeof(struct enc_key)); |
876c9d3a MT |
1298 | pkey->type = KEY_TYPE_ID_WEP; |
1299 | ||
1300 | /* Standardize the key length */ | |
1301 | pkey->len = (key_length > KEY_LEN_WEP_40) ? | |
1302 | KEY_LEN_WEP_104 : KEY_LEN_WEP_40; | |
1303 | memcpy(pkey->key, key_material, key_length); | |
1304 | } | |
1305 | ||
1306 | if (set_tx_key) { | |
1307 | /* Ensure the chosen key is valid */ | |
1308 | if (!pkey->len) { | |
9012b28a HS |
1309 | lbs_deb_wext("key not set, so cannot enable it\n"); |
1310 | ret = -EINVAL; | |
1311 | goto out; | |
876c9d3a MT |
1312 | } |
1313 | assoc_req->wep_tx_keyidx = index; | |
1314 | } | |
1315 | ||
889c05bd | 1316 | assoc_req->secinfo.wep_enabled = 1; |
876c9d3a | 1317 | |
9012b28a HS |
1318 | out: |
1319 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); | |
1320 | return ret; | |
876c9d3a MT |
1321 | } |
1322 | ||
1323 | static int validate_key_index(u16 def_index, u16 raw_index, | |
1324 | u16 *out_index, u16 *is_default) | |
1325 | { | |
1326 | if (!out_index || !is_default) | |
1327 | return -EINVAL; | |
1328 | ||
1329 | /* Verify index if present, otherwise use default TX key index */ | |
1330 | if (raw_index > 0) { | |
1331 | if (raw_index > 4) | |
1332 | return -EINVAL; | |
1333 | *out_index = raw_index - 1; | |
1334 | } else { | |
1335 | *out_index = def_index; | |
1336 | *is_default = 1; | |
1337 | } | |
1338 | return 0; | |
1339 | } | |
1340 | ||
1341 | static void disable_wep(struct assoc_request *assoc_req) | |
1342 | { | |
1343 | int i; | |
1344 | ||
90a42210 DW |
1345 | lbs_deb_enter(LBS_DEB_WEXT); |
1346 | ||
876c9d3a | 1347 | /* Set Open System auth mode */ |
6affe785 | 1348 | assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; |
876c9d3a MT |
1349 | |
1350 | /* Clear WEP keys and mark WEP as disabled */ | |
889c05bd | 1351 | assoc_req->secinfo.wep_enabled = 0; |
876c9d3a MT |
1352 | for (i = 0; i < 4; i++) |
1353 | assoc_req->wep_keys[i].len = 0; | |
1354 | ||
1355 | set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); | |
1356 | set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); | |
90a42210 DW |
1357 | |
1358 | lbs_deb_leave(LBS_DEB_WEXT); | |
1359 | } | |
1360 | ||
1361 | static void disable_wpa(struct assoc_request *assoc_req) | |
1362 | { | |
1363 | lbs_deb_enter(LBS_DEB_WEXT); | |
1364 | ||
1443b653 | 1365 | memset(&assoc_req->wpa_mcast_key, 0, sizeof (struct enc_key)); |
90a42210 DW |
1366 | assoc_req->wpa_mcast_key.flags = KEY_INFO_WPA_MCAST; |
1367 | set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags); | |
1368 | ||
1443b653 | 1369 | memset(&assoc_req->wpa_unicast_key, 0, sizeof (struct enc_key)); |
90a42210 DW |
1370 | assoc_req->wpa_unicast_key.flags = KEY_INFO_WPA_UNICAST; |
1371 | set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags); | |
1372 | ||
1373 | assoc_req->secinfo.WPAenabled = 0; | |
1374 | assoc_req->secinfo.WPA2enabled = 0; | |
1375 | set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); | |
1376 | ||
1377 | lbs_deb_leave(LBS_DEB_WEXT); | |
876c9d3a MT |
1378 | } |
1379 | ||
1380 | /** | |
1381 | * @brief Set Encryption key | |
1382 | * | |
1383 | * @param dev A pointer to net_device structure | |
1384 | * @param info A pointer to iw_request_info structure | |
1385 | * @param vwrq A pointer to iw_param structure | |
1386 | * @param extra A pointer to extra data buf | |
1387 | * @return 0 --success, otherwise fail | |
1388 | */ | |
10078321 | 1389 | static int lbs_set_encode(struct net_device *dev, |
876c9d3a MT |
1390 | struct iw_request_info *info, |
1391 | struct iw_point *dwrq, char *extra) | |
1392 | { | |
1393 | int ret = 0; | |
ab65f649 | 1394 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a MT |
1395 | struct assoc_request * assoc_req; |
1396 | u16 is_default = 0, index = 0, set_tx_key = 0; | |
1397 | ||
9012b28a | 1398 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 1399 | |
aa21c004 DW |
1400 | mutex_lock(&priv->lock); |
1401 | assoc_req = lbs_get_association_request(priv); | |
876c9d3a MT |
1402 | if (!assoc_req) { |
1403 | ret = -ENOMEM; | |
1404 | goto out; | |
1405 | } | |
1406 | ||
1407 | if (dwrq->flags & IW_ENCODE_DISABLED) { | |
1408 | disable_wep (assoc_req); | |
90a42210 | 1409 | disable_wpa (assoc_req); |
876c9d3a MT |
1410 | goto out; |
1411 | } | |
1412 | ||
1413 | ret = validate_key_index(assoc_req->wep_tx_keyidx, | |
1414 | (dwrq->flags & IW_ENCODE_INDEX), | |
1415 | &index, &is_default); | |
1416 | if (ret) { | |
1417 | ret = -EINVAL; | |
1418 | goto out; | |
1419 | } | |
1420 | ||
1421 | /* If WEP isn't enabled, or if there is no key data but a valid | |
1422 | * index, set the TX key. | |
1423 | */ | |
889c05bd | 1424 | if (!assoc_req->secinfo.wep_enabled || (dwrq->length == 0 && !is_default)) |
876c9d3a MT |
1425 | set_tx_key = 1; |
1426 | ||
10078321 | 1427 | ret = lbs_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key); |
876c9d3a MT |
1428 | if (ret) |
1429 | goto out; | |
1430 | ||
1431 | if (dwrq->length) | |
1432 | set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); | |
1433 | if (set_tx_key) | |
1434 | set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); | |
1435 | ||
1436 | if (dwrq->flags & IW_ENCODE_RESTRICTED) { | |
6affe785 | 1437 | assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; |
876c9d3a | 1438 | } else if (dwrq->flags & IW_ENCODE_OPEN) { |
6affe785 | 1439 | assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; |
876c9d3a MT |
1440 | } |
1441 | ||
1442 | out: | |
1443 | if (ret == 0) { | |
1444 | set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); | |
10078321 | 1445 | lbs_postpone_association_work(priv); |
876c9d3a | 1446 | } else { |
10078321 | 1447 | lbs_cancel_association_work(priv); |
876c9d3a | 1448 | } |
aa21c004 | 1449 | mutex_unlock(&priv->lock); |
876c9d3a | 1450 | |
9012b28a | 1451 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); |
876c9d3a MT |
1452 | return ret; |
1453 | } | |
1454 | ||
1455 | /** | |
1456 | * @brief Get Extended Encryption key (WPA/802.1x and WEP) | |
1457 | * | |
1458 | * @param dev A pointer to net_device structure | |
1459 | * @param info A pointer to iw_request_info structure | |
1460 | * @param vwrq A pointer to iw_param structure | |
1461 | * @param extra A pointer to extra data buf | |
1462 | * @return 0 on success, otherwise failure | |
1463 | */ | |
10078321 | 1464 | static int lbs_get_encodeext(struct net_device *dev, |
876c9d3a MT |
1465 | struct iw_request_info *info, |
1466 | struct iw_point *dwrq, | |
1467 | char *extra) | |
1468 | { | |
1469 | int ret = -EINVAL; | |
ab65f649 | 1470 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a MT |
1471 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; |
1472 | int index, max_key_len; | |
1473 | ||
9012b28a | 1474 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a MT |
1475 | |
1476 | max_key_len = dwrq->length - sizeof(*ext); | |
1477 | if (max_key_len < 0) | |
1478 | goto out; | |
1479 | ||
1480 | index = dwrq->flags & IW_ENCODE_INDEX; | |
1481 | if (index) { | |
1482 | if (index < 1 || index > 4) | |
1483 | goto out; | |
1484 | index--; | |
1485 | } else { | |
aa21c004 | 1486 | index = priv->wep_tx_keyidx; |
876c9d3a MT |
1487 | } |
1488 | ||
f59d9782 | 1489 | if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) && |
876c9d3a | 1490 | ext->alg != IW_ENCODE_ALG_WEP) { |
aa21c004 | 1491 | if (index != 0 || priv->mode != IW_MODE_INFRA) |
876c9d3a MT |
1492 | goto out; |
1493 | } | |
1494 | ||
1495 | dwrq->flags = index + 1; | |
1496 | memset(ext, 0, sizeof(*ext)); | |
1497 | ||
aa21c004 DW |
1498 | if ( !priv->secinfo.wep_enabled |
1499 | && !priv->secinfo.WPAenabled | |
1500 | && !priv->secinfo.WPA2enabled) { | |
876c9d3a MT |
1501 | ext->alg = IW_ENCODE_ALG_NONE; |
1502 | ext->key_len = 0; | |
1503 | dwrq->flags |= IW_ENCODE_DISABLED; | |
1504 | } else { | |
1505 | u8 *key = NULL; | |
1506 | ||
aa21c004 DW |
1507 | if ( priv->secinfo.wep_enabled |
1508 | && !priv->secinfo.WPAenabled | |
1509 | && !priv->secinfo.WPA2enabled) { | |
90a42210 | 1510 | /* WEP */ |
876c9d3a | 1511 | ext->alg = IW_ENCODE_ALG_WEP; |
aa21c004 DW |
1512 | ext->key_len = priv->wep_keys[index].len; |
1513 | key = &priv->wep_keys[index].key[0]; | |
1514 | } else if ( !priv->secinfo.wep_enabled | |
1515 | && (priv->secinfo.WPAenabled || | |
1516 | priv->secinfo.WPA2enabled)) { | |
876c9d3a | 1517 | /* WPA */ |
1443b653 | 1518 | struct enc_key * pkey = NULL; |
90a42210 | 1519 | |
aa21c004 DW |
1520 | if ( priv->wpa_mcast_key.len |
1521 | && (priv->wpa_mcast_key.flags & KEY_INFO_WPA_ENABLED)) | |
1522 | pkey = &priv->wpa_mcast_key; | |
1523 | else if ( priv->wpa_unicast_key.len | |
1524 | && (priv->wpa_unicast_key.flags & KEY_INFO_WPA_ENABLED)) | |
1525 | pkey = &priv->wpa_unicast_key; | |
90a42210 DW |
1526 | |
1527 | if (pkey) { | |
1528 | if (pkey->type == KEY_TYPE_ID_AES) { | |
1529 | ext->alg = IW_ENCODE_ALG_CCMP; | |
1530 | } else { | |
1531 | ext->alg = IW_ENCODE_ALG_TKIP; | |
1532 | } | |
1533 | ext->key_len = pkey->len; | |
1534 | key = &pkey->key[0]; | |
1535 | } else { | |
1536 | ext->alg = IW_ENCODE_ALG_TKIP; | |
1537 | ext->key_len = 0; | |
1538 | } | |
876c9d3a MT |
1539 | } else { |
1540 | goto out; | |
1541 | } | |
1542 | ||
1543 | if (ext->key_len > max_key_len) { | |
1544 | ret = -E2BIG; | |
1545 | goto out; | |
1546 | } | |
1547 | ||
1548 | if (ext->key_len) | |
1549 | memcpy(ext->key, key, ext->key_len); | |
1550 | else | |
1551 | dwrq->flags |= IW_ENCODE_NOKEY; | |
1552 | dwrq->flags |= IW_ENCODE_ENABLED; | |
1553 | } | |
1554 | ret = 0; | |
1555 | ||
1556 | out: | |
9012b28a | 1557 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); |
876c9d3a MT |
1558 | return ret; |
1559 | } | |
1560 | ||
1561 | /** | |
1562 | * @brief Set Encryption key Extended (WPA/802.1x and WEP) | |
1563 | * | |
1564 | * @param dev A pointer to net_device structure | |
1565 | * @param info A pointer to iw_request_info structure | |
1566 | * @param vwrq A pointer to iw_param structure | |
1567 | * @param extra A pointer to extra data buf | |
1568 | * @return 0 --success, otherwise fail | |
1569 | */ | |
10078321 | 1570 | static int lbs_set_encodeext(struct net_device *dev, |
876c9d3a MT |
1571 | struct iw_request_info *info, |
1572 | struct iw_point *dwrq, | |
1573 | char *extra) | |
1574 | { | |
1575 | int ret = 0; | |
ab65f649 | 1576 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a MT |
1577 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; |
1578 | int alg = ext->alg; | |
1579 | struct assoc_request * assoc_req; | |
1580 | ||
9012b28a | 1581 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 1582 | |
aa21c004 DW |
1583 | mutex_lock(&priv->lock); |
1584 | assoc_req = lbs_get_association_request(priv); | |
876c9d3a MT |
1585 | if (!assoc_req) { |
1586 | ret = -ENOMEM; | |
1587 | goto out; | |
1588 | } | |
1589 | ||
1590 | if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) { | |
1591 | disable_wep (assoc_req); | |
90a42210 | 1592 | disable_wpa (assoc_req); |
876c9d3a MT |
1593 | } else if (alg == IW_ENCODE_ALG_WEP) { |
1594 | u16 is_default = 0, index, set_tx_key = 0; | |
1595 | ||
1596 | ret = validate_key_index(assoc_req->wep_tx_keyidx, | |
1597 | (dwrq->flags & IW_ENCODE_INDEX), | |
1598 | &index, &is_default); | |
1599 | if (ret) | |
1600 | goto out; | |
1601 | ||
1602 | /* If WEP isn't enabled, or if there is no key data but a valid | |
1603 | * index, or if the set-TX-key flag was passed, set the TX key. | |
1604 | */ | |
889c05bd | 1605 | if ( !assoc_req->secinfo.wep_enabled |
876c9d3a MT |
1606 | || (dwrq->length == 0 && !is_default) |
1607 | || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) | |
1608 | set_tx_key = 1; | |
1609 | ||
1610 | /* Copy key to driver */ | |
10078321 | 1611 | ret = lbs_set_wep_key(assoc_req, ext->key, ext->key_len, index, |
876c9d3a MT |
1612 | set_tx_key); |
1613 | if (ret) | |
1614 | goto out; | |
1615 | ||
1616 | if (dwrq->flags & IW_ENCODE_RESTRICTED) { | |
6affe785 | 1617 | assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; |
876c9d3a | 1618 | } else if (dwrq->flags & IW_ENCODE_OPEN) { |
6affe785 | 1619 | assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; |
876c9d3a MT |
1620 | } |
1621 | ||
1622 | /* Mark the various WEP bits as modified */ | |
1623 | set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); | |
1624 | if (dwrq->length) | |
1625 | set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); | |
1626 | if (set_tx_key) | |
1627 | set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); | |
876c9d3a | 1628 | } else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) { |
1443b653 | 1629 | struct enc_key * pkey; |
876c9d3a MT |
1630 | |
1631 | /* validate key length */ | |
1632 | if (((alg == IW_ENCODE_ALG_TKIP) | |
1633 | && (ext->key_len != KEY_LEN_WPA_TKIP)) | |
1634 | || ((alg == IW_ENCODE_ALG_CCMP) | |
1635 | && (ext->key_len != KEY_LEN_WPA_AES))) { | |
8376e7a3 | 1636 | lbs_deb_wext("invalid size %d for key of alg " |
9012b28a | 1637 | "type %d\n", |
876c9d3a MT |
1638 | ext->key_len, |
1639 | alg); | |
1640 | ret = -EINVAL; | |
1641 | goto out; | |
1642 | } | |
1643 | ||
90a42210 | 1644 | if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { |
876c9d3a | 1645 | pkey = &assoc_req->wpa_mcast_key; |
90a42210 DW |
1646 | set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags); |
1647 | } else { | |
876c9d3a | 1648 | pkey = &assoc_req->wpa_unicast_key; |
90a42210 DW |
1649 | set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags); |
1650 | } | |
876c9d3a | 1651 | |
1443b653 | 1652 | memset(pkey, 0, sizeof (struct enc_key)); |
876c9d3a MT |
1653 | memcpy(pkey->key, ext->key, ext->key_len); |
1654 | pkey->len = ext->key_len; | |
90a42210 DW |
1655 | if (pkey->len) |
1656 | pkey->flags |= KEY_INFO_WPA_ENABLED; | |
876c9d3a | 1657 | |
90a42210 | 1658 | /* Do this after zeroing key structure */ |
876c9d3a MT |
1659 | if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { |
1660 | pkey->flags |= KEY_INFO_WPA_MCAST; | |
876c9d3a MT |
1661 | } else { |
1662 | pkey->flags |= KEY_INFO_WPA_UNICAST; | |
876c9d3a MT |
1663 | } |
1664 | ||
90a42210 | 1665 | if (alg == IW_ENCODE_ALG_TKIP) { |
876c9d3a | 1666 | pkey->type = KEY_TYPE_ID_TKIP; |
90a42210 | 1667 | } else if (alg == IW_ENCODE_ALG_CCMP) { |
876c9d3a | 1668 | pkey->type = KEY_TYPE_ID_AES; |
90a42210 | 1669 | } |
876c9d3a MT |
1670 | |
1671 | /* If WPA isn't enabled yet, do that now */ | |
1672 | if ( assoc_req->secinfo.WPAenabled == 0 | |
1673 | && assoc_req->secinfo.WPA2enabled == 0) { | |
1674 | assoc_req->secinfo.WPAenabled = 1; | |
1675 | assoc_req->secinfo.WPA2enabled = 1; | |
1676 | set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); | |
1677 | } | |
1678 | ||
9c31fd63 JC |
1679 | /* Only disable wep if necessary: can't waste time here. */ |
1680 | if (priv->mac_control & CMD_ACT_MAC_WEP_ENABLE) | |
1681 | disable_wep(assoc_req); | |
876c9d3a MT |
1682 | } |
1683 | ||
1684 | out: | |
9c40fc51 JC |
1685 | if (ret == 0) { |
1686 | /* 802.1x and WPA rekeying must happen as quickly as possible, | |
1687 | * especially during the 4-way handshake; thus if in | |
1688 | * infrastructure mode, and either (a) 802.1x is enabled or | |
1689 | * (b) WPA is being used, set the key right away. | |
1690 | */ | |
1691 | if (assoc_req->mode == IW_MODE_INFRA && | |
1692 | ((assoc_req->secinfo.key_mgmt & IW_AUTH_KEY_MGMT_802_1X) || | |
1693 | (assoc_req->secinfo.key_mgmt & IW_AUTH_KEY_MGMT_PSK) || | |
1694 | assoc_req->secinfo.WPAenabled || | |
1695 | assoc_req->secinfo.WPA2enabled)) { | |
1696 | lbs_do_association_work(priv); | |
1697 | } else | |
1698 | lbs_postpone_association_work(priv); | |
876c9d3a | 1699 | } else { |
10078321 | 1700 | lbs_cancel_association_work(priv); |
876c9d3a | 1701 | } |
aa21c004 | 1702 | mutex_unlock(&priv->lock); |
876c9d3a | 1703 | |
9012b28a | 1704 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); |
876c9d3a MT |
1705 | return ret; |
1706 | } | |
1707 | ||
1708 | ||
10078321 | 1709 | static int lbs_set_genie(struct net_device *dev, |
876c9d3a MT |
1710 | struct iw_request_info *info, |
1711 | struct iw_point *dwrq, | |
1712 | char *extra) | |
1713 | { | |
ab65f649 | 1714 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a MT |
1715 | int ret = 0; |
1716 | struct assoc_request * assoc_req; | |
1717 | ||
9012b28a | 1718 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 1719 | |
aa21c004 DW |
1720 | mutex_lock(&priv->lock); |
1721 | assoc_req = lbs_get_association_request(priv); | |
876c9d3a MT |
1722 | if (!assoc_req) { |
1723 | ret = -ENOMEM; | |
1724 | goto out; | |
1725 | } | |
1726 | ||
1727 | if (dwrq->length > MAX_WPA_IE_LEN || | |
1728 | (dwrq->length && extra == NULL)) { | |
1729 | ret = -EINVAL; | |
1730 | goto out; | |
1731 | } | |
1732 | ||
1733 | if (dwrq->length) { | |
1734 | memcpy(&assoc_req->wpa_ie[0], extra, dwrq->length); | |
1735 | assoc_req->wpa_ie_len = dwrq->length; | |
1736 | } else { | |
aa21c004 | 1737 | memset(&assoc_req->wpa_ie[0], 0, sizeof(priv->wpa_ie)); |
876c9d3a MT |
1738 | assoc_req->wpa_ie_len = 0; |
1739 | } | |
1740 | ||
1741 | out: | |
1742 | if (ret == 0) { | |
1743 | set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags); | |
10078321 | 1744 | lbs_postpone_association_work(priv); |
876c9d3a | 1745 | } else { |
10078321 | 1746 | lbs_cancel_association_work(priv); |
876c9d3a | 1747 | } |
aa21c004 | 1748 | mutex_unlock(&priv->lock); |
876c9d3a | 1749 | |
9012b28a | 1750 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); |
876c9d3a MT |
1751 | return ret; |
1752 | } | |
1753 | ||
10078321 | 1754 | static int lbs_get_genie(struct net_device *dev, |
876c9d3a MT |
1755 | struct iw_request_info *info, |
1756 | struct iw_point *dwrq, | |
1757 | char *extra) | |
1758 | { | |
9012b28a | 1759 | int ret = 0; |
ab65f649 | 1760 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a | 1761 | |
9012b28a | 1762 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 1763 | |
aa21c004 | 1764 | if (priv->wpa_ie_len == 0) { |
876c9d3a | 1765 | dwrq->length = 0; |
9012b28a | 1766 | goto out; |
876c9d3a MT |
1767 | } |
1768 | ||
aa21c004 | 1769 | if (dwrq->length < priv->wpa_ie_len) { |
9012b28a HS |
1770 | ret = -E2BIG; |
1771 | goto out; | |
876c9d3a MT |
1772 | } |
1773 | ||
aa21c004 DW |
1774 | dwrq->length = priv->wpa_ie_len; |
1775 | memcpy(extra, &priv->wpa_ie[0], priv->wpa_ie_len); | |
876c9d3a | 1776 | |
9012b28a HS |
1777 | out: |
1778 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); | |
1779 | return ret; | |
876c9d3a MT |
1780 | } |
1781 | ||
1782 | ||
10078321 | 1783 | static int lbs_set_auth(struct net_device *dev, |
876c9d3a MT |
1784 | struct iw_request_info *info, |
1785 | struct iw_param *dwrq, | |
1786 | char *extra) | |
1787 | { | |
ab65f649 | 1788 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a MT |
1789 | struct assoc_request * assoc_req; |
1790 | int ret = 0; | |
1791 | int updated = 0; | |
1792 | ||
9012b28a | 1793 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 1794 | |
aa21c004 DW |
1795 | mutex_lock(&priv->lock); |
1796 | assoc_req = lbs_get_association_request(priv); | |
876c9d3a MT |
1797 | if (!assoc_req) { |
1798 | ret = -ENOMEM; | |
1799 | goto out; | |
1800 | } | |
1801 | ||
1802 | switch (dwrq->flags & IW_AUTH_INDEX) { | |
2c8d5104 MH |
1803 | case IW_AUTH_PRIVACY_INVOKED: |
1804 | case IW_AUTH_RX_UNENCRYPTED_EAPOL: | |
876c9d3a MT |
1805 | case IW_AUTH_TKIP_COUNTERMEASURES: |
1806 | case IW_AUTH_CIPHER_PAIRWISE: | |
1807 | case IW_AUTH_CIPHER_GROUP: | |
90a42210 | 1808 | case IW_AUTH_DROP_UNENCRYPTED: |
876c9d3a MT |
1809 | /* |
1810 | * libertas does not use these parameters | |
1811 | */ | |
1812 | break; | |
1813 | ||
9c40fc51 JC |
1814 | case IW_AUTH_KEY_MGMT: |
1815 | assoc_req->secinfo.key_mgmt = dwrq->value; | |
1816 | updated = 1; | |
1817 | break; | |
1818 | ||
876c9d3a MT |
1819 | case IW_AUTH_WPA_VERSION: |
1820 | if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) { | |
1821 | assoc_req->secinfo.WPAenabled = 0; | |
1822 | assoc_req->secinfo.WPA2enabled = 0; | |
90a42210 | 1823 | disable_wpa (assoc_req); |
876c9d3a MT |
1824 | } |
1825 | if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) { | |
1826 | assoc_req->secinfo.WPAenabled = 1; | |
889c05bd | 1827 | assoc_req->secinfo.wep_enabled = 0; |
6affe785 | 1828 | assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; |
876c9d3a MT |
1829 | } |
1830 | if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) { | |
1831 | assoc_req->secinfo.WPA2enabled = 1; | |
889c05bd | 1832 | assoc_req->secinfo.wep_enabled = 0; |
6affe785 | 1833 | assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; |
876c9d3a MT |
1834 | } |
1835 | updated = 1; | |
1836 | break; | |
1837 | ||
876c9d3a MT |
1838 | case IW_AUTH_80211_AUTH_ALG: |
1839 | if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) { | |
6affe785 | 1840 | assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; |
876c9d3a | 1841 | } else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) { |
6affe785 | 1842 | assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; |
876c9d3a | 1843 | } else if (dwrq->value & IW_AUTH_ALG_LEAP) { |
6affe785 | 1844 | assoc_req->secinfo.auth_mode = IW_AUTH_ALG_LEAP; |
876c9d3a MT |
1845 | } else { |
1846 | ret = -EINVAL; | |
1847 | } | |
1848 | updated = 1; | |
1849 | break; | |
1850 | ||
1851 | case IW_AUTH_WPA_ENABLED: | |
1852 | if (dwrq->value) { | |
1853 | if (!assoc_req->secinfo.WPAenabled && | |
1854 | !assoc_req->secinfo.WPA2enabled) { | |
1855 | assoc_req->secinfo.WPAenabled = 1; | |
1856 | assoc_req->secinfo.WPA2enabled = 1; | |
889c05bd | 1857 | assoc_req->secinfo.wep_enabled = 0; |
6affe785 | 1858 | assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; |
876c9d3a MT |
1859 | } |
1860 | } else { | |
1861 | assoc_req->secinfo.WPAenabled = 0; | |
1862 | assoc_req->secinfo.WPA2enabled = 0; | |
90a42210 | 1863 | disable_wpa (assoc_req); |
876c9d3a MT |
1864 | } |
1865 | updated = 1; | |
1866 | break; | |
1867 | ||
1868 | default: | |
1869 | ret = -EOPNOTSUPP; | |
1870 | break; | |
1871 | } | |
1872 | ||
1873 | out: | |
1874 | if (ret == 0) { | |
1875 | if (updated) | |
1876 | set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); | |
10078321 | 1877 | lbs_postpone_association_work(priv); |
876c9d3a | 1878 | } else if (ret != -EOPNOTSUPP) { |
10078321 | 1879 | lbs_cancel_association_work(priv); |
876c9d3a | 1880 | } |
aa21c004 | 1881 | mutex_unlock(&priv->lock); |
876c9d3a | 1882 | |
9012b28a | 1883 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); |
876c9d3a MT |
1884 | return ret; |
1885 | } | |
1886 | ||
10078321 | 1887 | static int lbs_get_auth(struct net_device *dev, |
876c9d3a MT |
1888 | struct iw_request_info *info, |
1889 | struct iw_param *dwrq, | |
1890 | char *extra) | |
1891 | { | |
9012b28a | 1892 | int ret = 0; |
ab65f649 | 1893 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a | 1894 | |
9012b28a | 1895 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a MT |
1896 | |
1897 | switch (dwrq->flags & IW_AUTH_INDEX) { | |
9c40fc51 JC |
1898 | case IW_AUTH_KEY_MGMT: |
1899 | dwrq->value = priv->secinfo.key_mgmt; | |
1900 | break; | |
1901 | ||
876c9d3a MT |
1902 | case IW_AUTH_WPA_VERSION: |
1903 | dwrq->value = 0; | |
aa21c004 | 1904 | if (priv->secinfo.WPAenabled) |
876c9d3a | 1905 | dwrq->value |= IW_AUTH_WPA_VERSION_WPA; |
aa21c004 | 1906 | if (priv->secinfo.WPA2enabled) |
876c9d3a MT |
1907 | dwrq->value |= IW_AUTH_WPA_VERSION_WPA2; |
1908 | if (!dwrq->value) | |
1909 | dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED; | |
1910 | break; | |
1911 | ||
876c9d3a | 1912 | case IW_AUTH_80211_AUTH_ALG: |
aa21c004 | 1913 | dwrq->value = priv->secinfo.auth_mode; |
876c9d3a MT |
1914 | break; |
1915 | ||
1916 | case IW_AUTH_WPA_ENABLED: | |
aa21c004 | 1917 | if (priv->secinfo.WPAenabled && priv->secinfo.WPA2enabled) |
876c9d3a MT |
1918 | dwrq->value = 1; |
1919 | break; | |
1920 | ||
1921 | default: | |
9012b28a | 1922 | ret = -EOPNOTSUPP; |
876c9d3a MT |
1923 | } |
1924 | ||
9012b28a HS |
1925 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); |
1926 | return ret; | |
876c9d3a MT |
1927 | } |
1928 | ||
1929 | ||
10078321 | 1930 | static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
1931 | struct iw_param *vwrq, char *extra) |
1932 | { | |
1933 | int ret = 0; | |
ab65f649 | 1934 | struct lbs_private *priv = dev->ml_priv; |
87c8c72d | 1935 | s16 dbm = (s16) vwrq->value; |
876c9d3a | 1936 | |
9012b28a | 1937 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a MT |
1938 | |
1939 | if (vwrq->disabled) { | |
d5db2dfa | 1940 | lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 0); |
87c8c72d | 1941 | goto out; |
876c9d3a MT |
1942 | } |
1943 | ||
87c8c72d | 1944 | if (vwrq->fixed == 0) { |
0112c9e9 AN |
1945 | /* User requests automatic tx power control, however there are |
1946 | * many auto tx settings. For now use firmware defaults until | |
1947 | * we come up with a good way to expose these to the user. */ | |
1948 | if (priv->fwrelease < 0x09000000) { | |
1949 | ret = lbs_set_power_adapt_cfg(priv, 1, | |
1950 | POW_ADAPT_DEFAULT_P0, | |
1951 | POW_ADAPT_DEFAULT_P1, | |
1952 | POW_ADAPT_DEFAULT_P2); | |
1953 | if (ret) | |
1954 | goto out; | |
1955 | } | |
1956 | ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1, | |
1957 | TPC_DEFAULT_P2, 1); | |
1958 | if (ret) | |
1959 | goto out; | |
87c8c72d DW |
1960 | dbm = priv->txpower_max; |
1961 | } else { | |
1962 | /* Userspace check in iwrange if it should use dBm or mW, | |
1963 | * therefore this should never happen... Jean II */ | |
1964 | if ((vwrq->flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) { | |
1965 | ret = -EOPNOTSUPP; | |
1966 | goto out; | |
1967 | } | |
876c9d3a | 1968 | |
0112c9e9 AN |
1969 | /* Validate requested power level against firmware allowed |
1970 | * levels */ | |
87c8c72d DW |
1971 | if (priv->txpower_min && (dbm < priv->txpower_min)) { |
1972 | ret = -EINVAL; | |
1973 | goto out; | |
1974 | } | |
876c9d3a | 1975 | |
87c8c72d DW |
1976 | if (priv->txpower_max && (dbm > priv->txpower_max)) { |
1977 | ret = -EINVAL; | |
1978 | goto out; | |
1979 | } | |
0112c9e9 AN |
1980 | if (priv->fwrelease < 0x09000000) { |
1981 | ret = lbs_set_power_adapt_cfg(priv, 0, | |
1982 | POW_ADAPT_DEFAULT_P0, | |
1983 | POW_ADAPT_DEFAULT_P1, | |
1984 | POW_ADAPT_DEFAULT_P2); | |
1985 | if (ret) | |
1986 | goto out; | |
1987 | } | |
1988 | ret = lbs_set_tpc_cfg(priv, 0, TPC_DEFAULT_P0, TPC_DEFAULT_P1, | |
1989 | TPC_DEFAULT_P2, 1); | |
1990 | if (ret) | |
1991 | goto out; | |
87c8c72d | 1992 | } |
876c9d3a | 1993 | |
d5db2dfa DW |
1994 | /* If the radio was off, turn it on */ |
1995 | if (!priv->radio_on) { | |
1996 | ret = lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 1); | |
1997 | if (ret) | |
1998 | goto out; | |
1999 | } | |
876c9d3a | 2000 | |
87c8c72d | 2001 | lbs_deb_wext("txpower set %d dBm\n", dbm); |
876c9d3a | 2002 | |
87c8c72d | 2003 | ret = lbs_set_tx_power(priv, dbm); |
876c9d3a | 2004 | |
87c8c72d | 2005 | out: |
9012b28a | 2006 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); |
876c9d3a MT |
2007 | return ret; |
2008 | } | |
2009 | ||
10078321 | 2010 | static int lbs_get_essid(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
2011 | struct iw_point *dwrq, char *extra) |
2012 | { | |
ab65f649 | 2013 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a | 2014 | |
9012b28a HS |
2015 | lbs_deb_enter(LBS_DEB_WEXT); |
2016 | ||
876c9d3a MT |
2017 | /* |
2018 | * Note : if dwrq->flags != 0, we should get the relevant SSID from | |
2019 | * the SSID list... | |
2020 | */ | |
2021 | ||
2022 | /* | |
2023 | * Get the current SSID | |
2024 | */ | |
aa21c004 DW |
2025 | if (priv->connect_status == LBS_CONNECTED) { |
2026 | memcpy(extra, priv->curbssparams.ssid, | |
2027 | priv->curbssparams.ssid_len); | |
876c9d3a MT |
2028 | } else { |
2029 | memset(extra, 0, 32); | |
876c9d3a MT |
2030 | } |
2031 | /* | |
2032 | * If none, we may want to get the one that was set | |
2033 | */ | |
2034 | ||
aa21c004 | 2035 | dwrq->length = priv->curbssparams.ssid_len; |
876c9d3a MT |
2036 | |
2037 | dwrq->flags = 1; /* active */ | |
2038 | ||
9012b28a | 2039 | lbs_deb_leave(LBS_DEB_WEXT); |
876c9d3a MT |
2040 | return 0; |
2041 | } | |
2042 | ||
10078321 | 2043 | static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
2044 | struct iw_point *dwrq, char *extra) |
2045 | { | |
ab65f649 | 2046 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a | 2047 | int ret = 0; |
243e84e9 | 2048 | u8 ssid[IEEE80211_MAX_SSID_LEN]; |
d8efea25 | 2049 | u8 ssid_len = 0; |
876c9d3a | 2050 | struct assoc_request * assoc_req; |
d8efea25 | 2051 | int in_ssid_len = dwrq->length; |
9387b7ca | 2052 | DECLARE_SSID_BUF(ssid_buf); |
876c9d3a | 2053 | |
9012b28a | 2054 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 2055 | |
d5db2dfa DW |
2056 | if (!priv->radio_on) { |
2057 | ret = -EINVAL; | |
2058 | goto out; | |
2059 | } | |
2060 | ||
876c9d3a | 2061 | /* Check the size of the string */ |
243e84e9 | 2062 | if (in_ssid_len > IEEE80211_MAX_SSID_LEN) { |
876c9d3a MT |
2063 | ret = -E2BIG; |
2064 | goto out; | |
2065 | } | |
2066 | ||
d8efea25 | 2067 | memset(&ssid, 0, sizeof(ssid)); |
876c9d3a | 2068 | |
d8efea25 | 2069 | if (!dwrq->flags || !in_ssid_len) { |
876c9d3a MT |
2070 | /* "any" SSID requested; leave SSID blank */ |
2071 | } else { | |
2072 | /* Specific SSID requested */ | |
d8efea25 DW |
2073 | memcpy(&ssid, extra, in_ssid_len); |
2074 | ssid_len = in_ssid_len; | |
876c9d3a MT |
2075 | } |
2076 | ||
d8efea25 DW |
2077 | if (!ssid_len) { |
2078 | lbs_deb_wext("requested any SSID\n"); | |
2079 | } else { | |
2080 | lbs_deb_wext("requested SSID '%s'\n", | |
9387b7ca | 2081 | print_ssid(ssid_buf, ssid, ssid_len)); |
d8efea25 | 2082 | } |
876c9d3a MT |
2083 | |
2084 | out: | |
aa21c004 | 2085 | mutex_lock(&priv->lock); |
876c9d3a MT |
2086 | if (ret == 0) { |
2087 | /* Get or create the current association request */ | |
aa21c004 | 2088 | assoc_req = lbs_get_association_request(priv); |
876c9d3a MT |
2089 | if (!assoc_req) { |
2090 | ret = -ENOMEM; | |
2091 | } else { | |
2092 | /* Copy the SSID to the association request */ | |
243e84e9 | 2093 | memcpy(&assoc_req->ssid, &ssid, IEEE80211_MAX_SSID_LEN); |
d8efea25 | 2094 | assoc_req->ssid_len = ssid_len; |
876c9d3a | 2095 | set_bit(ASSOC_FLAG_SSID, &assoc_req->flags); |
10078321 | 2096 | lbs_postpone_association_work(priv); |
876c9d3a MT |
2097 | } |
2098 | } | |
2099 | ||
2100 | /* Cancel the association request if there was an error */ | |
2101 | if (ret != 0) { | |
10078321 | 2102 | lbs_cancel_association_work(priv); |
876c9d3a MT |
2103 | } |
2104 | ||
aa21c004 | 2105 | mutex_unlock(&priv->lock); |
876c9d3a | 2106 | |
9012b28a | 2107 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); |
876c9d3a MT |
2108 | return ret; |
2109 | } | |
2110 | ||
f5956bf1 DW |
2111 | static int lbs_mesh_get_essid(struct net_device *dev, |
2112 | struct iw_request_info *info, | |
2113 | struct iw_point *dwrq, char *extra) | |
2114 | { | |
ab65f649 | 2115 | struct lbs_private *priv = dev->ml_priv; |
f5956bf1 DW |
2116 | |
2117 | lbs_deb_enter(LBS_DEB_WEXT); | |
2118 | ||
2119 | memcpy(extra, priv->mesh_ssid, priv->mesh_ssid_len); | |
2120 | ||
2121 | dwrq->length = priv->mesh_ssid_len; | |
2122 | ||
2123 | dwrq->flags = 1; /* active */ | |
2124 | ||
2125 | lbs_deb_leave(LBS_DEB_WEXT); | |
2126 | return 0; | |
2127 | } | |
2128 | ||
2129 | static int lbs_mesh_set_essid(struct net_device *dev, | |
2130 | struct iw_request_info *info, | |
2131 | struct iw_point *dwrq, char *extra) | |
2132 | { | |
ab65f649 | 2133 | struct lbs_private *priv = dev->ml_priv; |
f5956bf1 DW |
2134 | int ret = 0; |
2135 | ||
2136 | lbs_deb_enter(LBS_DEB_WEXT); | |
2137 | ||
d5db2dfa DW |
2138 | if (!priv->radio_on) { |
2139 | ret = -EINVAL; | |
2140 | goto out; | |
2141 | } | |
2142 | ||
f5956bf1 | 2143 | /* Check the size of the string */ |
243e84e9 | 2144 | if (dwrq->length > IEEE80211_MAX_SSID_LEN) { |
f5956bf1 DW |
2145 | ret = -E2BIG; |
2146 | goto out; | |
2147 | } | |
2148 | ||
2149 | if (!dwrq->flags || !dwrq->length) { | |
2150 | ret = -EINVAL; | |
2151 | goto out; | |
2152 | } else { | |
2153 | /* Specific SSID requested */ | |
2154 | memcpy(priv->mesh_ssid, extra, dwrq->length); | |
2155 | priv->mesh_ssid_len = dwrq->length; | |
2156 | } | |
2157 | ||
edaea5ce | 2158 | lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, |
c14951fe | 2159 | priv->channel); |
f5956bf1 DW |
2160 | out: |
2161 | lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret); | |
2162 | return ret; | |
2163 | } | |
2164 | ||
876c9d3a MT |
2165 | /** |
2166 | * @brief Connect to the AP or Ad-hoc Network with specific bssid | |
2167 | * | |
2168 | * @param dev A pointer to net_device structure | |
2169 | * @param info A pointer to iw_request_info structure | |
2170 | * @param awrq A pointer to iw_param structure | |
2171 | * @param extra A pointer to extra data buf | |
2172 | * @return 0 --success, otherwise fail | |
2173 | */ | |
10078321 | 2174 | static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info, |
876c9d3a MT |
2175 | struct sockaddr *awrq, char *extra) |
2176 | { | |
ab65f649 | 2177 | struct lbs_private *priv = dev->ml_priv; |
876c9d3a MT |
2178 | struct assoc_request * assoc_req; |
2179 | int ret = 0; | |
2180 | ||
9012b28a | 2181 | lbs_deb_enter(LBS_DEB_WEXT); |
876c9d3a | 2182 | |
d5db2dfa DW |
2183 | if (!priv->radio_on) |
2184 | return -EINVAL; | |
2185 | ||
876c9d3a MT |
2186 | if (awrq->sa_family != ARPHRD_ETHER) |
2187 | return -EINVAL; | |
2188 | ||
e174961c | 2189 | lbs_deb_wext("ASSOC: WAP: sa_data %pM\n", awrq->sa_data); |
876c9d3a | 2190 | |
aa21c004 | 2191 | mutex_lock(&priv->lock); |
876c9d3a MT |
2192 | |
2193 | /* Get or create the current association request */ | |
aa21c004 | 2194 | assoc_req = lbs_get_association_request(priv); |
876c9d3a | 2195 | if (!assoc_req) { |
10078321 | 2196 | lbs_cancel_association_work(priv); |
876c9d3a MT |
2197 | ret = -ENOMEM; |
2198 | } else { | |
2199 | /* Copy the BSSID to the association request */ | |
2200 | memcpy(&assoc_req->bssid, awrq->sa_data, ETH_ALEN); | |
2201 | set_bit(ASSOC_FLAG_BSSID, &assoc_req->flags); | |
10078321 | 2202 | lbs_postpone_association_work(priv); |
876c9d3a MT |
2203 | } |
2204 | ||
aa21c004 | 2205 | mutex_unlock(&priv->lock); |
876c9d3a MT |
2206 | |
2207 | return ret; | |
2208 | } | |
2209 | ||
876c9d3a MT |
2210 | /* |
2211 | * iwconfig settable callbacks | |
2212 | */ | |
10078321 | 2213 | static const iw_handler lbs_handler[] = { |
876c9d3a | 2214 | (iw_handler) NULL, /* SIOCSIWCOMMIT */ |
10078321 | 2215 | (iw_handler) lbs_get_name, /* SIOCGIWNAME */ |
876c9d3a MT |
2216 | (iw_handler) NULL, /* SIOCSIWNWID */ |
2217 | (iw_handler) NULL, /* SIOCGIWNWID */ | |
10078321 HS |
2218 | (iw_handler) lbs_set_freq, /* SIOCSIWFREQ */ |
2219 | (iw_handler) lbs_get_freq, /* SIOCGIWFREQ */ | |
2220 | (iw_handler) lbs_set_mode, /* SIOCSIWMODE */ | |
2221 | (iw_handler) lbs_get_mode, /* SIOCGIWMODE */ | |
876c9d3a MT |
2222 | (iw_handler) NULL, /* SIOCSIWSENS */ |
2223 | (iw_handler) NULL, /* SIOCGIWSENS */ | |
2224 | (iw_handler) NULL, /* SIOCSIWRANGE */ | |
10078321 | 2225 | (iw_handler) lbs_get_range, /* SIOCGIWRANGE */ |
876c9d3a MT |
2226 | (iw_handler) NULL, /* SIOCSIWPRIV */ |
2227 | (iw_handler) NULL, /* SIOCGIWPRIV */ | |
2228 | (iw_handler) NULL, /* SIOCSIWSTATS */ | |
2229 | (iw_handler) NULL, /* SIOCGIWSTATS */ | |
2230 | iw_handler_set_spy, /* SIOCSIWSPY */ | |
2231 | iw_handler_get_spy, /* SIOCGIWSPY */ | |
2232 | iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ | |
2233 | iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ | |
10078321 HS |
2234 | (iw_handler) lbs_set_wap, /* SIOCSIWAP */ |
2235 | (iw_handler) lbs_get_wap, /* SIOCGIWAP */ | |
876c9d3a MT |
2236 | (iw_handler) NULL, /* SIOCSIWMLME */ |
2237 | (iw_handler) NULL, /* SIOCGIWAPLIST - deprecated */ | |
10078321 HS |
2238 | (iw_handler) lbs_set_scan, /* SIOCSIWSCAN */ |
2239 | (iw_handler) lbs_get_scan, /* SIOCGIWSCAN */ | |
2240 | (iw_handler) lbs_set_essid, /* SIOCSIWESSID */ | |
2241 | (iw_handler) lbs_get_essid, /* SIOCGIWESSID */ | |
2242 | (iw_handler) lbs_set_nick, /* SIOCSIWNICKN */ | |
2243 | (iw_handler) lbs_get_nick, /* SIOCGIWNICKN */ | |
876c9d3a MT |
2244 | (iw_handler) NULL, /* -- hole -- */ |
2245 | (iw_handler) NULL, /* -- hole -- */ | |
10078321 HS |
2246 | (iw_handler) lbs_set_rate, /* SIOCSIWRATE */ |
2247 | (iw_handler) lbs_get_rate, /* SIOCGIWRATE */ | |
2248 | (iw_handler) lbs_set_rts, /* SIOCSIWRTS */ | |
2249 | (iw_handler) lbs_get_rts, /* SIOCGIWRTS */ | |
2250 | (iw_handler) lbs_set_frag, /* SIOCSIWFRAG */ | |
2251 | (iw_handler) lbs_get_frag, /* SIOCGIWFRAG */ | |
2252 | (iw_handler) lbs_set_txpow, /* SIOCSIWTXPOW */ | |
2253 | (iw_handler) lbs_get_txpow, /* SIOCGIWTXPOW */ | |
2254 | (iw_handler) lbs_set_retry, /* SIOCSIWRETRY */ | |
2255 | (iw_handler) lbs_get_retry, /* SIOCGIWRETRY */ | |
2256 | (iw_handler) lbs_set_encode, /* SIOCSIWENCODE */ | |
2257 | (iw_handler) lbs_get_encode, /* SIOCGIWENCODE */ | |
2258 | (iw_handler) lbs_set_power, /* SIOCSIWPOWER */ | |
2259 | (iw_handler) lbs_get_power, /* SIOCGIWPOWER */ | |
876c9d3a MT |
2260 | (iw_handler) NULL, /* -- hole -- */ |
2261 | (iw_handler) NULL, /* -- hole -- */ | |
10078321 HS |
2262 | (iw_handler) lbs_set_genie, /* SIOCSIWGENIE */ |
2263 | (iw_handler) lbs_get_genie, /* SIOCGIWGENIE */ | |
2264 | (iw_handler) lbs_set_auth, /* SIOCSIWAUTH */ | |
2265 | (iw_handler) lbs_get_auth, /* SIOCGIWAUTH */ | |
2266 | (iw_handler) lbs_set_encodeext,/* SIOCSIWENCODEEXT */ | |
2267 | (iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */ | |
876c9d3a MT |
2268 | (iw_handler) NULL, /* SIOCSIWPMKSA */ |
2269 | }; | |
2270 | ||
f5e05b69 LCCR |
2271 | static const iw_handler mesh_wlan_handler[] = { |
2272 | (iw_handler) NULL, /* SIOCSIWCOMMIT */ | |
10078321 | 2273 | (iw_handler) lbs_get_name, /* SIOCGIWNAME */ |
f5e05b69 LCCR |
2274 | (iw_handler) NULL, /* SIOCSIWNWID */ |
2275 | (iw_handler) NULL, /* SIOCGIWNWID */ | |
823eaa2c | 2276 | (iw_handler) lbs_mesh_set_freq, /* SIOCSIWFREQ */ |
10078321 | 2277 | (iw_handler) lbs_get_freq, /* SIOCGIWFREQ */ |
f5e05b69 LCCR |
2278 | (iw_handler) NULL, /* SIOCSIWMODE */ |
2279 | (iw_handler) mesh_wlan_get_mode, /* SIOCGIWMODE */ | |
2280 | (iw_handler) NULL, /* SIOCSIWSENS */ | |
2281 | (iw_handler) NULL, /* SIOCGIWSENS */ | |
2282 | (iw_handler) NULL, /* SIOCSIWRANGE */ | |
10078321 | 2283 | (iw_handler) lbs_get_range, /* SIOCGIWRANGE */ |
f5e05b69 LCCR |
2284 | (iw_handler) NULL, /* SIOCSIWPRIV */ |
2285 | (iw_handler) NULL, /* SIOCGIWPRIV */ | |
2286 | (iw_handler) NULL, /* SIOCSIWSTATS */ | |
2287 | (iw_handler) NULL, /* SIOCGIWSTATS */ | |
2288 | iw_handler_set_spy, /* SIOCSIWSPY */ | |
2289 | iw_handler_get_spy, /* SIOCGIWSPY */ | |
2290 | iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ | |
2291 | iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ | |
2292 | (iw_handler) NULL, /* SIOCSIWAP */ | |
2293 | (iw_handler) NULL, /* SIOCGIWAP */ | |
2294 | (iw_handler) NULL, /* SIOCSIWMLME */ | |
2295 | (iw_handler) NULL, /* SIOCGIWAPLIST - deprecated */ | |
10078321 HS |
2296 | (iw_handler) lbs_set_scan, /* SIOCSIWSCAN */ |
2297 | (iw_handler) lbs_get_scan, /* SIOCGIWSCAN */ | |
f5956bf1 DW |
2298 | (iw_handler) lbs_mesh_set_essid,/* SIOCSIWESSID */ |
2299 | (iw_handler) lbs_mesh_get_essid,/* SIOCGIWESSID */ | |
f5e05b69 LCCR |
2300 | (iw_handler) NULL, /* SIOCSIWNICKN */ |
2301 | (iw_handler) mesh_get_nick, /* SIOCGIWNICKN */ | |
2302 | (iw_handler) NULL, /* -- hole -- */ | |
2303 | (iw_handler) NULL, /* -- hole -- */ | |
10078321 HS |
2304 | (iw_handler) lbs_set_rate, /* SIOCSIWRATE */ |
2305 | (iw_handler) lbs_get_rate, /* SIOCGIWRATE */ | |
2306 | (iw_handler) lbs_set_rts, /* SIOCSIWRTS */ | |
2307 | (iw_handler) lbs_get_rts, /* SIOCGIWRTS */ | |
2308 | (iw_handler) lbs_set_frag, /* SIOCSIWFRAG */ | |
2309 | (iw_handler) lbs_get_frag, /* SIOCGIWFRAG */ | |
2310 | (iw_handler) lbs_set_txpow, /* SIOCSIWTXPOW */ | |
2311 | (iw_handler) lbs_get_txpow, /* SIOCGIWTXPOW */ | |
2312 | (iw_handler) lbs_set_retry, /* SIOCSIWRETRY */ | |
2313 | (iw_handler) lbs_get_retry, /* SIOCGIWRETRY */ | |
2314 | (iw_handler) lbs_set_encode, /* SIOCSIWENCODE */ | |
2315 | (iw_handler) lbs_get_encode, /* SIOCGIWENCODE */ | |
2316 | (iw_handler) lbs_set_power, /* SIOCSIWPOWER */ | |
2317 | (iw_handler) lbs_get_power, /* SIOCGIWPOWER */ | |
f5e05b69 LCCR |
2318 | (iw_handler) NULL, /* -- hole -- */ |
2319 | (iw_handler) NULL, /* -- hole -- */ | |
10078321 HS |
2320 | (iw_handler) lbs_set_genie, /* SIOCSIWGENIE */ |
2321 | (iw_handler) lbs_get_genie, /* SIOCGIWGENIE */ | |
2322 | (iw_handler) lbs_set_auth, /* SIOCSIWAUTH */ | |
2323 | (iw_handler) lbs_get_auth, /* SIOCGIWAUTH */ | |
2324 | (iw_handler) lbs_set_encodeext,/* SIOCSIWENCODEEXT */ | |
2325 | (iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */ | |
f5e05b69 LCCR |
2326 | (iw_handler) NULL, /* SIOCSIWPMKSA */ |
2327 | }; | |
10078321 HS |
2328 | struct iw_handler_def lbs_handler_def = { |
2329 | .num_standard = ARRAY_SIZE(lbs_handler), | |
2330 | .standard = (iw_handler *) lbs_handler, | |
2331 | .get_wireless_stats = lbs_get_wireless_stats, | |
876c9d3a | 2332 | }; |
f5e05b69 LCCR |
2333 | |
2334 | struct iw_handler_def mesh_handler_def = { | |
ff8ac609 | 2335 | .num_standard = ARRAY_SIZE(mesh_wlan_handler), |
f5e05b69 | 2336 | .standard = (iw_handler *) mesh_wlan_handler, |
10078321 | 2337 | .get_wireless_stats = lbs_get_wireless_stats, |
f5e05b69 | 2338 | }; |