Commit | Line | Data |
---|---|---|
8fc8598e JC |
1 | /* |
2 | This file contains wireless extension handlers. | |
3 | ||
4 | This is part of rtl8180 OpenSource driver. | |
559a4c31 | 5 | Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com> |
8fc8598e JC |
6 | Released under the terms of GPL (General Public Licence) |
7 | ||
8 | Parts of this driver are based on the GPL part | |
9 | of the official realtek driver. | |
10 | ||
11 | Parts of this driver are based on the rtl8180 driver skeleton | |
12 | from Patric Schenke & Andres Salomon. | |
13 | ||
14 | Parts of this driver are based on the Intel Pro Wireless 2100 GPL driver. | |
15 | ||
ffae3055 | 16 | We want to thank the Authors of those projects and the Ndiswrapper |
8fc8598e JC |
17 | project Authors. |
18 | */ | |
19 | ||
20 | #include <linux/string.h> | |
21 | #include "r8192U.h" | |
22 | #include "r8192U_hw.h" | |
23 | ||
8fc8598e | 24 | #include "dot11d.h" |
c4d6b8fb | 25 | #include "r8192U_wx.h" |
8fc8598e JC |
26 | |
27 | #define RATE_COUNT 12 | |
c4d6b8fb | 28 | static const u32 rtl8180_rates[] = {1000000, 2000000, 5500000, 11000000, |
654d1ce9 | 29 | 6000000, 9000000, 12000000, 18000000, 24000000, 36000000, 48000000, 54000000}; |
8fc8598e JC |
30 | |
31 | ||
32 | #ifndef ENETDOWN | |
33 | #define ENETDOWN 1 | |
34 | #endif | |
35 | ||
36 | static int r8192_wx_get_freq(struct net_device *dev, | |
37 | struct iw_request_info *a, | |
38 | union iwreq_data *wrqu, char *b) | |
39 | { | |
40 | struct r8192_priv *priv = ieee80211_priv(dev); | |
41 | ||
654d1ce9 | 42 | return ieee80211_wx_get_freq(priv->ieee80211, a, wrqu, b); |
8fc8598e JC |
43 | } |
44 | ||
45 | ||
8fc8598e JC |
46 | static int r8192_wx_get_mode(struct net_device *dev, struct iw_request_info *a, |
47 | union iwreq_data *wrqu, char *b) | |
48 | { | |
654d1ce9 | 49 | struct r8192_priv *priv = ieee80211_priv(dev); |
8fc8598e | 50 | |
654d1ce9 | 51 | return ieee80211_wx_get_mode(priv->ieee80211, a, wrqu, b); |
8fc8598e JC |
52 | } |
53 | ||
54 | ||
55 | ||
56 | static int r8192_wx_get_rate(struct net_device *dev, | |
57 | struct iw_request_info *info, | |
58 | union iwreq_data *wrqu, char *extra) | |
59 | { | |
60 | struct r8192_priv *priv = ieee80211_priv(dev); | |
a08d541a | 61 | |
654d1ce9 | 62 | return ieee80211_wx_get_rate(priv->ieee80211, info, wrqu, extra); |
8fc8598e JC |
63 | } |
64 | ||
65 | ||
66 | ||
67 | static int r8192_wx_set_rate(struct net_device *dev, | |
68 | struct iw_request_info *info, | |
69 | union iwreq_data *wrqu, char *extra) | |
70 | { | |
71 | int ret; | |
72 | struct r8192_priv *priv = ieee80211_priv(dev); | |
73 | ||
74 | down(&priv->wx_sem); | |
75 | ||
654d1ce9 | 76 | ret = ieee80211_wx_set_rate(priv->ieee80211, info, wrqu, extra); |
8fc8598e JC |
77 | |
78 | up(&priv->wx_sem); | |
79 | ||
80 | return ret; | |
81 | } | |
82 | ||
83 | ||
84 | static int r8192_wx_set_rts(struct net_device *dev, | |
85 | struct iw_request_info *info, | |
86 | union iwreq_data *wrqu, char *extra) | |
87 | { | |
88 | int ret; | |
89 | struct r8192_priv *priv = ieee80211_priv(dev); | |
90 | ||
91 | down(&priv->wx_sem); | |
92 | ||
654d1ce9 | 93 | ret = ieee80211_wx_set_rts(priv->ieee80211, info, wrqu, extra); |
8fc8598e JC |
94 | |
95 | up(&priv->wx_sem); | |
96 | ||
97 | return ret; | |
98 | } | |
99 | ||
100 | static int r8192_wx_get_rts(struct net_device *dev, | |
101 | struct iw_request_info *info, | |
102 | union iwreq_data *wrqu, char *extra) | |
103 | { | |
104 | struct r8192_priv *priv = ieee80211_priv(dev); | |
a08d541a | 105 | |
654d1ce9 | 106 | return ieee80211_wx_get_rts(priv->ieee80211, info, wrqu, extra); |
8fc8598e JC |
107 | } |
108 | ||
109 | static int r8192_wx_set_power(struct net_device *dev, | |
110 | struct iw_request_info *info, | |
111 | union iwreq_data *wrqu, char *extra) | |
112 | { | |
113 | int ret; | |
114 | struct r8192_priv *priv = ieee80211_priv(dev); | |
115 | ||
116 | down(&priv->wx_sem); | |
117 | ||
654d1ce9 | 118 | ret = ieee80211_wx_set_power(priv->ieee80211, info, wrqu, extra); |
8fc8598e JC |
119 | |
120 | up(&priv->wx_sem); | |
121 | ||
122 | return ret; | |
123 | } | |
124 | ||
125 | static int r8192_wx_get_power(struct net_device *dev, | |
126 | struct iw_request_info *info, | |
127 | union iwreq_data *wrqu, char *extra) | |
128 | { | |
129 | struct r8192_priv *priv = ieee80211_priv(dev); | |
a08d541a | 130 | |
654d1ce9 | 131 | return ieee80211_wx_get_power(priv->ieee80211, info, wrqu, extra); |
8fc8598e JC |
132 | } |
133 | ||
8fc8598e JC |
134 | static int r8192_wx_force_reset(struct net_device *dev, |
135 | struct iw_request_info *info, | |
136 | union iwreq_data *wrqu, char *extra) | |
137 | { | |
138 | struct r8192_priv *priv = ieee80211_priv(dev); | |
139 | ||
140 | down(&priv->wx_sem); | |
141 | ||
54160729 | 142 | netdev_dbg(dev, "%s(): force reset ! extra is %d\n", __func__, *extra); |
8fc8598e JC |
143 | priv->force_reset = *extra; |
144 | up(&priv->wx_sem); | |
145 | return 0; | |
146 | ||
147 | } | |
148 | ||
149 | ||
150 | static int r8192_wx_set_rawtx(struct net_device *dev, | |
151 | struct iw_request_info *info, | |
152 | union iwreq_data *wrqu, char *extra) | |
153 | { | |
154 | struct r8192_priv *priv = ieee80211_priv(dev); | |
155 | int ret; | |
156 | ||
157 | down(&priv->wx_sem); | |
158 | ||
159 | ret = ieee80211_wx_set_rawtx(priv->ieee80211, info, wrqu, extra); | |
160 | ||
161 | up(&priv->wx_sem); | |
162 | ||
163 | return ret; | |
164 | ||
165 | } | |
166 | ||
167 | static int r8192_wx_set_crcmon(struct net_device *dev, | |
168 | struct iw_request_info *info, | |
169 | union iwreq_data *wrqu, char *extra) | |
170 | { | |
171 | struct r8192_priv *priv = ieee80211_priv(dev); | |
172 | int *parms = (int *)extra; | |
173 | int enable = (parms[0] > 0); | |
8fc8598e JC |
174 | |
175 | down(&priv->wx_sem); | |
176 | ||
654d1ce9 CH |
177 | if (enable) |
178 | priv->crcmon = 1; | |
8fc8598e | 179 | else |
654d1ce9 | 180 | priv->crcmon = 0; |
8fc8598e JC |
181 | |
182 | DMESG("bad CRC in monitor mode are %s", | |
183 | priv->crcmon ? "accepted" : "rejected"); | |
184 | ||
8fc8598e JC |
185 | up(&priv->wx_sem); |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
190 | static int r8192_wx_set_mode(struct net_device *dev, struct iw_request_info *a, | |
191 | union iwreq_data *wrqu, char *b) | |
192 | { | |
193 | struct r8192_priv *priv = ieee80211_priv(dev); | |
194 | int ret; | |
a08d541a | 195 | |
8fc8598e JC |
196 | down(&priv->wx_sem); |
197 | ||
654d1ce9 | 198 | ret = ieee80211_wx_set_mode(priv->ieee80211, a, wrqu, b); |
8fc8598e JC |
199 | |
200 | rtl8192_set_rxconf(dev); | |
201 | ||
202 | up(&priv->wx_sem); | |
203 | return ret; | |
204 | } | |
205 | ||
0db7a34e | 206 | struct iw_range_with_scan_capa { |
e406322b MCC |
207 | /* Informative stuff (to choose between different interface) */ |
208 | __u32 throughput; /* To give an idea... */ | |
209 | /* In theory this value should be the maximum benchmarked | |
210 | * TCP/IP throughput, because with most of these devices the | |
211 | * bit rate is meaningless (overhead an co) to estimate how | |
212 | * fast the connection will go and pick the fastest one. | |
213 | * I suggest people to play with Netperf or any benchmark... | |
214 | */ | |
215 | ||
216 | /* NWID (or domain id) */ | |
217 | __u32 min_nwid; /* Minimal NWID we are able to set */ | |
218 | __u32 max_nwid; /* Maximal NWID we are able to set */ | |
219 | ||
220 | /* Old Frequency (backward compat - moved lower ) */ | |
221 | __u16 old_num_channels; | |
222 | __u8 old_num_frequency; | |
223 | ||
224 | /* Scan capabilities */ | |
225 | __u8 scan_capa; | |
8fc8598e JC |
226 | }; |
227 | static int rtl8180_wx_get_range(struct net_device *dev, | |
228 | struct iw_request_info *info, | |
229 | union iwreq_data *wrqu, char *extra) | |
230 | { | |
231 | struct iw_range *range = (struct iw_range *)extra; | |
b81c2b0a | 232 | struct iw_range_with_scan_capa *tmp = (struct iw_range_with_scan_capa *)range; |
8fc8598e JC |
233 | struct r8192_priv *priv = ieee80211_priv(dev); |
234 | u16 val; | |
235 | int i; | |
236 | ||
237 | wrqu->data.length = sizeof(*range); | |
238 | memset(range, 0, sizeof(*range)); | |
239 | ||
240 | /* Let's try to keep this struct in the same order as in | |
241 | * linux/include/wireless.h | |
242 | */ | |
243 | ||
244 | /* TODO: See what values we can set, and remove the ones we can't | |
245 | * set, or fill them with some default data. | |
246 | */ | |
247 | ||
248 | /* ~5 Mb/s real (802.11b) */ | |
249 | range->throughput = 5 * 1000 * 1000; | |
250 | ||
0bb927cf CH |
251 | /* TODO: Not used in 802.11b? */ |
252 | /* range->min_nwid; */ /* Minimal NWID we are able to set */ | |
253 | /* TODO: Not used in 802.11b? */ | |
254 | /* range->max_nwid; */ /* Maximal NWID we are able to set */ | |
8fc8598e | 255 | |
e406322b | 256 | /* Old Frequency (backward compat - moved lower ) */ |
0bb927cf CH |
257 | /* range->old_num_channels; */ |
258 | /* range->old_num_frequency; */ | |
259 | /* range->old_freq[6]; */ /* Filler to keep "version" at the same offset */ | |
654d1ce9 | 260 | if (priv->rf_set_sens != NULL) |
8fc8598e JC |
261 | range->sensitivity = priv->max_sens; /* signal level threshold range */ |
262 | ||
263 | range->max_qual.qual = 100; | |
264 | /* TODO: Find real max RSSI and stick here */ | |
265 | range->max_qual.level = 0; | |
3cd66a18 | 266 | range->max_qual.noise = 0x100 - 98; |
8fc8598e JC |
267 | range->max_qual.updated = 7; /* Updated all three */ |
268 | ||
269 | range->avg_qual.qual = 92; /* > 8% missed beacons is 'bad' */ | |
589b3d06 | 270 | /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ |
3cd66a18 | 271 | range->avg_qual.level = 0x100 - 78; |
8fc8598e JC |
272 | range->avg_qual.noise = 0; |
273 | range->avg_qual.updated = 7; /* Updated all three */ | |
274 | ||
275 | range->num_bitrates = RATE_COUNT; | |
276 | ||
2930d0b9 | 277 | for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++) |
8fc8598e | 278 | range->bitrate[i] = rtl8180_rates[i]; |
8fc8598e JC |
279 | |
280 | range->min_frag = MIN_FRAG_THRESHOLD; | |
281 | range->max_frag = MAX_FRAG_THRESHOLD; | |
282 | ||
654d1ce9 | 283 | range->min_pmp = 0; |
8fc8598e JC |
284 | range->max_pmp = 5000000; |
285 | range->min_pmt = 0; | |
286 | range->max_pmt = 65535*1000; | |
287 | range->pmp_flags = IW_POWER_PERIOD; | |
288 | range->pmt_flags = IW_POWER_TIMEOUT; | |
289 | range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; | |
290 | ||
291 | range->we_version_compiled = WIRELESS_EXT; | |
292 | range->we_version_source = 16; | |
293 | ||
0bb927cf CH |
294 | /* range->retry_capa; */ /* What retry options are supported */ |
295 | /* range->retry_flags; */ /* How to decode max/min retry limit */ | |
296 | /* range->r_time_flags; */ /* How to decode max/min retry life */ | |
0b5b4e22 | 297 | /* range->min_retry; */ /* Minimal number of retries */ |
0bb927cf CH |
298 | /* range->max_retry; */ /* Maximal number of retries */ |
299 | /* range->min_r_time; */ /* Minimal retry lifetime */ | |
300 | /* range->max_r_time; */ /* Maximal retry lifetime */ | |
8fc8598e JC |
301 | |
302 | ||
303 | for (i = 0, val = 0; i < 14; i++) { | |
304 | ||
0bb927cf | 305 | /* Include only legal frequencies for some countries */ |
8fc8598e | 306 | if ((GET_DOT11D_INFO(priv->ieee80211)->channel_map)[i+1]) { |
e406322b | 307 | range->freq[val].i = i + 1; |
8fc8598e JC |
308 | range->freq[val].m = ieee80211_wlan_frequencies[i] * 100000; |
309 | range->freq[val].e = 1; | |
310 | val++; | |
311 | } else { | |
0bb927cf CH |
312 | /* FIXME: do we need to set anything for channels */ |
313 | /* we don't use ? */ | |
8fc8598e JC |
314 | } |
315 | ||
316 | if (val == IW_MAX_FREQUENCIES) | |
28cda5ae | 317 | break; |
8fc8598e JC |
318 | } |
319 | range->num_frequency = val; | |
e406322b | 320 | range->num_channels = val; |
8fc8598e JC |
321 | range->enc_capa = IW_ENC_CAPA_WPA|IW_ENC_CAPA_WPA2| |
322 | IW_ENC_CAPA_CIPHER_TKIP|IW_ENC_CAPA_CIPHER_CCMP; | |
8fc8598e JC |
323 | tmp->scan_capa = 0x01; |
324 | return 0; | |
325 | } | |
326 | ||
327 | ||
328 | static int r8192_wx_set_scan(struct net_device *dev, struct iw_request_info *a, | |
329 | union iwreq_data *wrqu, char *b) | |
330 | { | |
331 | struct r8192_priv *priv = ieee80211_priv(dev); | |
b81c2b0a | 332 | struct ieee80211_device *ieee = priv->ieee80211; |
8fc8598e JC |
333 | int ret = 0; |
334 | ||
28cda5ae CH |
335 | if (!priv->up) |
336 | return -ENETDOWN; | |
8fc8598e | 337 | |
956ff821 | 338 | if (priv->ieee80211->LinkDetectInfo.bBusyTraffic) |
8fc8598e | 339 | return -EAGAIN; |
28cda5ae | 340 | if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { |
b81c2b0a | 341 | struct iw_scan_req *req = (struct iw_scan_req *)b; |
95c0bab2 | 342 | |
28cda5ae | 343 | if (req->essid_len) { |
8fc8598e JC |
344 | ieee->current_network.ssid_len = req->essid_len; |
345 | memcpy(ieee->current_network.ssid, req->essid, req->essid_len); | |
8fc8598e JC |
346 | } |
347 | } | |
348 | ||
349 | down(&priv->wx_sem); | |
654d1ce9 | 350 | if (priv->ieee80211->state != IEEE80211_LINKED) { |
e406322b MCC |
351 | priv->ieee80211->scanning = 0; |
352 | ieee80211_softmac_scan_syncro(priv->ieee80211); | |
353 | ret = 0; | |
28cda5ae CH |
354 | } else { |
355 | ret = ieee80211_wx_set_scan(priv->ieee80211, a, wrqu, b); | |
e406322b | 356 | } |
8fc8598e JC |
357 | up(&priv->wx_sem); |
358 | return ret; | |
359 | } | |
360 | ||
361 | ||
362 | static int r8192_wx_get_scan(struct net_device *dev, struct iw_request_info *a, | |
363 | union iwreq_data *wrqu, char *b) | |
364 | { | |
365 | ||
366 | int ret; | |
367 | struct r8192_priv *priv = ieee80211_priv(dev); | |
368 | ||
28cda5ae CH |
369 | if (!priv->up) |
370 | return -ENETDOWN; | |
8fc8598e JC |
371 | |
372 | down(&priv->wx_sem); | |
373 | ||
654d1ce9 | 374 | ret = ieee80211_wx_get_scan(priv->ieee80211, a, wrqu, b); |
8fc8598e JC |
375 | |
376 | up(&priv->wx_sem); | |
377 | ||
378 | return ret; | |
379 | } | |
380 | ||
381 | static int r8192_wx_set_essid(struct net_device *dev, | |
382 | struct iw_request_info *a, | |
383 | union iwreq_data *wrqu, char *b) | |
384 | { | |
385 | struct r8192_priv *priv = ieee80211_priv(dev); | |
386 | int ret; | |
a08d541a | 387 | |
8fc8598e JC |
388 | down(&priv->wx_sem); |
389 | ||
654d1ce9 | 390 | ret = ieee80211_wx_set_essid(priv->ieee80211, a, wrqu, b); |
8fc8598e JC |
391 | |
392 | up(&priv->wx_sem); | |
393 | ||
394 | return ret; | |
395 | } | |
396 | ||
397 | ||
398 | ||
399 | ||
400 | static int r8192_wx_get_essid(struct net_device *dev, | |
401 | struct iw_request_info *a, | |
402 | union iwreq_data *wrqu, char *b) | |
403 | { | |
404 | int ret; | |
405 | struct r8192_priv *priv = ieee80211_priv(dev); | |
406 | ||
407 | down(&priv->wx_sem); | |
408 | ||
409 | ret = ieee80211_wx_get_essid(priv->ieee80211, a, wrqu, b); | |
410 | ||
411 | up(&priv->wx_sem); | |
412 | ||
413 | return ret; | |
414 | } | |
415 | ||
416 | ||
417 | static int r8192_wx_set_freq(struct net_device *dev, struct iw_request_info *a, | |
418 | union iwreq_data *wrqu, char *b) | |
419 | { | |
420 | int ret; | |
421 | struct r8192_priv *priv = ieee80211_priv(dev); | |
422 | ||
423 | down(&priv->wx_sem); | |
424 | ||
425 | ret = ieee80211_wx_set_freq(priv->ieee80211, a, wrqu, b); | |
426 | ||
427 | up(&priv->wx_sem); | |
428 | return ret; | |
429 | } | |
430 | ||
431 | static int r8192_wx_get_name(struct net_device *dev, | |
432 | struct iw_request_info *info, | |
433 | union iwreq_data *wrqu, char *extra) | |
434 | { | |
435 | struct r8192_priv *priv = ieee80211_priv(dev); | |
a08d541a | 436 | |
8fc8598e JC |
437 | return ieee80211_wx_get_name(priv->ieee80211, info, wrqu, extra); |
438 | } | |
439 | ||
440 | ||
441 | static int r8192_wx_set_frag(struct net_device *dev, | |
442 | struct iw_request_info *info, | |
443 | union iwreq_data *wrqu, char *extra) | |
444 | { | |
445 | struct r8192_priv *priv = ieee80211_priv(dev); | |
446 | ||
447 | if (wrqu->frag.disabled) | |
448 | priv->ieee80211->fts = DEFAULT_FRAG_THRESHOLD; | |
449 | else { | |
450 | if (wrqu->frag.value < MIN_FRAG_THRESHOLD || | |
451 | wrqu->frag.value > MAX_FRAG_THRESHOLD) | |
452 | return -EINVAL; | |
453 | ||
454 | priv->ieee80211->fts = wrqu->frag.value & ~0x1; | |
455 | } | |
456 | ||
457 | return 0; | |
458 | } | |
459 | ||
460 | ||
461 | static int r8192_wx_get_frag(struct net_device *dev, | |
462 | struct iw_request_info *info, | |
463 | union iwreq_data *wrqu, char *extra) | |
464 | { | |
465 | struct r8192_priv *priv = ieee80211_priv(dev); | |
466 | ||
467 | wrqu->frag.value = priv->ieee80211->fts; | |
468 | wrqu->frag.fixed = 0; /* no auto select */ | |
469 | wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FRAG_THRESHOLD); | |
470 | ||
471 | return 0; | |
472 | } | |
473 | ||
474 | ||
475 | static int r8192_wx_set_wap(struct net_device *dev, | |
476 | struct iw_request_info *info, | |
477 | union iwreq_data *awrq, | |
478 | char *extra) | |
479 | { | |
480 | ||
481 | int ret; | |
482 | struct r8192_priv *priv = ieee80211_priv(dev); | |
0bb927cf | 483 | /* struct sockaddr *temp = (struct sockaddr *)awrq; */ |
8fc8598e JC |
484 | down(&priv->wx_sem); |
485 | ||
654d1ce9 | 486 | ret = ieee80211_wx_set_wap(priv->ieee80211, info, awrq, extra); |
8fc8598e JC |
487 | |
488 | up(&priv->wx_sem); | |
489 | ||
490 | return ret; | |
491 | ||
492 | } | |
493 | ||
494 | ||
495 | static int r8192_wx_get_wap(struct net_device *dev, | |
496 | struct iw_request_info *info, | |
497 | union iwreq_data *wrqu, char *extra) | |
498 | { | |
499 | struct r8192_priv *priv = ieee80211_priv(dev); | |
500 | ||
654d1ce9 | 501 | return ieee80211_wx_get_wap(priv->ieee80211, info, wrqu, extra); |
8fc8598e JC |
502 | } |
503 | ||
504 | ||
505 | static int r8192_wx_get_enc(struct net_device *dev, | |
506 | struct iw_request_info *info, | |
507 | union iwreq_data *wrqu, char *key) | |
508 | { | |
509 | struct r8192_priv *priv = ieee80211_priv(dev); | |
510 | ||
511 | return ieee80211_wx_get_encode(priv->ieee80211, info, wrqu, key); | |
512 | } | |
513 | ||
514 | static int r8192_wx_set_enc(struct net_device *dev, | |
515 | struct iw_request_info *info, | |
516 | union iwreq_data *wrqu, char *key) | |
517 | { | |
518 | struct r8192_priv *priv = ieee80211_priv(dev); | |
519 | struct ieee80211_device *ieee = priv->ieee80211; | |
520 | int ret; | |
654d1ce9 CH |
521 | u32 hwkey[4] = {0, 0, 0, 0}; |
522 | u8 mask = 0xff; | |
523 | u32 key_idx = 0; | |
654d1ce9 CH |
524 | u8 zero_addr[4][6] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, |
525 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, | |
526 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, | |
527 | {0x00, 0x00, 0x00, 0x00, 0x00, 0x03} }; | |
8fc8598e JC |
528 | int i; |
529 | ||
28cda5ae CH |
530 | if (!priv->up) |
531 | return -ENETDOWN; | |
8fc8598e JC |
532 | |
533 | down(&priv->wx_sem); | |
534 | ||
535 | RT_TRACE(COMP_SEC, "Setting SW wep key"); | |
654d1ce9 | 536 | ret = ieee80211_wx_set_encode(priv->ieee80211, info, wrqu, key); |
8fc8598e JC |
537 | |
538 | up(&priv->wx_sem); | |
539 | ||
540 | ||
541 | ||
0bb927cf | 542 | /* sometimes, the length is zero while we do not type key value */ |
654d1ce9 | 543 | if (wrqu->encoding.length != 0) { |
8fc8598e | 544 | |
654d1ce9 | 545 | for (i = 0; i < 4; i++) { |
8fc8598e | 546 | hwkey[i] |= key[4*i+0]&mask; |
28cda5ae CH |
547 | if (i == 1 && (4*i+1) == wrqu->encoding.length) |
548 | mask = 0x00; | |
549 | if (i == 3 && (4*i+1) == wrqu->encoding.length) | |
550 | mask = 0x00; | |
8fc8598e JC |
551 | hwkey[i] |= (key[4*i+1]&mask)<<8; |
552 | hwkey[i] |= (key[4*i+2]&mask)<<16; | |
553 | hwkey[i] |= (key[4*i+3]&mask)<<24; | |
554 | } | |
555 | ||
556 | #define CONF_WEP40 0x4 | |
557 | #define CONF_WEP104 0x14 | |
558 | ||
654d1ce9 | 559 | switch (wrqu->encoding.flags & IW_ENCODE_INDEX) { |
28cda5ae CH |
560 | case 0: |
561 | key_idx = ieee->tx_keyidx; | |
562 | break; | |
563 | case 1: | |
564 | key_idx = 0; | |
565 | break; | |
566 | case 2: | |
567 | key_idx = 1; | |
568 | break; | |
569 | case 3: | |
570 | key_idx = 2; | |
571 | break; | |
572 | case 4: | |
573 | key_idx = 3; | |
574 | break; | |
575 | default: | |
576 | break; | |
8fc8598e JC |
577 | } |
578 | ||
654d1ce9 | 579 | if (wrqu->encoding.length == 0x5) { |
8fc8598e JC |
580 | ieee->pairwise_key_type = KEY_TYPE_WEP40; |
581 | EnableHWSecurityConfig8192(dev); | |
582 | ||
654d1ce9 | 583 | setKey(dev, |
0bb927cf CH |
584 | key_idx, /* EntryNo */ |
585 | key_idx, /* KeyIndex */ | |
586 | KEY_TYPE_WEP40, /* KeyType */ | |
8fc8598e | 587 | zero_addr[key_idx], |
0bb927cf CH |
588 | 0, /* DefaultKey */ |
589 | hwkey); /* KeyContent */ | |
8fc8598e JC |
590 | |
591 | } | |
592 | ||
654d1ce9 | 593 | else if (wrqu->encoding.length == 0xd) { |
8fc8598e JC |
594 | ieee->pairwise_key_type = KEY_TYPE_WEP104; |
595 | EnableHWSecurityConfig8192(dev); | |
596 | ||
654d1ce9 | 597 | setKey(dev, |
0bb927cf CH |
598 | key_idx, /* EntryNo */ |
599 | key_idx, /* KeyIndex */ | |
600 | KEY_TYPE_WEP104, /* KeyType */ | |
8fc8598e | 601 | zero_addr[key_idx], |
0bb927cf CH |
602 | 0, /* DefaultKey */ |
603 | hwkey); /* KeyContent */ | |
8fc8598e | 604 | |
28cda5ae CH |
605 | } else { |
606 | printk("wrong type in WEP, not WEP40 and WEP104\n"); | |
8fc8598e | 607 | } |
8fc8598e JC |
608 | |
609 | } | |
610 | ||
611 | return ret; | |
612 | } | |
613 | ||
614 | ||
28cda5ae | 615 | static int r8192_wx_set_scan_type(struct net_device *dev, struct iw_request_info *aa, |
e00b8fdc CH |
616 | union iwreq_data *wrqu, char *p) |
617 | { | |
8fc8598e | 618 | |
e406322b | 619 | struct r8192_priv *priv = ieee80211_priv(dev); |
654d1ce9 CH |
620 | int *parms = (int *)p; |
621 | int mode = parms[0]; | |
8fc8598e JC |
622 | |
623 | priv->ieee80211->active_scan = mode; | |
624 | ||
625 | return 1; | |
626 | } | |
627 | ||
628 | ||
629 | ||
630 | static int r8192_wx_set_retry(struct net_device *dev, | |
631 | struct iw_request_info *info, | |
632 | union iwreq_data *wrqu, char *extra) | |
633 | { | |
634 | struct r8192_priv *priv = ieee80211_priv(dev); | |
635 | int err = 0; | |
636 | ||
637 | down(&priv->wx_sem); | |
638 | ||
639 | if (wrqu->retry.flags & IW_RETRY_LIFETIME || | |
640 | wrqu->retry.disabled){ | |
641 | err = -EINVAL; | |
642 | goto exit; | |
643 | } | |
654d1ce9 | 644 | if (!(wrqu->retry.flags & IW_RETRY_LIMIT)) { |
8fc8598e JC |
645 | err = -EINVAL; |
646 | goto exit; | |
647 | } | |
648 | ||
654d1ce9 CH |
649 | if (wrqu->retry.value > R8180_MAX_RETRY) { |
650 | err = -EINVAL; | |
8fc8598e JC |
651 | goto exit; |
652 | } | |
653 | if (wrqu->retry.flags & IW_RETRY_MAX) { | |
654 | priv->retry_rts = wrqu->retry.value; | |
655 | DMESG("Setting retry for RTS/CTS data to %d", wrqu->retry.value); | |
656 | ||
654d1ce9 | 657 | } else { |
8fc8598e JC |
658 | priv->retry_data = wrqu->retry.value; |
659 | DMESG("Setting retry for non RTS/CTS data to %d", wrqu->retry.value); | |
660 | } | |
661 | ||
662 | /* FIXME ! | |
663 | * We might try to write directly the TX config register | |
664 | * or to restart just the (R)TX process. | |
665 | * I'm unsure if whole reset is really needed | |
666 | */ | |
667 | ||
e406322b | 668 | rtl8192_commit(dev); |
8fc8598e JC |
669 | exit: |
670 | up(&priv->wx_sem); | |
671 | ||
672 | return err; | |
673 | } | |
674 | ||
675 | static int r8192_wx_get_retry(struct net_device *dev, | |
676 | struct iw_request_info *info, | |
677 | union iwreq_data *wrqu, char *extra) | |
678 | { | |
679 | struct r8192_priv *priv = ieee80211_priv(dev); | |
680 | ||
681 | ||
682 | wrqu->retry.disabled = 0; /* can't be disabled */ | |
683 | ||
684 | if ((wrqu->retry.flags & IW_RETRY_TYPE) == | |
685 | IW_RETRY_LIFETIME) | |
686 | return -EINVAL; | |
687 | ||
688 | if (wrqu->retry.flags & IW_RETRY_MAX) { | |
689 | wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX; | |
690 | wrqu->retry.value = priv->retry_rts; | |
691 | } else { | |
692 | wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN; | |
693 | wrqu->retry.value = priv->retry_data; | |
694 | } | |
8fc8598e JC |
695 | |
696 | ||
697 | return 0; | |
698 | } | |
699 | ||
700 | static int r8192_wx_get_sens(struct net_device *dev, | |
701 | struct iw_request_info *info, | |
702 | union iwreq_data *wrqu, char *extra) | |
703 | { | |
704 | struct r8192_priv *priv = ieee80211_priv(dev); | |
a08d541a | 705 | |
654d1ce9 | 706 | if (priv->rf_set_sens == NULL) |
8fc8598e JC |
707 | return -1; /* we have not this support for this radio */ |
708 | wrqu->sens.value = priv->sens; | |
709 | return 0; | |
710 | } | |
711 | ||
712 | ||
713 | static int r8192_wx_set_sens(struct net_device *dev, | |
714 | struct iw_request_info *info, | |
715 | union iwreq_data *wrqu, char *extra) | |
716 | { | |
717 | ||
718 | struct r8192_priv *priv = ieee80211_priv(dev); | |
8fc8598e | 719 | short err = 0; |
a08d541a | 720 | |
8fc8598e | 721 | down(&priv->wx_sem); |
654d1ce9 CH |
722 | if (priv->rf_set_sens == NULL) { |
723 | err = -1; /* we have not this support for this radio */ | |
8fc8598e JC |
724 | goto exit; |
725 | } | |
654d1ce9 | 726 | if (priv->rf_set_sens(dev, wrqu->sens.value) == 0) |
8fc8598e JC |
727 | priv->sens = wrqu->sens.value; |
728 | else | |
654d1ce9 | 729 | err = -EINVAL; |
8fc8598e JC |
730 | |
731 | exit: | |
732 | up(&priv->wx_sem); | |
733 | ||
734 | return err; | |
735 | } | |
736 | ||
0bb927cf | 737 | /* hw security need to reorganized. */ |
8fc8598e | 738 | static int r8192_wx_set_enc_ext(struct net_device *dev, |
e406322b MCC |
739 | struct iw_request_info *info, |
740 | union iwreq_data *wrqu, char *extra) | |
8fc8598e | 741 | { |
654d1ce9 | 742 | int ret = 0; |
8fc8598e | 743 | struct r8192_priv *priv = ieee80211_priv(dev); |
b81c2b0a | 744 | struct ieee80211_device *ieee = priv->ieee80211; |
8fc8598e JC |
745 | |
746 | ||
747 | down(&priv->wx_sem); | |
748 | ret = ieee80211_wx_set_encode_ext(priv->ieee80211, info, wrqu, extra); | |
749 | ||
750 | { | |
654d1ce9 | 751 | u8 broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
8fc8598e JC |
752 | u8 zero[6] = {0}; |
753 | u32 key[4] = {0}; | |
754 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | |
755 | struct iw_point *encoding = &wrqu->encoding; | |
8fc8598e | 756 | u8 idx = 0, alg = 0, group = 0; |
95c0bab2 | 757 | |
28cda5ae | 758 | if ((encoding->flags & IW_ENCODE_DISABLED) || ext->alg == IW_ENCODE_ALG_NONE) |
0bb927cf | 759 | /* none is not allowed to use hwsec WB 2008.07.01 */ |
8fc8598e JC |
760 | goto end_hw_sec; |
761 | ||
0bb927cf | 762 | /* as IW_ENCODE_ALG_CCMP is defined to be 3 and KEY_TYPE_CCMP is defined to 4; */ |
28cda5ae | 763 | alg = (ext->alg == IW_ENCODE_ALG_CCMP)?KEY_TYPE_CCMP:ext->alg; |
8fc8598e JC |
764 | idx = encoding->flags & IW_ENCODE_INDEX; |
765 | if (idx) | |
654d1ce9 | 766 | idx--; |
8fc8598e JC |
767 | group = ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY; |
768 | ||
28cda5ae | 769 | if ((!group) || (IW_MODE_ADHOC == ieee->iw_mode) || (alg == KEY_TYPE_WEP40)) { |
654d1ce9 | 770 | if ((ext->key_len == 13) && (alg == KEY_TYPE_WEP40)) |
8fc8598e JC |
771 | alg = KEY_TYPE_WEP104; |
772 | ieee->pairwise_key_type = alg; | |
773 | EnableHWSecurityConfig8192(dev); | |
774 | } | |
0bb927cf | 775 | memcpy((u8 *)key, ext->key, 16); /* we only get 16 bytes key.why? WB 2008.7.1 */ |
8fc8598e | 776 | |
28cda5ae | 777 | if ((alg & KEY_TYPE_WEP40) && (ieee->auth_mode != 2)) { |
8fc8598e | 778 | |
654d1ce9 | 779 | setKey(dev, |
0bb927cf CH |
780 | idx, /* EntryNao */ |
781 | idx, /* KeyIndex */ | |
782 | alg, /* KeyType */ | |
783 | zero, /* MacAddr */ | |
784 | 0, /* DefaultKey */ | |
785 | key); /* KeyContent */ | |
28cda5ae | 786 | } else if (group) { |
8fc8598e | 787 | ieee->group_key_type = alg; |
654d1ce9 | 788 | setKey(dev, |
0bb927cf CH |
789 | idx, /* EntryNo */ |
790 | idx, /* KeyIndex */ | |
791 | alg, /* KeyType */ | |
792 | broadcast_addr, /* MacAddr */ | |
793 | 0, /* DefaultKey */ | |
794 | key); /* KeyContent */ | |
795 | } else { /* pairwise key */ | |
654d1ce9 | 796 | setKey(dev, |
0bb927cf CH |
797 | 4, /* EntryNo */ |
798 | idx, /* KeyIndex */ | |
799 | alg, /* KeyType */ | |
800 | (u8 *)ieee->ap_mac_addr,/* MacAddr */ | |
801 | 0, /* DefaultKey */ | |
0b5b4e22 | 802 | key); /* KeyContent */ |
8fc8598e JC |
803 | } |
804 | ||
805 | ||
806 | } | |
807 | ||
808 | end_hw_sec: | |
809 | ||
810 | up(&priv->wx_sem); | |
8fc8598e JC |
811 | return ret; |
812 | ||
813 | } | |
814 | static int r8192_wx_set_auth(struct net_device *dev, | |
e406322b MCC |
815 | struct iw_request_info *info, |
816 | union iwreq_data *data, char *extra) | |
8fc8598e | 817 | { |
654d1ce9 | 818 | int ret = 0; |
8fc8598e | 819 | struct r8192_priv *priv = ieee80211_priv(dev); |
a08d541a | 820 | |
8fc8598e JC |
821 | down(&priv->wx_sem); |
822 | ret = ieee80211_wx_set_auth(priv->ieee80211, info, &(data->param), extra); | |
823 | up(&priv->wx_sem); | |
8fc8598e JC |
824 | return ret; |
825 | } | |
826 | ||
827 | static int r8192_wx_set_mlme(struct net_device *dev, | |
e406322b MCC |
828 | struct iw_request_info *info, |
829 | union iwreq_data *wrqu, char *extra) | |
8fc8598e | 830 | { |
8fc8598e | 831 | |
654d1ce9 | 832 | int ret = 0; |
8fc8598e | 833 | struct r8192_priv *priv = ieee80211_priv(dev); |
a08d541a | 834 | |
8fc8598e JC |
835 | down(&priv->wx_sem); |
836 | ret = ieee80211_wx_set_mlme(priv->ieee80211, info, wrqu, extra); | |
837 | ||
838 | up(&priv->wx_sem); | |
8fc8598e JC |
839 | return ret; |
840 | } | |
e6c1ef6c | 841 | |
8fc8598e | 842 | static int r8192_wx_set_gen_ie(struct net_device *dev, |
e406322b MCC |
843 | struct iw_request_info *info, |
844 | union iwreq_data *data, char *extra) | |
8fc8598e | 845 | { |
654d1ce9 | 846 | int ret = 0; |
e406322b | 847 | struct r8192_priv *priv = ieee80211_priv(dev); |
a08d541a | 848 | |
e406322b | 849 | down(&priv->wx_sem); |
e406322b | 850 | ret = ieee80211_wx_set_gen_ie(priv->ieee80211, extra, data->data.length); |
e406322b | 851 | up(&priv->wx_sem); |
e406322b | 852 | return ret; |
8fc8598e JC |
853 | |
854 | ||
855 | } | |
856 | ||
857 | static int dummy(struct net_device *dev, struct iw_request_info *a, | |
654d1ce9 | 858 | union iwreq_data *wrqu, char *b) |
8fc8598e JC |
859 | { |
860 | return -1; | |
861 | } | |
862 | ||
863 | ||
28cda5ae | 864 | static iw_handler r8192_wx_handlers[] = { |
e406322b | 865 | NULL, /* SIOCSIWCOMMIT */ |
35997ff0 | 866 | r8192_wx_get_name, /* SIOCGIWNAME */ |
e406322b MCC |
867 | dummy, /* SIOCSIWNWID */ |
868 | dummy, /* SIOCGIWNWID */ | |
869 | r8192_wx_set_freq, /* SIOCSIWFREQ */ | |
870 | r8192_wx_get_freq, /* SIOCGIWFREQ */ | |
871 | r8192_wx_set_mode, /* SIOCSIWMODE */ | |
872 | r8192_wx_get_mode, /* SIOCGIWMODE */ | |
873 | r8192_wx_set_sens, /* SIOCSIWSENS */ | |
874 | r8192_wx_get_sens, /* SIOCGIWSENS */ | |
875 | NULL, /* SIOCSIWRANGE */ | |
876 | rtl8180_wx_get_range, /* SIOCGIWRANGE */ | |
877 | NULL, /* SIOCSIWPRIV */ | |
878 | NULL, /* SIOCGIWPRIV */ | |
879 | NULL, /* SIOCSIWSTATS */ | |
880 | NULL, /* SIOCGIWSTATS */ | |
881 | dummy, /* SIOCSIWSPY */ | |
882 | dummy, /* SIOCGIWSPY */ | |
883 | NULL, /* SIOCGIWTHRSPY */ | |
884 | NULL, /* SIOCWIWTHRSPY */ | |
35997ff0 | 885 | r8192_wx_set_wap, /* SIOCSIWAP */ |
e406322b | 886 | r8192_wx_get_wap, /* SIOCGIWAP */ |
e406322b | 887 | r8192_wx_set_mlme, /* MLME-- */ |
589b3d06 | 888 | dummy, /* SIOCGIWAPLIST -- deprecated */ |
e406322b MCC |
889 | r8192_wx_set_scan, /* SIOCSIWSCAN */ |
890 | r8192_wx_get_scan, /* SIOCGIWSCAN */ | |
891 | r8192_wx_set_essid, /* SIOCSIWESSID */ | |
892 | r8192_wx_get_essid, /* SIOCGIWESSID */ | |
893 | dummy, /* SIOCSIWNICKN */ | |
894 | dummy, /* SIOCGIWNICKN */ | |
895 | NULL, /* -- hole -- */ | |
896 | NULL, /* -- hole -- */ | |
897 | r8192_wx_set_rate, /* SIOCSIWRATE */ | |
898 | r8192_wx_get_rate, /* SIOCGIWRATE */ | |
899 | r8192_wx_set_rts, /* SIOCSIWRTS */ | |
900 | r8192_wx_get_rts, /* SIOCGIWRTS */ | |
901 | r8192_wx_set_frag, /* SIOCSIWFRAG */ | |
902 | r8192_wx_get_frag, /* SIOCGIWFRAG */ | |
903 | dummy, /* SIOCSIWTXPOW */ | |
904 | dummy, /* SIOCGIWTXPOW */ | |
905 | r8192_wx_set_retry, /* SIOCSIWRETRY */ | |
906 | r8192_wx_get_retry, /* SIOCGIWRETRY */ | |
907 | r8192_wx_set_enc, /* SIOCSIWENCODE */ | |
908 | r8192_wx_get_enc, /* SIOCGIWENCODE */ | |
909 | r8192_wx_set_power, /* SIOCSIWPOWER */ | |
910 | r8192_wx_get_power, /* SIOCGIWPOWER */ | |
8fc8598e | 911 | NULL, /*---hole---*/ |
35997ff0 | 912 | NULL, /*---hole---*/ |
0bb927cf | 913 | r8192_wx_set_gen_ie, /* NULL, */ /* SIOCSIWGENIE */ |
35997ff0 | 914 | NULL, /* SIOCSIWGENIE */ |
8fc8598e | 915 | |
0bb927cf CH |
916 | r8192_wx_set_auth,/* NULL, */ /* SIOCSIWAUTH */ |
917 | NULL,/* r8192_wx_get_auth, */ /* NULL, */ /* SIOCSIWAUTH */ | |
35997ff0 | 918 | r8192_wx_set_enc_ext, /* SIOCSIWENCODEEXT */ |
0bb927cf | 919 | NULL,/* r8192_wx_get_enc_ext, *//* NULL, */ /* SIOCSIWENCODEEXT */ |
35997ff0 SH |
920 | NULL, /* SIOCSIWPMKSA */ |
921 | NULL, /*---hole---*/ | |
8fc8598e JC |
922 | |
923 | }; | |
924 | ||
925 | ||
926 | static const struct iw_priv_args r8192_private_args[] = { | |
927 | ||
928 | { | |
929 | SIOCIWFIRSTPRIV + 0x0, | |
930 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "badcrc" | |
931 | }, | |
932 | ||
933 | { | |
934 | SIOCIWFIRSTPRIV + 0x1, | |
935 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "activescan" | |
936 | ||
937 | }, | |
938 | { | |
939 | SIOCIWFIRSTPRIV + 0x2, | |
940 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rawtx" | |
aa0cb59c | 941 | }, |
8fc8598e JC |
942 | { |
943 | SIOCIWFIRSTPRIV + 0x3, | |
944 | IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "forcereset" | |
945 | ||
946 | } | |
947 | ||
948 | }; | |
949 | ||
950 | ||
951 | static iw_handler r8192_private_handler[] = { | |
a2581a4a | 952 | r8192_wx_set_crcmon, |
8fc8598e JC |
953 | r8192_wx_set_scan_type, |
954 | r8192_wx_set_rawtx, | |
8fc8598e JC |
955 | r8192_wx_force_reset, |
956 | }; | |
957 | ||
8fc8598e JC |
958 | struct iw_statistics *r8192_get_wireless_stats(struct net_device *dev) |
959 | { | |
654d1ce9 | 960 | struct r8192_priv *priv = ieee80211_priv(dev); |
b81c2b0a XR |
961 | struct ieee80211_device *ieee = priv->ieee80211; |
962 | struct iw_statistics *wstats = &priv->wstats; | |
8fc8598e JC |
963 | int tmp_level = 0; |
964 | int tmp_qual = 0; | |
965 | int tmp_noise = 0; | |
a08d541a | 966 | |
28cda5ae | 967 | if (ieee->state < IEEE80211_LINKED) { |
8fc8598e JC |
968 | wstats->qual.qual = 0; |
969 | wstats->qual.level = 0; | |
970 | wstats->qual.noise = 0; | |
8fc8598e | 971 | wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; |
8fc8598e JC |
972 | return wstats; |
973 | } | |
974 | ||
654d1ce9 | 975 | tmp_level = (&ieee->current_network)->stats.rssi; |
8fc8598e JC |
976 | tmp_qual = (&ieee->current_network)->stats.signal; |
977 | tmp_noise = (&ieee->current_network)->stats.noise; | |
8fc8598e JC |
978 | |
979 | wstats->qual.level = tmp_level; | |
980 | wstats->qual.qual = tmp_qual; | |
981 | wstats->qual.noise = tmp_noise; | |
31c207f4 | 982 | wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; |
8fc8598e JC |
983 | return wstats; |
984 | } | |
8fc8598e JC |
985 | |
986 | ||
654d1ce9 | 987 | struct iw_handler_def r8192_wx_handlers_def = { |
8fc8598e | 988 | .standard = r8192_wx_handlers, |
b330f606 | 989 | .num_standard = ARRAY_SIZE(r8192_wx_handlers), |
8fc8598e | 990 | .private = r8192_private_handler, |
b330f606 | 991 | .num_private = ARRAY_SIZE(r8192_private_handler), |
e406322b | 992 | .num_private_args = sizeof(r8192_private_args) / sizeof(struct iw_priv_args), |
8fc8598e | 993 | .get_wireless_stats = r8192_get_wireless_stats, |
8fc8598e JC |
994 | .private_args = (struct iw_priv_args *)r8192_private_args, |
995 | }; |