Commit | Line | Data |
---|---|---|
cb3126e6 KR |
1 | /* cfg80211 Interface for prism2_usb module */ |
2 | ||
3 | ||
d34602de | 4 | /* Prism2 channel/frequency/bitrate declarations */ |
cb3126e6 KR |
5 | static const struct ieee80211_channel prism2_channels[] = { |
6 | { .center_freq = 2412 }, | |
7 | { .center_freq = 2417 }, | |
8 | { .center_freq = 2422 }, | |
9 | { .center_freq = 2427 }, | |
10 | { .center_freq = 2432 }, | |
11 | { .center_freq = 2437 }, | |
12 | { .center_freq = 2442 }, | |
13 | { .center_freq = 2447 }, | |
14 | { .center_freq = 2452 }, | |
15 | { .center_freq = 2457 }, | |
16 | { .center_freq = 2462 }, | |
17 | { .center_freq = 2467 }, | |
18 | { .center_freq = 2472 }, | |
19 | { .center_freq = 2484 }, | |
20 | }; | |
21 | ||
22 | static const struct ieee80211_rate prism2_rates[] = { | |
23 | { .bitrate = 10 }, | |
24 | { .bitrate = 20 }, | |
25 | { .bitrate = 55 }, | |
26 | { .bitrate = 110 } | |
27 | }; | |
28 | ||
29 | #define PRISM2_NUM_CIPHER_SUITES 2 | |
30 | static const u32 prism2_cipher_suites[PRISM2_NUM_CIPHER_SUITES] = { | |
31 | WLAN_CIPHER_SUITE_WEP40, | |
32 | WLAN_CIPHER_SUITE_WEP104 | |
33 | }; | |
34 | ||
35 | ||
36 | /* prism2 device private data */ | |
37 | struct prism2_wiphy_private { | |
38 | wlandevice_t *wlandev; | |
39 | ||
40 | struct ieee80211_supported_band band; | |
41 | struct ieee80211_channel channels[ARRAY_SIZE(prism2_channels)]; | |
42 | struct ieee80211_rate rates[ARRAY_SIZE(prism2_rates)]; | |
43 | ||
44 | struct cfg80211_scan_request *scan_request; | |
45 | }; | |
46 | ||
47 | static const void * const prism2_wiphy_privid = &prism2_wiphy_privid; | |
48 | ||
49 | ||
50 | /* Helper Functions */ | |
51 | static int prism2_result2err(int prism2_result) | |
52 | { | |
53 | int err = 0; | |
54 | ||
55 | switch (prism2_result) { | |
56 | case P80211ENUM_resultcode_invalid_parameters: | |
57 | err = -EINVAL; | |
58 | break; | |
59 | case P80211ENUM_resultcode_implementation_failure: | |
60 | err = -EIO; | |
61 | break; | |
62 | case P80211ENUM_resultcode_not_supported: | |
63 | err = -EOPNOTSUPP; | |
64 | break; | |
65 | default: | |
66 | err = 0; | |
67 | break; | |
68 | } | |
69 | ||
70 | return err; | |
71 | } | |
72 | ||
73 | static int prism2_domibset_uint32(wlandevice_t *wlandev, u32 did, u32 data) | |
74 | { | |
b6bb56e6 | 75 | struct p80211msg_dot11req_mibset msg; |
bb1da756 HS |
76 | p80211item_uint32_t *mibitem = |
77 | (p80211item_uint32_t *) &msg.mibattribute.data; | |
cb3126e6 KR |
78 | |
79 | msg.msgcode = DIDmsg_dot11req_mibset; | |
80 | mibitem->did = did; | |
81 | mibitem->data = data; | |
82 | ||
8dd82ebe | 83 | return p80211req_dorequest(wlandev, (u8 *) &msg); |
cb3126e6 KR |
84 | } |
85 | ||
86 | static int prism2_domibset_pstr32(wlandevice_t *wlandev, | |
87 | u32 did, u8 len, u8 *data) | |
88 | { | |
b6bb56e6 | 89 | struct p80211msg_dot11req_mibset msg; |
bb1da756 HS |
90 | p80211item_pstr32_t *mibitem = |
91 | (p80211item_pstr32_t *) &msg.mibattribute.data; | |
cb3126e6 KR |
92 | |
93 | msg.msgcode = DIDmsg_dot11req_mibset; | |
94 | mibitem->did = did; | |
95 | mibitem->data.len = len; | |
96 | memcpy(mibitem->data.data, data, len); | |
97 | ||
8dd82ebe | 98 | return p80211req_dorequest(wlandev, (u8 *) &msg); |
cb3126e6 KR |
99 | } |
100 | ||
101 | ||
102 | /* The interface functions, called by the cfg80211 layer */ | |
55da06eb TB |
103 | static int prism2_change_virtual_intf(struct wiphy *wiphy, |
104 | struct net_device *dev, | |
105 | enum nl80211_iftype type, u32 *flags, | |
106 | struct vif_params *params) | |
cb3126e6 KR |
107 | { |
108 | wlandevice_t *wlandev = dev->ml_priv; | |
109 | u32 data; | |
110 | int result; | |
111 | int err = 0; | |
112 | ||
113 | switch (type) { | |
114 | case NL80211_IFTYPE_ADHOC: | |
8dd82ebe EH |
115 | if (wlandev->macmode == WLAN_MACMODE_IBSS_STA) |
116 | goto exit; | |
cb3126e6 KR |
117 | wlandev->macmode = WLAN_MACMODE_IBSS_STA; |
118 | data = 0; | |
119 | break; | |
120 | case NL80211_IFTYPE_STATION: | |
8dd82ebe EH |
121 | if (wlandev->macmode == WLAN_MACMODE_ESS_STA) |
122 | goto exit; | |
cb3126e6 KR |
123 | wlandev->macmode = WLAN_MACMODE_ESS_STA; |
124 | data = 1; | |
125 | break; | |
126 | default: | |
eed88971 | 127 | netdev_warn(dev, "Operation mode: %d not support\n", type); |
cb3126e6 KR |
128 | return -EOPNOTSUPP; |
129 | } | |
130 | ||
131 | /* Set Operation mode to the PORT TYPE RID */ | |
8aac4d44 DN |
132 | result = prism2_domibset_uint32(wlandev, |
133 | DIDmib_p2_p2Static_p2CnfPortType, | |
134 | data); | |
cb3126e6 KR |
135 | |
136 | if (result) | |
137 | err = -EFAULT; | |
8dd82ebe | 138 | |
cb3126e6 KR |
139 | dev->ieee80211_ptr->iftype = type; |
140 | ||
141 | exit: | |
142 | return err; | |
143 | } | |
144 | ||
55da06eb TB |
145 | static int prism2_add_key(struct wiphy *wiphy, struct net_device *dev, |
146 | u8 key_index, bool pairwise, const u8 *mac_addr, | |
147 | struct key_params *params) | |
8dd82ebe | 148 | { |
cb3126e6 KR |
149 | wlandevice_t *wlandev = dev->ml_priv; |
150 | u32 did; | |
151 | ||
152 | int err = 0; | |
153 | int result = 0; | |
154 | ||
155 | switch (params->cipher) { | |
156 | case WLAN_CIPHER_SUITE_WEP40: | |
157 | case WLAN_CIPHER_SUITE_WEP104: | |
158 | result = prism2_domibset_uint32(wlandev, | |
8dd82ebe EH |
159 | DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID, |
160 | key_index); | |
161 | if (result) | |
162 | goto exit; | |
cb3126e6 KR |
163 | |
164 | /* send key to driver */ | |
165 | switch (key_index) { | |
166 | case 0: | |
8dd82ebe | 167 | did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0; |
cb3126e6 KR |
168 | break; |
169 | ||
170 | case 1: | |
8dd82ebe | 171 | did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1; |
cb3126e6 KR |
172 | break; |
173 | ||
174 | case 2: | |
8dd82ebe | 175 | did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2; |
cb3126e6 KR |
176 | break; |
177 | ||
178 | case 3: | |
8dd82ebe | 179 | did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3; |
cb3126e6 KR |
180 | break; |
181 | ||
182 | default: | |
183 | err = -EINVAL; | |
184 | goto exit; | |
185 | } | |
186 | ||
bb1da756 HS |
187 | result = prism2_domibset_pstr32(wlandev, did, |
188 | params->key_len, params->key); | |
8dd82ebe EH |
189 | if (result) |
190 | goto exit; | |
cb3126e6 KR |
191 | break; |
192 | ||
193 | default: | |
194 | pr_debug("Unsupported cipher suite\n"); | |
195 | result = 1; | |
196 | } | |
197 | ||
198 | exit: | |
8dd82ebe EH |
199 | if (result) |
200 | err = -EFAULT; | |
cb3126e6 KR |
201 | |
202 | return err; | |
203 | } | |
204 | ||
55da06eb TB |
205 | static int prism2_get_key(struct wiphy *wiphy, struct net_device *dev, |
206 | u8 key_index, bool pairwise, | |
207 | const u8 *mac_addr, void *cookie, | |
208 | void (*callback)(void *cookie, struct key_params*)) | |
8dd82ebe | 209 | { |
cb3126e6 KR |
210 | wlandevice_t *wlandev = dev->ml_priv; |
211 | struct key_params params; | |
212 | int len; | |
213 | ||
8dd82ebe EH |
214 | if (key_index >= NUM_WEPKEYS) |
215 | return -EINVAL; | |
cb3126e6 KR |
216 | |
217 | len = wlandev->wep_keylens[key_index]; | |
218 | memset(¶ms, 0, sizeof(params)); | |
219 | ||
8dd82ebe | 220 | if (len == 13) |
cb3126e6 | 221 | params.cipher = WLAN_CIPHER_SUITE_WEP104; |
8dd82ebe | 222 | else if (len == 5) |
cb3126e6 | 223 | params.cipher = WLAN_CIPHER_SUITE_WEP104; |
8dd82ebe EH |
224 | else |
225 | return -ENOENT; | |
cb3126e6 KR |
226 | params.key_len = len; |
227 | params.key = wlandev->wep_keys[key_index]; | |
aff3ea4e | 228 | params.seq_len = 0; |
cb3126e6 KR |
229 | |
230 | callback(cookie, ¶ms); | |
8dd82ebe | 231 | |
cb3126e6 KR |
232 | return 0; |
233 | } | |
234 | ||
55da06eb TB |
235 | static int prism2_del_key(struct wiphy *wiphy, struct net_device *dev, |
236 | u8 key_index, bool pairwise, const u8 *mac_addr) | |
8dd82ebe | 237 | { |
cb3126e6 KR |
238 | wlandevice_t *wlandev = dev->ml_priv; |
239 | u32 did; | |
240 | int err = 0; | |
241 | int result = 0; | |
242 | ||
243 | /* There is no direct way in the hardware (AFAIK) of removing | |
244 | a key, so we will cheat by setting the key to a bogus value */ | |
245 | /* send key to driver */ | |
246 | switch (key_index) { | |
247 | case 0: | |
248 | did = | |
249 | DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0; | |
250 | break; | |
251 | ||
252 | case 1: | |
253 | did = | |
254 | DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1; | |
255 | break; | |
256 | ||
257 | case 2: | |
258 | did = | |
259 | DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2; | |
260 | break; | |
261 | ||
262 | case 3: | |
263 | did = | |
264 | DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3; | |
265 | break; | |
266 | ||
267 | default: | |
268 | err = -EINVAL; | |
269 | goto exit; | |
270 | } | |
271 | ||
272 | result = prism2_domibset_pstr32(wlandev, did, 13, "0000000000000"); | |
273 | ||
274 | exit: | |
8dd82ebe EH |
275 | if (result) |
276 | err = -EFAULT; | |
cb3126e6 KR |
277 | |
278 | return err; | |
279 | } | |
280 | ||
55da06eb TB |
281 | static int prism2_set_default_key(struct wiphy *wiphy, struct net_device *dev, |
282 | u8 key_index, bool unicast, bool multicast) | |
8dd82ebe | 283 | { |
cb3126e6 KR |
284 | wlandevice_t *wlandev = dev->ml_priv; |
285 | ||
286 | int err = 0; | |
287 | int result = 0; | |
288 | ||
289 | result = prism2_domibset_uint32(wlandev, | |
290 | DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID, | |
291 | key_index); | |
292 | ||
8dd82ebe EH |
293 | if (result) |
294 | err = -EFAULT; | |
cb3126e6 KR |
295 | |
296 | return err; | |
297 | } | |
298 | ||
299 | ||
55da06eb | 300 | static int prism2_get_station(struct wiphy *wiphy, struct net_device *dev, |
3b3a0162 | 301 | const u8 *mac, struct station_info *sinfo) |
8dd82ebe | 302 | { |
cb3126e6 | 303 | wlandevice_t *wlandev = dev->ml_priv; |
b6bb56e6 | 304 | struct p80211msg_lnxreq_commsquality quality; |
cb3126e6 KR |
305 | int result; |
306 | ||
307 | memset(sinfo, 0, sizeof(*sinfo)); | |
308 | ||
309 | if ((wlandev == NULL) || (wlandev->msdstate != WLAN_MSD_RUNNING)) | |
310 | return -EOPNOTSUPP; | |
311 | ||
312 | /* build request message */ | |
313 | quality.msgcode = DIDmsg_lnxreq_commsquality; | |
314 | quality.dbm.data = P80211ENUM_truth_true; | |
315 | quality.dbm.status = P80211ENUM_msgitem_status_data_ok; | |
316 | ||
317 | /* send message to nsd */ | |
318 | if (wlandev->mlmerequest == NULL) | |
319 | return -EOPNOTSUPP; | |
320 | ||
3d049431 | 321 | result = wlandev->mlmerequest(wlandev, (struct p80211msg *) &quality); |
cb3126e6 KR |
322 | |
323 | ||
324 | if (result == 0) { | |
325 | sinfo->txrate.legacy = quality.txrate.data; | |
326 | sinfo->filled |= STATION_INFO_TX_BITRATE; | |
327 | sinfo->signal = quality.level.data; | |
328 | sinfo->filled |= STATION_INFO_SIGNAL; | |
329 | } | |
330 | ||
331 | return result; | |
332 | } | |
333 | ||
bb1da756 HS |
334 | static int prism2_scan(struct wiphy *wiphy, |
335 | struct cfg80211_scan_request *request) | |
cb3126e6 | 336 | { |
5d5d7c3b | 337 | struct net_device *dev; |
cb3126e6 | 338 | struct prism2_wiphy_private *priv = wiphy_priv(wiphy); |
5d5d7c3b | 339 | wlandevice_t *wlandev; |
b6bb56e6 EH |
340 | struct p80211msg_dot11req_scan msg1; |
341 | struct p80211msg_dot11req_scan_results msg2; | |
19f798ad | 342 | struct cfg80211_bss *bss; |
cb3126e6 KR |
343 | int result; |
344 | int err = 0; | |
345 | int numbss = 0; | |
346 | int i = 0; | |
347 | u8 ie_buf[46]; | |
348 | int ie_len; | |
349 | ||
350 | if (!request) | |
351 | return -EINVAL; | |
352 | ||
5d5d7c3b EG |
353 | dev = request->wdev->netdev; |
354 | wlandev = dev->ml_priv; | |
355 | ||
cb3126e6 KR |
356 | if (priv->scan_request && priv->scan_request != request) |
357 | return -EBUSY; | |
358 | ||
359 | if (wlandev->macmode == WLAN_MACMODE_ESS_AP) { | |
eed88971 | 360 | netdev_err(dev, "Can't scan in AP mode\n"); |
cb3126e6 KR |
361 | return -EOPNOTSUPP; |
362 | } | |
363 | ||
364 | priv->scan_request = request; | |
365 | ||
b6bb56e6 | 366 | memset(&msg1, 0x00, sizeof(struct p80211msg_dot11req_scan)); |
cb3126e6 KR |
367 | msg1.msgcode = DIDmsg_dot11req_scan; |
368 | msg1.bsstype.data = P80211ENUM_bsstype_any; | |
369 | ||
625aeb3a | 370 | memset(&msg1.bssid.data.data, 0xFF, sizeof(msg1.bssid.data.data)); |
cb3126e6 KR |
371 | msg1.bssid.data.len = 6; |
372 | ||
373 | if (request->n_ssids > 0) { | |
374 | msg1.scantype.data = P80211ENUM_scantype_active; | |
375 | msg1.ssid.data.len = request->ssids->ssid_len; | |
8aac4d44 DN |
376 | memcpy(msg1.ssid.data.data, |
377 | request->ssids->ssid, request->ssids->ssid_len); | |
cb3126e6 KR |
378 | } else { |
379 | msg1.scantype.data = 0; | |
380 | } | |
381 | msg1.probedelay.data = 0; | |
382 | ||
383 | for (i = 0; | |
384 | (i < request->n_channels) && i < ARRAY_SIZE(prism2_channels); | |
385 | i++) | |
386 | msg1.channellist.data.data[i] = | |
bb1da756 HS |
387 | ieee80211_frequency_to_channel( |
388 | request->channels[i]->center_freq); | |
cb3126e6 KR |
389 | msg1.channellist.data.len = request->n_channels; |
390 | ||
391 | msg1.maxchanneltime.data = 250; | |
392 | msg1.minchanneltime.data = 200; | |
393 | ||
394 | result = p80211req_dorequest(wlandev, (u8 *) &msg1); | |
395 | if (result) { | |
396 | err = prism2_result2err(msg1.resultcode.data); | |
397 | goto exit; | |
398 | } | |
399 | /* Now retrieve scan results */ | |
400 | numbss = msg1.numbss.data; | |
401 | ||
402 | for (i = 0; i < numbss; i++) { | |
4e5e9d7c ZG |
403 | int freq; |
404 | ||
cb3126e6 KR |
405 | memset(&msg2, 0, sizeof(msg2)); |
406 | msg2.msgcode = DIDmsg_dot11req_scan_results; | |
407 | msg2.bssindex.data = i; | |
408 | ||
409 | result = p80211req_dorequest(wlandev, (u8 *) &msg2); | |
410 | if ((result != 0) || | |
411 | (msg2.resultcode.data != P80211ENUM_resultcode_success)) { | |
412 | break; | |
413 | } | |
414 | ||
415 | ie_buf[0] = WLAN_EID_SSID; | |
416 | ie_buf[1] = msg2.ssid.data.len; | |
417 | ie_len = ie_buf[1] + 2; | |
418 | memcpy(&ie_buf[2], &(msg2.ssid.data.data), msg2.ssid.data.len); | |
4e5e9d7c ZG |
419 | freq = ieee80211_channel_to_frequency(msg2.dschannel.data, |
420 | IEEE80211_BAND_2GHZ); | |
19f798ad | 421 | bss = cfg80211_inform_bss(wiphy, |
4e5e9d7c | 422 | ieee80211_get_channel(wiphy, freq), |
cb3126e6 KR |
423 | (const u8 *) &(msg2.bssid.data.data), |
424 | msg2.timestamp.data, msg2.capinfo.data, | |
425 | msg2.beaconperiod.data, | |
426 | ie_buf, | |
427 | ie_len, | |
428 | (msg2.signal.data - 65536) * 100, /* Conversion to signed type */ | |
429 | GFP_KERNEL | |
430 | ); | |
19f798ad KW |
431 | |
432 | if (!bss) { | |
433 | err = -ENOMEM; | |
434 | goto exit; | |
435 | } | |
436 | ||
5b112d3d | 437 | cfg80211_put_bss(wiphy, bss); |
cb3126e6 KR |
438 | } |
439 | ||
8dd82ebe | 440 | if (result) |
cb3126e6 | 441 | err = prism2_result2err(msg2.resultcode.data); |
cb3126e6 KR |
442 | |
443 | exit: | |
444 | cfg80211_scan_done(request, err ? 1 : 0); | |
445 | priv->scan_request = NULL; | |
446 | return err; | |
447 | } | |
448 | ||
55da06eb | 449 | static int prism2_set_wiphy_params(struct wiphy *wiphy, u32 changed) |
8dd82ebe | 450 | { |
cb3126e6 KR |
451 | struct prism2_wiphy_private *priv = wiphy_priv(wiphy); |
452 | wlandevice_t *wlandev = priv->wlandev; | |
453 | u32 data; | |
454 | int result; | |
455 | int err = 0; | |
456 | ||
457 | if (changed & WIPHY_PARAM_RTS_THRESHOLD) { | |
458 | if (wiphy->rts_threshold == -1) | |
459 | data = 2347; | |
460 | else | |
461 | data = wiphy->rts_threshold; | |
462 | ||
8dd82ebe EH |
463 | result = prism2_domibset_uint32(wlandev, |
464 | DIDmib_dot11mac_dot11OperationTable_dot11RTSThreshold, | |
465 | data); | |
cb3126e6 KR |
466 | if (result) { |
467 | err = -EFAULT; | |
468 | goto exit; | |
469 | } | |
470 | } | |
471 | ||
472 | if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { | |
cb3126e6 KR |
473 | if (wiphy->frag_threshold == -1) |
474 | data = 2346; | |
475 | else | |
476 | data = wiphy->frag_threshold; | |
477 | ||
8dd82ebe EH |
478 | result = prism2_domibset_uint32(wlandev, |
479 | DIDmib_dot11mac_dot11OperationTable_dot11FragmentationThreshold, | |
480 | data); | |
cb3126e6 KR |
481 | if (result) { |
482 | err = -EFAULT; | |
483 | goto exit; | |
484 | } | |
485 | } | |
486 | ||
487 | exit: | |
488 | return err; | |
489 | } | |
490 | ||
55da06eb TB |
491 | static int prism2_connect(struct wiphy *wiphy, struct net_device *dev, |
492 | struct cfg80211_connect_params *sme) | |
8dd82ebe | 493 | { |
cb3126e6 KR |
494 | wlandevice_t *wlandev = dev->ml_priv; |
495 | struct ieee80211_channel *channel = sme->channel; | |
b6bb56e6 | 496 | struct p80211msg_lnxreq_autojoin msg_join; |
cb3126e6 KR |
497 | u32 did; |
498 | int length = sme->ssid_len; | |
499 | int chan = -1; | |
500 | int is_wep = (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40) || | |
501 | (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104); | |
502 | int result; | |
503 | int err = 0; | |
504 | ||
505 | /* Set the channel */ | |
506 | if (channel) { | |
507 | chan = ieee80211_frequency_to_channel(channel->center_freq); | |
8dd82ebe EH |
508 | result = prism2_domibset_uint32(wlandev, |
509 | DIDmib_dot11phy_dot11PhyDSSSTable_dot11CurrentChannel, | |
510 | chan); | |
511 | if (result) | |
512 | goto exit; | |
cb3126e6 KR |
513 | } |
514 | ||
d34602de | 515 | /* Set the authorization */ |
cb3126e6 KR |
516 | if ((sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) || |
517 | ((sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) && !is_wep)) | |
518 | msg_join.authtype.data = P80211ENUM_authalg_opensystem; | |
519 | else if ((sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) || | |
520 | ((sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) && is_wep)) | |
521 | msg_join.authtype.data = P80211ENUM_authalg_sharedkey; | |
8dd82ebe | 522 | else |
eed88971 | 523 | netdev_warn(dev, |
8dd82ebe EH |
524 | "Unhandled authorisation type for connect (%d)\n", |
525 | sme->auth_type); | |
cb3126e6 KR |
526 | |
527 | /* Set the encryption - we only support wep */ | |
528 | if (is_wep) { | |
cb3126e6 KR |
529 | if (sme->key) { |
530 | result = prism2_domibset_uint32(wlandev, | |
531 | DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID, | |
532 | sme->key_idx); | |
8dd82ebe EH |
533 | if (result) |
534 | goto exit; | |
cb3126e6 KR |
535 | |
536 | /* send key to driver */ | |
537 | switch (sme->key_idx) { | |
538 | case 0: | |
8dd82ebe | 539 | did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0; |
cb3126e6 KR |
540 | break; |
541 | ||
542 | case 1: | |
8dd82ebe | 543 | did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1; |
cb3126e6 KR |
544 | break; |
545 | ||
546 | case 2: | |
8dd82ebe | 547 | did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2; |
cb3126e6 KR |
548 | break; |
549 | ||
550 | case 3: | |
8dd82ebe | 551 | did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3; |
cb3126e6 KR |
552 | break; |
553 | ||
554 | default: | |
555 | err = -EINVAL; | |
556 | goto exit; | |
557 | } | |
558 | ||
8aac4d44 DN |
559 | result = prism2_domibset_pstr32(wlandev, |
560 | did, sme->key_len, | |
561 | (u8 *)sme->key); | |
8dd82ebe EH |
562 | if (result) |
563 | goto exit; | |
cb3126e6 KR |
564 | |
565 | } | |
566 | ||
567 | /* Assume we should set privacy invoked and exclude unencrypted | |
568 | We could possibly use sme->privacy here, but the assumption | |
569 | seems reasonable anyway */ | |
8dd82ebe EH |
570 | result = prism2_domibset_uint32(wlandev, |
571 | DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked, | |
572 | P80211ENUM_truth_true); | |
573 | if (result) | |
574 | goto exit; | |
575 | ||
576 | result = prism2_domibset_uint32(wlandev, | |
577 | DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted, | |
578 | P80211ENUM_truth_true); | |
579 | if (result) | |
580 | goto exit; | |
cb3126e6 KR |
581 | |
582 | } else { | |
8dd82ebe EH |
583 | /* Assume we should unset privacy invoked |
584 | and exclude unencrypted */ | |
585 | result = prism2_domibset_uint32(wlandev, | |
586 | DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked, | |
587 | P80211ENUM_truth_false); | |
588 | if (result) | |
589 | goto exit; | |
590 | ||
591 | result = prism2_domibset_uint32(wlandev, | |
592 | DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted, | |
593 | P80211ENUM_truth_false); | |
594 | if (result) | |
595 | goto exit; | |
cb3126e6 KR |
596 | |
597 | } | |
598 | ||
599 | /* Now do the actual join. Note there is no way that I can | |
600 | see to request a specific bssid */ | |
601 | msg_join.msgcode = DIDmsg_lnxreq_autojoin; | |
602 | ||
603 | memcpy(msg_join.ssid.data.data, sme->ssid, length); | |
604 | msg_join.ssid.data.len = length; | |
605 | ||
8dd82ebe | 606 | result = p80211req_dorequest(wlandev, (u8 *) &msg_join); |
cb3126e6 KR |
607 | |
608 | exit: | |
8dd82ebe EH |
609 | if (result) |
610 | err = -EFAULT; | |
cb3126e6 KR |
611 | |
612 | return err; | |
613 | } | |
614 | ||
55da06eb TB |
615 | static int prism2_disconnect(struct wiphy *wiphy, struct net_device *dev, |
616 | u16 reason_code) | |
8dd82ebe | 617 | { |
cb3126e6 | 618 | wlandevice_t *wlandev = dev->ml_priv; |
b6bb56e6 | 619 | struct p80211msg_lnxreq_autojoin msg_join; |
cb3126e6 KR |
620 | int result; |
621 | int err = 0; | |
622 | ||
623 | ||
624 | /* Do a join, with a bogus ssid. Thats the only way I can think of */ | |
625 | msg_join.msgcode = DIDmsg_lnxreq_autojoin; | |
626 | ||
627 | memcpy(msg_join.ssid.data.data, "---", 3); | |
628 | msg_join.ssid.data.len = 3; | |
629 | ||
8dd82ebe | 630 | result = p80211req_dorequest(wlandev, (u8 *) &msg_join); |
cb3126e6 | 631 | |
8dd82ebe EH |
632 | if (result) |
633 | err = -EFAULT; | |
cb3126e6 KR |
634 | |
635 | return err; | |
636 | } | |
637 | ||
638 | ||
55da06eb TB |
639 | static int prism2_join_ibss(struct wiphy *wiphy, struct net_device *dev, |
640 | struct cfg80211_ibss_params *params) | |
8dd82ebe | 641 | { |
cb3126e6 KR |
642 | return -EOPNOTSUPP; |
643 | } | |
644 | ||
55da06eb | 645 | static int prism2_leave_ibss(struct wiphy *wiphy, struct net_device *dev) |
8dd82ebe | 646 | { |
cb3126e6 KR |
647 | return -EOPNOTSUPP; |
648 | } | |
649 | ||
650 | ||
55da06eb TB |
651 | static int prism2_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, |
652 | enum nl80211_tx_power_setting type, int mbm) | |
8dd82ebe | 653 | { |
cb3126e6 KR |
654 | struct prism2_wiphy_private *priv = wiphy_priv(wiphy); |
655 | wlandevice_t *wlandev = priv->wlandev; | |
656 | u32 data; | |
657 | int result; | |
658 | int err = 0; | |
659 | ||
9015e499 | 660 | if (type == NL80211_TX_POWER_AUTOMATIC) |
cb3126e6 KR |
661 | data = 30; |
662 | else | |
9015e499 | 663 | data = MBM_TO_DBM(mbm); |
cb3126e6 KR |
664 | |
665 | result = prism2_domibset_uint32(wlandev, | |
666 | DIDmib_dot11phy_dot11PhyTxPowerTable_dot11CurrentTxPowerLevel, | |
667 | data); | |
668 | ||
669 | if (result) { | |
670 | err = -EFAULT; | |
671 | goto exit; | |
672 | } | |
673 | ||
674 | exit: | |
675 | return err; | |
676 | } | |
677 | ||
55da06eb TB |
678 | static int prism2_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, |
679 | int *dbm) | |
8dd82ebe | 680 | { |
cb3126e6 KR |
681 | struct prism2_wiphy_private *priv = wiphy_priv(wiphy); |
682 | wlandevice_t *wlandev = priv->wlandev; | |
b6bb56e6 | 683 | struct p80211msg_dot11req_mibget msg; |
8aac4d44 | 684 | p80211item_uint32_t *mibitem; |
cb3126e6 KR |
685 | int result; |
686 | int err = 0; | |
687 | ||
8aac4d44 | 688 | mibitem = (p80211item_uint32_t *) &msg.mibattribute.data; |
cb3126e6 KR |
689 | msg.msgcode = DIDmsg_dot11req_mibget; |
690 | mibitem->did = | |
691 | DIDmib_dot11phy_dot11PhyTxPowerTable_dot11CurrentTxPowerLevel; | |
692 | ||
8dd82ebe | 693 | result = p80211req_dorequest(wlandev, (u8 *) &msg); |
cb3126e6 KR |
694 | |
695 | if (result) { | |
696 | err = -EFAULT; | |
697 | goto exit; | |
698 | } | |
699 | ||
700 | *dbm = mibitem->data; | |
701 | ||
702 | exit: | |
703 | return err; | |
704 | } | |
705 | ||
706 | ||
707 | ||
708 | ||
709 | /* Interface callback functions, passing data back up to the cfg80211 layer */ | |
8dd82ebe EH |
710 | void prism2_connect_result(wlandevice_t *wlandev, u8 failed) |
711 | { | |
8aac4d44 DN |
712 | u16 status = failed ? |
713 | WLAN_STATUS_UNSPECIFIED_FAILURE : WLAN_STATUS_SUCCESS; | |
cb3126e6 KR |
714 | |
715 | cfg80211_connect_result(wlandev->netdev, wlandev->bssid, | |
8dd82ebe | 716 | NULL, 0, NULL, 0, status, GFP_KERNEL); |
cb3126e6 KR |
717 | } |
718 | ||
8dd82ebe EH |
719 | void prism2_disconnected(wlandevice_t *wlandev) |
720 | { | |
cb3126e6 KR |
721 | cfg80211_disconnected(wlandev->netdev, 0, NULL, |
722 | 0, GFP_KERNEL); | |
723 | } | |
724 | ||
8dd82ebe EH |
725 | void prism2_roamed(wlandevice_t *wlandev) |
726 | { | |
ed9d0102 | 727 | cfg80211_roamed(wlandev->netdev, NULL, wlandev->bssid, |
cb3126e6 KR |
728 | NULL, 0, NULL, 0, GFP_KERNEL); |
729 | } | |
730 | ||
731 | ||
732 | /* Structures for declaring wiphy interface */ | |
733 | static const struct cfg80211_ops prism2_usb_cfg_ops = { | |
734 | .change_virtual_intf = prism2_change_virtual_intf, | |
735 | .add_key = prism2_add_key, | |
736 | .get_key = prism2_get_key, | |
737 | .del_key = prism2_del_key, | |
738 | .set_default_key = prism2_set_default_key, | |
739 | .get_station = prism2_get_station, | |
740 | .scan = prism2_scan, | |
741 | .set_wiphy_params = prism2_set_wiphy_params, | |
742 | .connect = prism2_connect, | |
743 | .disconnect = prism2_disconnect, | |
744 | .join_ibss = prism2_join_ibss, | |
745 | .leave_ibss = prism2_leave_ibss, | |
746 | .set_tx_power = prism2_set_tx_power, | |
747 | .get_tx_power = prism2_get_tx_power, | |
748 | }; | |
749 | ||
750 | ||
751 | /* Functions to create/free wiphy interface */ | |
36d9c250 | 752 | static struct wiphy *wlan_create_wiphy(struct device *dev, wlandevice_t *wlandev) |
cb3126e6 KR |
753 | { |
754 | struct wiphy *wiphy; | |
755 | struct prism2_wiphy_private *priv; | |
8aac4d44 DN |
756 | |
757 | wiphy = wiphy_new(&prism2_usb_cfg_ops, sizeof(*priv)); | |
cb3126e6 KR |
758 | if (!wiphy) |
759 | return NULL; | |
760 | ||
761 | priv = wiphy_priv(wiphy); | |
762 | priv->wlandev = wlandev; | |
763 | memcpy(priv->channels, prism2_channels, sizeof(prism2_channels)); | |
764 | memcpy(priv->rates, prism2_rates, sizeof(prism2_rates)); | |
765 | priv->band.channels = priv->channels; | |
766 | priv->band.n_channels = ARRAY_SIZE(prism2_channels); | |
767 | priv->band.bitrates = priv->rates; | |
768 | priv->band.n_bitrates = ARRAY_SIZE(prism2_rates); | |
aff3ea4e KR |
769 | priv->band.band = IEEE80211_BAND_2GHZ; |
770 | priv->band.ht_cap.ht_supported = false; | |
cb3126e6 KR |
771 | wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; |
772 | ||
773 | set_wiphy_dev(wiphy, dev); | |
774 | wiphy->privid = prism2_wiphy_privid; | |
775 | wiphy->max_scan_ssids = 1; | |
8dd82ebe EH |
776 | wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
777 | | BIT(NL80211_IFTYPE_ADHOC); | |
cb3126e6 KR |
778 | wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; |
779 | wiphy->n_cipher_suites = PRISM2_NUM_CIPHER_SUITES; | |
780 | wiphy->cipher_suites = prism2_cipher_suites; | |
781 | ||
782 | if (wiphy_register(wiphy) < 0) | |
783 | return NULL; | |
784 | ||
785 | return wiphy; | |
786 | } | |
787 | ||
788 | ||
36d9c250 | 789 | static void wlan_free_wiphy(struct wiphy *wiphy) |
cb3126e6 KR |
790 | { |
791 | wiphy_unregister(wiphy); | |
792 | wiphy_free(wiphy); | |
793 | } |