libertas: move association code from join.c into scan.c
[safe/jmp/linux-2.6] / drivers / net / wireless / libertas / assoc.c
1 /* Copyright (C) 2006, Red Hat, Inc. */
2
3 #include <linux/etherdevice.h>
4
5 #include "assoc.h"
6 #include "decl.h"
7 #include "host.h"
8 #include "cmd.h"
9
10
11 static const u8 bssid_any[ETH_ALEN]  __attribute__ ((aligned (2))) =
12         { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
13 static const u8 bssid_off[ETH_ALEN]  __attribute__ ((aligned (2))) =
14         { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
15
16 /* The firmware needs certain bits masked out of the beacon-derviced capability
17  * field when associating/joining to BSSs.
18  */
19 #define CAPINFO_MASK    (~(0xda00))
20
21
22
23 /**
24  *  @brief Associate to a specific BSS discovered in a scan
25  *
26  *  @param priv      A pointer to struct lbs_private structure
27  *  @param pbssdesc  Pointer to the BSS descriptor to associate with.
28  *
29  *  @return          0-success, otherwise fail
30  */
31 static int lbs_associate(struct lbs_private *priv,
32         struct assoc_request *assoc_req)
33 {
34         int ret;
35
36         lbs_deb_enter(LBS_DEB_ASSOC);
37
38         ret = lbs_prepare_and_send_command(priv, CMD_802_11_AUTHENTICATE,
39                                     0, CMD_OPTION_WAITFORRSP,
40                                     0, assoc_req->bss.bssid);
41
42         if (ret)
43                 goto done;
44
45         /* set preamble to firmware */
46         if ((priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
47             (assoc_req->bss.capability & WLAN_CAPABILITY_SHORT_PREAMBLE))
48                 priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
49         else
50                 priv->preamble = CMD_TYPE_LONG_PREAMBLE;
51
52         lbs_set_radio_control(priv);
53
54         ret = lbs_prepare_and_send_command(priv, CMD_802_11_ASSOCIATE,
55                                     0, CMD_OPTION_WAITFORRSP, 0, assoc_req);
56
57 done:
58         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
59         return ret;
60 }
61
62 /**
63  *  @brief Join an adhoc network found in a previous scan
64  *
65  *  @param priv         A pointer to struct lbs_private structure
66  *  @param pbssdesc     Pointer to a BSS descriptor found in a previous scan
67  *                      to attempt to join
68  *
69  *  @return             0--success, -1--fail
70  */
71 static int lbs_join_adhoc_network(struct lbs_private *priv,
72         struct assoc_request *assoc_req)
73 {
74         struct bss_descriptor *bss = &assoc_req->bss;
75         int ret = 0;
76
77         lbs_deb_join("current SSID '%s', ssid length %u\n",
78                 escape_essid(priv->curbssparams.ssid,
79                 priv->curbssparams.ssid_len),
80                 priv->curbssparams.ssid_len);
81         lbs_deb_join("requested ssid '%s', ssid length %u\n",
82                 escape_essid(bss->ssid, bss->ssid_len),
83                 bss->ssid_len);
84
85         /* check if the requested SSID is already joined */
86         if (priv->curbssparams.ssid_len &&
87             !lbs_ssid_cmp(priv->curbssparams.ssid,
88                         priv->curbssparams.ssid_len,
89                         bss->ssid, bss->ssid_len) &&
90             (priv->mode == IW_MODE_ADHOC) &&
91             (priv->connect_status == LBS_CONNECTED)) {
92                 union iwreq_data wrqu;
93
94                 lbs_deb_join("ADHOC_J_CMD: New ad-hoc SSID is the same as "
95                         "current, not attempting to re-join");
96
97                 /* Send the re-association event though, because the association
98                  * request really was successful, even if just a null-op.
99                  */
100                 memset(&wrqu, 0, sizeof(wrqu));
101                 memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid,
102                        ETH_ALEN);
103                 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
104                 wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
105                 goto out;
106         }
107
108         /* Use shortpreamble only when both creator and card supports
109            short preamble */
110         if (!(bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) ||
111             !(priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)) {
112                 lbs_deb_join("AdhocJoin: Long preamble\n");
113                 priv->preamble = CMD_TYPE_LONG_PREAMBLE;
114         } else {
115                 lbs_deb_join("AdhocJoin: Short preamble\n");
116                 priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
117         }
118
119         lbs_set_radio_control(priv);
120
121         lbs_deb_join("AdhocJoin: channel = %d\n", assoc_req->channel);
122         lbs_deb_join("AdhocJoin: band = %c\n", assoc_req->band);
123
124         priv->adhoccreate = 0;
125
126         ret = lbs_prepare_and_send_command(priv, CMD_802_11_AD_HOC_JOIN,
127                                     0, CMD_OPTION_WAITFORRSP,
128                                     OID_802_11_SSID, assoc_req);
129
130 out:
131         return ret;
132 }
133
134 /**
135  *  @brief Start an Adhoc Network
136  *
137  *  @param priv         A pointer to struct lbs_private structure
138  *  @param adhocssid    The ssid of the Adhoc Network
139  *  @return             0--success, -1--fail
140  */
141 static int lbs_start_adhoc_network(struct lbs_private *priv,
142         struct assoc_request *assoc_req)
143 {
144         int ret = 0;
145
146         priv->adhoccreate = 1;
147
148         if (priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) {
149                 lbs_deb_join("AdhocStart: Short preamble\n");
150                 priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
151         } else {
152                 lbs_deb_join("AdhocStart: Long preamble\n");
153                 priv->preamble = CMD_TYPE_LONG_PREAMBLE;
154         }
155
156         lbs_set_radio_control(priv);
157
158         lbs_deb_join("AdhocStart: channel = %d\n", assoc_req->channel);
159         lbs_deb_join("AdhocStart: band = %d\n", assoc_req->band);
160
161         ret = lbs_prepare_and_send_command(priv, CMD_802_11_AD_HOC_START,
162                                     0, CMD_OPTION_WAITFORRSP, 0, assoc_req);
163
164         return ret;
165 }
166
167 int lbs_stop_adhoc_network(struct lbs_private *priv)
168 {
169         return lbs_prepare_and_send_command(priv, CMD_802_11_AD_HOC_STOP,
170                                      0, CMD_OPTION_WAITFORRSP, 0, NULL);
171 }
172
173 static int assoc_helper_essid(struct lbs_private *priv,
174                               struct assoc_request * assoc_req)
175 {
176         int ret = 0;
177         struct bss_descriptor * bss;
178         int channel = -1;
179
180         lbs_deb_enter(LBS_DEB_ASSOC);
181
182         /* FIXME: take channel into account when picking SSIDs if a channel
183          * is set.
184          */
185
186         if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
187                 channel = assoc_req->channel;
188
189         lbs_deb_assoc("SSID '%s' requested\n",
190                       escape_essid(assoc_req->ssid, assoc_req->ssid_len));
191         if (assoc_req->mode == IW_MODE_INFRA) {
192                 lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
193                         assoc_req->ssid_len);
194
195                 bss = lbs_find_ssid_in_list(priv, assoc_req->ssid,
196                                 assoc_req->ssid_len, NULL, IW_MODE_INFRA, channel);
197                 if (bss != NULL) {
198                         memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
199                         ret = lbs_associate(priv, assoc_req);
200                 } else {
201                         lbs_deb_assoc("SSID not found; cannot associate\n");
202                 }
203         } else if (assoc_req->mode == IW_MODE_ADHOC) {
204                 /* Scan for the network, do not save previous results.  Stale
205                  *   scan data will cause us to join a non-existant adhoc network
206                  */
207                 lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
208                         assoc_req->ssid_len);
209
210                 /* Search for the requested SSID in the scan table */
211                 bss = lbs_find_ssid_in_list(priv, assoc_req->ssid,
212                                 assoc_req->ssid_len, NULL, IW_MODE_ADHOC, channel);
213                 if (bss != NULL) {
214                         lbs_deb_assoc("SSID found, will join\n");
215                         memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
216                         lbs_join_adhoc_network(priv, assoc_req);
217                 } else {
218                         /* else send START command */
219                         lbs_deb_assoc("SSID not found, creating adhoc network\n");
220                         memcpy(&assoc_req->bss.ssid, &assoc_req->ssid,
221                                 IW_ESSID_MAX_SIZE);
222                         assoc_req->bss.ssid_len = assoc_req->ssid_len;
223                         lbs_start_adhoc_network(priv, assoc_req);
224                 }
225         }
226
227         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
228         return ret;
229 }
230
231
232 static int assoc_helper_bssid(struct lbs_private *priv,
233                               struct assoc_request * assoc_req)
234 {
235         int ret = 0;
236         struct bss_descriptor * bss;
237         DECLARE_MAC_BUF(mac);
238
239         lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID %s",
240                 print_mac(mac, assoc_req->bssid));
241
242         /* Search for index position in list for requested MAC */
243         bss = lbs_find_bssid_in_list(priv, assoc_req->bssid,
244                             assoc_req->mode);
245         if (bss == NULL) {
246                 lbs_deb_assoc("ASSOC: WAP: BSSID %s not found, "
247                         "cannot associate.\n", print_mac(mac, assoc_req->bssid));
248                 goto out;
249         }
250
251         memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
252         if (assoc_req->mode == IW_MODE_INFRA) {
253                 ret = lbs_associate(priv, assoc_req);
254                 lbs_deb_assoc("ASSOC: lbs_associate(bssid) returned %d\n", ret);
255         } else if (assoc_req->mode == IW_MODE_ADHOC) {
256                 lbs_join_adhoc_network(priv, assoc_req);
257         }
258
259 out:
260         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
261         return ret;
262 }
263
264
265 static int assoc_helper_associate(struct lbs_private *priv,
266                                   struct assoc_request * assoc_req)
267 {
268         int ret = 0, done = 0;
269
270         lbs_deb_enter(LBS_DEB_ASSOC);
271
272         /* If we're given and 'any' BSSID, try associating based on SSID */
273
274         if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
275                 if (compare_ether_addr(bssid_any, assoc_req->bssid)
276                     && compare_ether_addr(bssid_off, assoc_req->bssid)) {
277                         ret = assoc_helper_bssid(priv, assoc_req);
278                         done = 1;
279                 }
280         }
281
282         if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
283                 ret = assoc_helper_essid(priv, assoc_req);
284         }
285
286         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
287         return ret;
288 }
289
290
291 static int assoc_helper_mode(struct lbs_private *priv,
292                              struct assoc_request * assoc_req)
293 {
294         int ret = 0;
295
296         lbs_deb_enter(LBS_DEB_ASSOC);
297
298         if (assoc_req->mode == priv->mode)
299                 goto done;
300
301         if (assoc_req->mode == IW_MODE_INFRA) {
302                 if (priv->psstate != PS_STATE_FULL_POWER)
303                         lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
304                 priv->psmode = LBS802_11POWERMODECAM;
305         }
306
307         priv->mode = assoc_req->mode;
308         ret = lbs_prepare_and_send_command(priv,
309                                     CMD_802_11_SNMP_MIB,
310                                     0, CMD_OPTION_WAITFORRSP,
311                                     OID_802_11_INFRASTRUCTURE_MODE,
312                 /* Shoot me now */  (void *) (size_t) assoc_req->mode);
313
314 done:
315         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
316         return ret;
317 }
318
319
320 int lbs_update_channel(struct lbs_private *priv)
321 {
322         int ret;
323
324         /* the channel in f/w could be out of sync; get the current channel */
325         lbs_deb_enter(LBS_DEB_ASSOC);
326
327         ret = lbs_get_channel(priv);
328         if (ret > 0) {
329                 priv->curbssparams.channel = ret;
330                 ret = 0;
331         }
332         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
333         return ret;
334 }
335
336 static int assoc_helper_channel(struct lbs_private *priv,
337                                 struct assoc_request * assoc_req)
338 {
339         int ret = 0;
340
341         lbs_deb_enter(LBS_DEB_ASSOC);
342
343         ret = lbs_update_channel(priv);
344         if (ret) {
345                 lbs_deb_assoc("ASSOC: channel: error getting channel.\n");
346                 goto done;
347         }
348
349         if (assoc_req->channel == priv->curbssparams.channel)
350                 goto done;
351
352         if (priv->mesh_dev) {
353                 /* Change mesh channel first; 21.p21 firmware won't let
354                    you change channel otherwise (even though it'll return
355                    an error to this */
356                 lbs_mesh_config(priv, 0, assoc_req->channel);
357         }
358
359         lbs_deb_assoc("ASSOC: channel: %d -> %d\n",
360                       priv->curbssparams.channel, assoc_req->channel);
361
362         ret = lbs_set_channel(priv, assoc_req->channel);
363         if (ret < 0)
364                 lbs_deb_assoc("ASSOC: channel: error setting channel.\n");
365
366         /* FIXME: shouldn't need to grab the channel _again_ after setting
367          * it since the firmware is supposed to return the new channel, but
368          * whatever... */
369         ret = lbs_update_channel(priv);
370         if (ret) {
371                 lbs_deb_assoc("ASSOC: channel: error getting channel.\n");
372                 goto done;
373         }
374
375         if (assoc_req->channel != priv->curbssparams.channel) {
376                 lbs_deb_assoc("ASSOC: channel: failed to update channel to %d\n",
377                               assoc_req->channel);
378                 goto restore_mesh;
379         }
380
381         if (   assoc_req->secinfo.wep_enabled
382             &&   (assoc_req->wep_keys[0].len
383                || assoc_req->wep_keys[1].len
384                || assoc_req->wep_keys[2].len
385                || assoc_req->wep_keys[3].len)) {
386                 /* Make sure WEP keys are re-sent to firmware */
387                 set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
388         }
389
390         /* Must restart/rejoin adhoc networks after channel change */
391         set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
392
393  restore_mesh:
394         if (priv->mesh_dev)
395                 lbs_mesh_config(priv, 1, priv->curbssparams.channel);
396
397  done:
398         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
399         return ret;
400 }
401
402
403 static int assoc_helper_wep_keys(struct lbs_private *priv,
404                                  struct assoc_request *assoc_req)
405 {
406         int i;
407         int ret = 0;
408
409         lbs_deb_enter(LBS_DEB_ASSOC);
410
411         /* Set or remove WEP keys */
412         if (assoc_req->wep_keys[0].len || assoc_req->wep_keys[1].len ||
413             assoc_req->wep_keys[2].len || assoc_req->wep_keys[3].len)
414                 ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_ADD, assoc_req);
415         else
416                 ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_REMOVE, assoc_req);
417
418         if (ret)
419                 goto out;
420
421         /* enable/disable the MAC's WEP packet filter */
422         if (assoc_req->secinfo.wep_enabled)
423                 priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE;
424         else
425                 priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE;
426
427         lbs_set_mac_control(priv);
428
429         mutex_lock(&priv->lock);
430
431         /* Copy WEP keys into priv wep key fields */
432         for (i = 0; i < 4; i++) {
433                 memcpy(&priv->wep_keys[i], &assoc_req->wep_keys[i],
434                        sizeof(struct enc_key));
435         }
436         priv->wep_tx_keyidx = assoc_req->wep_tx_keyidx;
437
438         mutex_unlock(&priv->lock);
439
440 out:
441         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
442         return ret;
443 }
444
445 static int assoc_helper_secinfo(struct lbs_private *priv,
446                                 struct assoc_request * assoc_req)
447 {
448         int ret = 0;
449         uint16_t do_wpa;
450         uint16_t rsn = 0;
451
452         lbs_deb_enter(LBS_DEB_ASSOC);
453
454         memcpy(&priv->secinfo, &assoc_req->secinfo,
455                 sizeof(struct lbs_802_11_security));
456
457         lbs_set_mac_control(priv);
458
459         /* If RSN is already enabled, don't try to enable it again, since
460          * ENABLE_RSN resets internal state machines and will clobber the
461          * 4-way WPA handshake.
462          */
463
464         /* Get RSN enabled/disabled */
465         ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_GET, &rsn);
466         if (ret) {
467                 lbs_deb_assoc("Failed to get RSN status: %d\n", ret);
468                 goto out;
469         }
470
471         /* Don't re-enable RSN if it's already enabled */
472         do_wpa = assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled;
473         if (do_wpa == rsn)
474                 goto out;
475
476         /* Set RSN enabled/disabled */
477         ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_SET, &do_wpa);
478
479 out:
480         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
481         return ret;
482 }
483
484
485 static int assoc_helper_wpa_keys(struct lbs_private *priv,
486                                  struct assoc_request * assoc_req)
487 {
488         int ret = 0;
489         unsigned int flags = assoc_req->flags;
490
491         lbs_deb_enter(LBS_DEB_ASSOC);
492
493         /* Work around older firmware bug where WPA unicast and multicast
494          * keys must be set independently.  Seen in SDIO parts with firmware
495          * version 5.0.11p0.
496          */
497
498         if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
499                 clear_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
500                 ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
501                 assoc_req->flags = flags;
502         }
503
504         if (ret)
505                 goto out;
506
507         if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
508                 clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
509
510                 ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
511                 assoc_req->flags = flags;
512         }
513
514 out:
515         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
516         return ret;
517 }
518
519
520 static int assoc_helper_wpa_ie(struct lbs_private *priv,
521                                struct assoc_request * assoc_req)
522 {
523         int ret = 0;
524
525         lbs_deb_enter(LBS_DEB_ASSOC);
526
527         if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
528                 memcpy(&priv->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len);
529                 priv->wpa_ie_len = assoc_req->wpa_ie_len;
530         } else {
531                 memset(&priv->wpa_ie, 0, MAX_WPA_IE_LEN);
532                 priv->wpa_ie_len = 0;
533         }
534
535         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
536         return ret;
537 }
538
539
540 static int should_deauth_infrastructure(struct lbs_private *priv,
541                                         struct assoc_request * assoc_req)
542 {
543         int ret = 0;
544
545         if (priv->connect_status != LBS_CONNECTED)
546                 return 0;
547
548         lbs_deb_enter(LBS_DEB_ASSOC);
549         if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
550                 lbs_deb_assoc("Deauthenticating due to new SSID\n");
551                 ret = 1;
552                 goto out;
553         }
554
555         if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
556                 if (priv->secinfo.auth_mode != assoc_req->secinfo.auth_mode) {
557                         lbs_deb_assoc("Deauthenticating due to new security\n");
558                         ret = 1;
559                         goto out;
560                 }
561         }
562
563         if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
564                 lbs_deb_assoc("Deauthenticating due to new BSSID\n");
565                 ret = 1;
566                 goto out;
567         }
568
569         if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
570                 lbs_deb_assoc("Deauthenticating due to channel switch\n");
571                 ret = 1;
572                 goto out;
573         }
574
575         /* FIXME: deal with 'auto' mode somehow */
576         if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
577                 if (assoc_req->mode != IW_MODE_INFRA) {
578                         lbs_deb_assoc("Deauthenticating due to leaving "
579                                 "infra mode\n");
580                         ret = 1;
581                         goto out;
582                 }
583         }
584
585 out:
586         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
587         return ret;
588 }
589
590
591 static int should_stop_adhoc(struct lbs_private *priv,
592                              struct assoc_request * assoc_req)
593 {
594         lbs_deb_enter(LBS_DEB_ASSOC);
595
596         if (priv->connect_status != LBS_CONNECTED)
597                 return 0;
598
599         if (lbs_ssid_cmp(priv->curbssparams.ssid,
600                               priv->curbssparams.ssid_len,
601                               assoc_req->ssid, assoc_req->ssid_len) != 0)
602                 return 1;
603
604         /* FIXME: deal with 'auto' mode somehow */
605         if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
606                 if (assoc_req->mode != IW_MODE_ADHOC)
607                         return 1;
608         }
609
610         if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
611                 if (assoc_req->channel != priv->curbssparams.channel)
612                         return 1;
613         }
614
615         lbs_deb_leave(LBS_DEB_ASSOC);
616         return 0;
617 }
618
619
620 void lbs_association_worker(struct work_struct *work)
621 {
622         struct lbs_private *priv = container_of(work, struct lbs_private,
623                 assoc_work.work);
624         struct assoc_request * assoc_req = NULL;
625         int ret = 0;
626         int find_any_ssid = 0;
627         DECLARE_MAC_BUF(mac);
628
629         lbs_deb_enter(LBS_DEB_ASSOC);
630
631         mutex_lock(&priv->lock);
632         assoc_req = priv->pending_assoc_req;
633         priv->pending_assoc_req = NULL;
634         priv->in_progress_assoc_req = assoc_req;
635         mutex_unlock(&priv->lock);
636
637         if (!assoc_req)
638                 goto done;
639
640         lbs_deb_assoc(
641                 "Association Request:\n"
642                 "    flags:     0x%08lx\n"
643                 "    SSID:      '%s'\n"
644                 "    chann:     %d\n"
645                 "    band:      %d\n"
646                 "    mode:      %d\n"
647                 "    BSSID:     %s\n"
648                 "    secinfo:  %s%s%s\n"
649                 "    auth_mode: %d\n",
650                 assoc_req->flags,
651                 escape_essid(assoc_req->ssid, assoc_req->ssid_len),
652                 assoc_req->channel, assoc_req->band, assoc_req->mode,
653                 print_mac(mac, assoc_req->bssid),
654                 assoc_req->secinfo.WPAenabled ? " WPA" : "",
655                 assoc_req->secinfo.WPA2enabled ? " WPA2" : "",
656                 assoc_req->secinfo.wep_enabled ? " WEP" : "",
657                 assoc_req->secinfo.auth_mode);
658
659         /* If 'any' SSID was specified, find an SSID to associate with */
660         if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)
661             && !assoc_req->ssid_len)
662                 find_any_ssid = 1;
663
664         /* But don't use 'any' SSID if there's a valid locked BSSID to use */
665         if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
666                 if (compare_ether_addr(assoc_req->bssid, bssid_any)
667                     && compare_ether_addr(assoc_req->bssid, bssid_off))
668                         find_any_ssid = 0;
669         }
670
671         if (find_any_ssid) {
672                 u8 new_mode;
673
674                 ret = lbs_find_best_network_ssid(priv, assoc_req->ssid,
675                                 &assoc_req->ssid_len, assoc_req->mode, &new_mode);
676                 if (ret) {
677                         lbs_deb_assoc("Could not find best network\n");
678                         ret = -ENETUNREACH;
679                         goto out;
680                 }
681
682                 /* Ensure we switch to the mode of the AP */
683                 if (assoc_req->mode == IW_MODE_AUTO) {
684                         set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
685                         assoc_req->mode = new_mode;
686                 }
687         }
688
689         /*
690          * Check if the attributes being changing require deauthentication
691          * from the currently associated infrastructure access point.
692          */
693         if (priv->mode == IW_MODE_INFRA) {
694                 if (should_deauth_infrastructure(priv, assoc_req)) {
695                         ret = lbs_send_deauthentication(priv);
696                         if (ret) {
697                                 lbs_deb_assoc("Deauthentication due to new "
698                                         "configuration request failed: %d\n",
699                                         ret);
700                         }
701                 }
702         } else if (priv->mode == IW_MODE_ADHOC) {
703                 if (should_stop_adhoc(priv, assoc_req)) {
704                         ret = lbs_stop_adhoc_network(priv);
705                         if (ret) {
706                                 lbs_deb_assoc("Teardown of AdHoc network due to "
707                                         "new configuration request failed: %d\n",
708                                         ret);
709                         }
710
711                 }
712         }
713
714         /* Send the various configuration bits to the firmware */
715         if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
716                 ret = assoc_helper_mode(priv, assoc_req);
717                 if (ret)
718                         goto out;
719         }
720
721         if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
722                 ret = assoc_helper_channel(priv, assoc_req);
723                 if (ret)
724                         goto out;
725         }
726
727         if (   test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)
728             || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) {
729                 ret = assoc_helper_wep_keys(priv, assoc_req);
730                 if (ret)
731                         goto out;
732         }
733
734         if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
735                 ret = assoc_helper_secinfo(priv, assoc_req);
736                 if (ret)
737                         goto out;
738         }
739
740         if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
741                 ret = assoc_helper_wpa_ie(priv, assoc_req);
742                 if (ret)
743                         goto out;
744         }
745
746         if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)
747             || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
748                 ret = assoc_helper_wpa_keys(priv, assoc_req);
749                 if (ret)
750                         goto out;
751         }
752
753         /* SSID/BSSID should be the _last_ config option set, because they
754          * trigger the association attempt.
755          */
756         if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)
757             || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
758                 int success = 1;
759
760                 ret = assoc_helper_associate(priv, assoc_req);
761                 if (ret) {
762                         lbs_deb_assoc("ASSOC: association unsuccessful: %d\n",
763                                 ret);
764                         success = 0;
765                 }
766
767                 if (priv->connect_status != LBS_CONNECTED) {
768                         lbs_deb_assoc("ASSOC: association unsuccessful, "
769                                 "not connected\n");
770                         success = 0;
771                 }
772
773                 if (success) {
774                         lbs_deb_assoc("associated to %s\n",
775                                 print_mac(mac, priv->curbssparams.bssid));
776                         lbs_prepare_and_send_command(priv,
777                                 CMD_802_11_RSSI,
778                                 0, CMD_OPTION_WAITFORRSP, 0, NULL);
779                 } else {
780                         ret = -1;
781                 }
782         }
783
784 out:
785         if (ret) {
786                 lbs_deb_assoc("ASSOC: reconfiguration attempt unsuccessful: %d\n",
787                         ret);
788         }
789
790         mutex_lock(&priv->lock);
791         priv->in_progress_assoc_req = NULL;
792         mutex_unlock(&priv->lock);
793         kfree(assoc_req);
794
795 done:
796         lbs_deb_leave(LBS_DEB_ASSOC);
797 }
798
799
800 /*
801  * Caller MUST hold any necessary locks
802  */
803 struct assoc_request *lbs_get_association_request(struct lbs_private *priv)
804 {
805         struct assoc_request * assoc_req;
806
807         lbs_deb_enter(LBS_DEB_ASSOC);
808         if (!priv->pending_assoc_req) {
809                 priv->pending_assoc_req = kzalloc(sizeof(struct assoc_request),
810                                                      GFP_KERNEL);
811                 if (!priv->pending_assoc_req) {
812                         lbs_pr_info("Not enough memory to allocate association"
813                                 " request!\n");
814                         return NULL;
815                 }
816         }
817
818         /* Copy current configuration attributes to the association request,
819          * but don't overwrite any that are already set.
820          */
821         assoc_req = priv->pending_assoc_req;
822         if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
823                 memcpy(&assoc_req->ssid, &priv->curbssparams.ssid,
824                        IW_ESSID_MAX_SIZE);
825                 assoc_req->ssid_len = priv->curbssparams.ssid_len;
826         }
827
828         if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
829                 assoc_req->channel = priv->curbssparams.channel;
830
831         if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags))
832                 assoc_req->band = priv->curbssparams.band;
833
834         if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags))
835                 assoc_req->mode = priv->mode;
836
837         if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
838                 memcpy(&assoc_req->bssid, priv->curbssparams.bssid,
839                         ETH_ALEN);
840         }
841
842         if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) {
843                 int i;
844                 for (i = 0; i < 4; i++) {
845                         memcpy(&assoc_req->wep_keys[i], &priv->wep_keys[i],
846                                 sizeof(struct enc_key));
847                 }
848         }
849
850         if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags))
851                 assoc_req->wep_tx_keyidx = priv->wep_tx_keyidx;
852
853         if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
854                 memcpy(&assoc_req->wpa_mcast_key, &priv->wpa_mcast_key,
855                         sizeof(struct enc_key));
856         }
857
858         if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
859                 memcpy(&assoc_req->wpa_unicast_key, &priv->wpa_unicast_key,
860                         sizeof(struct enc_key));
861         }
862
863         if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
864                 memcpy(&assoc_req->secinfo, &priv->secinfo,
865                         sizeof(struct lbs_802_11_security));
866         }
867
868         if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
869                 memcpy(&assoc_req->wpa_ie, &priv->wpa_ie,
870                         MAX_WPA_IE_LEN);
871                 assoc_req->wpa_ie_len = priv->wpa_ie_len;
872         }
873
874         lbs_deb_leave(LBS_DEB_ASSOC);
875         return assoc_req;
876 }
877
878
879 /**
880  *  @brief This function finds common rates between rate1 and card rates.
881  *
882  * It will fill common rates in rate1 as output if found.
883  *
884  * NOTE: Setting the MSB of the basic rates need to be taken
885  *   care, either before or after calling this function
886  *
887  *  @param priv     A pointer to struct lbs_private structure
888  *  @param rate1       the buffer which keeps input and output
889  *  @param rate1_size  the size of rate1 buffer; new size of buffer on return
890  *
891  *  @return            0 or -1
892  */
893 static int get_common_rates(struct lbs_private *priv,
894         u8 *rates,
895         u16 *rates_size)
896 {
897         u8 *card_rates = lbs_bg_rates;
898         size_t num_card_rates = sizeof(lbs_bg_rates);
899         int ret = 0, i, j;
900         u8 tmp[30];
901         size_t tmp_size = 0;
902
903         /* For each rate in card_rates that exists in rate1, copy to tmp */
904         for (i = 0; card_rates[i] && (i < num_card_rates); i++) {
905                 for (j = 0; rates[j] && (j < *rates_size); j++) {
906                         if (rates[j] == card_rates[i])
907                                 tmp[tmp_size++] = card_rates[i];
908                 }
909         }
910
911         lbs_deb_hex(LBS_DEB_JOIN, "AP rates    ", rates, *rates_size);
912         lbs_deb_hex(LBS_DEB_JOIN, "card rates  ", card_rates, num_card_rates);
913         lbs_deb_hex(LBS_DEB_JOIN, "common rates", tmp, tmp_size);
914         lbs_deb_join("TX data rate 0x%02x\n", priv->cur_rate);
915
916         if (!priv->auto_rate) {
917                 for (i = 0; i < tmp_size; i++) {
918                         if (tmp[i] == priv->cur_rate)
919                                 goto done;
920                 }
921                 lbs_pr_alert("Previously set fixed data rate %#x isn't "
922                        "compatible with the network.\n", priv->cur_rate);
923                 ret = -1;
924                 goto done;
925         }
926         ret = 0;
927
928 done:
929         memset(rates, 0, *rates_size);
930         *rates_size = min_t(int, tmp_size, *rates_size);
931         memcpy(rates, tmp, *rates_size);
932         return ret;
933 }
934
935
936 /**
937  *  @brief Sets the MSB on basic rates as the firmware requires
938  *
939  * Scan through an array and set the MSB for basic data rates.
940  *
941  *  @param rates     buffer of data rates
942  *  @param len       size of buffer
943  */
944 static void lbs_set_basic_rate_flags(u8 *rates, size_t len)
945 {
946         int i;
947
948         for (i = 0; i < len; i++) {
949                 if (rates[i] == 0x02 || rates[i] == 0x04 ||
950                     rates[i] == 0x0b || rates[i] == 0x16)
951                         rates[i] |= 0x80;
952         }
953 }
954
955 /**
956  *  @brief Send Deauthentication Request
957  *
958  *  @param priv      A pointer to struct lbs_private structure
959  *  @return          0--success, -1--fail
960  */
961 int lbs_send_deauthentication(struct lbs_private *priv)
962 {
963         return lbs_prepare_and_send_command(priv, CMD_802_11_DEAUTHENTICATE,
964                                      0, CMD_OPTION_WAITFORRSP, 0, NULL);
965 }
966
967 /**
968  *  @brief This function prepares command of authenticate.
969  *
970  *  @param priv      A pointer to struct lbs_private structure
971  *  @param cmd       A pointer to cmd_ds_command structure
972  *  @param pdata_buf Void cast of pointer to a BSSID to authenticate with
973  *
974  *  @return         0 or -1
975  */
976 int lbs_cmd_80211_authenticate(struct lbs_private *priv,
977                                  struct cmd_ds_command *cmd,
978                                  void *pdata_buf)
979 {
980         struct cmd_ds_802_11_authenticate *pauthenticate = &cmd->params.auth;
981         int ret = -1;
982         u8 *bssid = pdata_buf;
983         DECLARE_MAC_BUF(mac);
984
985         lbs_deb_enter(LBS_DEB_JOIN);
986
987         cmd->command = cpu_to_le16(CMD_802_11_AUTHENTICATE);
988         cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_authenticate)
989                         + S_DS_GEN);
990
991         /* translate auth mode to 802.11 defined wire value */
992         switch (priv->secinfo.auth_mode) {
993         case IW_AUTH_ALG_OPEN_SYSTEM:
994                 pauthenticate->authtype = 0x00;
995                 break;
996         case IW_AUTH_ALG_SHARED_KEY:
997                 pauthenticate->authtype = 0x01;
998                 break;
999         case IW_AUTH_ALG_LEAP:
1000                 pauthenticate->authtype = 0x80;
1001                 break;
1002         default:
1003                 lbs_deb_join("AUTH_CMD: invalid auth alg 0x%X\n",
1004                         priv->secinfo.auth_mode);
1005                 goto out;
1006         }
1007
1008         memcpy(pauthenticate->macaddr, bssid, ETH_ALEN);
1009
1010         lbs_deb_join("AUTH_CMD: BSSID %s, auth 0x%x\n",
1011                 print_mac(mac, bssid), pauthenticate->authtype);
1012         ret = 0;
1013
1014 out:
1015         lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
1016         return ret;
1017 }
1018
1019 int lbs_cmd_80211_deauthenticate(struct lbs_private *priv,
1020                                    struct cmd_ds_command *cmd)
1021 {
1022         struct cmd_ds_802_11_deauthenticate *dauth = &cmd->params.deauth;
1023
1024         lbs_deb_enter(LBS_DEB_JOIN);
1025
1026         cmd->command = cpu_to_le16(CMD_802_11_DEAUTHENTICATE);
1027         cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_deauthenticate) +
1028                              S_DS_GEN);
1029
1030         /* set AP MAC address */
1031         memmove(dauth->macaddr, priv->curbssparams.bssid, ETH_ALEN);
1032
1033         /* Reason code 3 = Station is leaving */
1034 #define REASON_CODE_STA_LEAVING 3
1035         dauth->reasoncode = cpu_to_le16(REASON_CODE_STA_LEAVING);
1036
1037         lbs_deb_leave(LBS_DEB_JOIN);
1038         return 0;
1039 }
1040
1041 int lbs_cmd_80211_associate(struct lbs_private *priv,
1042                               struct cmd_ds_command *cmd, void *pdata_buf)
1043 {
1044         struct cmd_ds_802_11_associate *passo = &cmd->params.associate;
1045         int ret = 0;
1046         struct assoc_request *assoc_req = pdata_buf;
1047         struct bss_descriptor *bss = &assoc_req->bss;
1048         u8 *pos;
1049         u16 tmpcap, tmplen;
1050         struct mrvlietypes_ssidparamset *ssid;
1051         struct mrvlietypes_phyparamset *phy;
1052         struct mrvlietypes_ssparamset *ss;
1053         struct mrvlietypes_ratesparamset *rates;
1054         struct mrvlietypes_rsnparamset *rsn;
1055
1056         lbs_deb_enter(LBS_DEB_ASSOC);
1057
1058         pos = (u8 *) passo;
1059
1060         if (!priv) {
1061                 ret = -1;
1062                 goto done;
1063         }
1064
1065         cmd->command = cpu_to_le16(CMD_802_11_ASSOCIATE);
1066
1067         memcpy(passo->peerstaaddr, bss->bssid, sizeof(passo->peerstaaddr));
1068         pos += sizeof(passo->peerstaaddr);
1069
1070         /* set the listen interval */
1071         passo->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL);
1072
1073         pos += sizeof(passo->capability);
1074         pos += sizeof(passo->listeninterval);
1075         pos += sizeof(passo->bcnperiod);
1076         pos += sizeof(passo->dtimperiod);
1077
1078         ssid = (struct mrvlietypes_ssidparamset *) pos;
1079         ssid->header.type = cpu_to_le16(TLV_TYPE_SSID);
1080         tmplen = bss->ssid_len;
1081         ssid->header.len = cpu_to_le16(tmplen);
1082         memcpy(ssid->ssid, bss->ssid, tmplen);
1083         pos += sizeof(ssid->header) + tmplen;
1084
1085         phy = (struct mrvlietypes_phyparamset *) pos;
1086         phy->header.type = cpu_to_le16(TLV_TYPE_PHY_DS);
1087         tmplen = sizeof(phy->fh_ds.dsparamset);
1088         phy->header.len = cpu_to_le16(tmplen);
1089         memcpy(&phy->fh_ds.dsparamset,
1090                &bss->phyparamset.dsparamset.currentchan,
1091                tmplen);
1092         pos += sizeof(phy->header) + tmplen;
1093
1094         ss = (struct mrvlietypes_ssparamset *) pos;
1095         ss->header.type = cpu_to_le16(TLV_TYPE_CF);
1096         tmplen = sizeof(ss->cf_ibss.cfparamset);
1097         ss->header.len = cpu_to_le16(tmplen);
1098         pos += sizeof(ss->header) + tmplen;
1099
1100         rates = (struct mrvlietypes_ratesparamset *) pos;
1101         rates->header.type = cpu_to_le16(TLV_TYPE_RATES);
1102         memcpy(&rates->rates, &bss->rates, MAX_RATES);
1103         tmplen = MAX_RATES;
1104         if (get_common_rates(priv, rates->rates, &tmplen)) {
1105                 ret = -1;
1106                 goto done;
1107         }
1108         pos += sizeof(rates->header) + tmplen;
1109         rates->header.len = cpu_to_le16(tmplen);
1110         lbs_deb_assoc("ASSOC_CMD: num rates %u\n", tmplen);
1111
1112         /* Copy the infra. association rates into Current BSS state structure */
1113         memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
1114         memcpy(&priv->curbssparams.rates, &rates->rates, tmplen);
1115
1116         /* Set MSB on basic rates as the firmware requires, but _after_
1117          * copying to current bss rates.
1118          */
1119         lbs_set_basic_rate_flags(rates->rates, tmplen);
1120
1121         if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
1122                 rsn = (struct mrvlietypes_rsnparamset *) pos;
1123                 /* WPA_IE or WPA2_IE */
1124                 rsn->header.type = cpu_to_le16((u16) assoc_req->wpa_ie[0]);
1125                 tmplen = (u16) assoc_req->wpa_ie[1];
1126                 rsn->header.len = cpu_to_le16(tmplen);
1127                 memcpy(rsn->rsnie, &assoc_req->wpa_ie[2], tmplen);
1128                 lbs_deb_hex(LBS_DEB_JOIN, "ASSOC_CMD: RSN IE", (u8 *) rsn,
1129                         sizeof(rsn->header) + tmplen);
1130                 pos += sizeof(rsn->header) + tmplen;
1131         }
1132
1133         /* update curbssparams */
1134         priv->curbssparams.channel = bss->phyparamset.dsparamset.currentchan;
1135
1136         if (lbs_parse_dnld_countryinfo_11d(priv, bss)) {
1137                 ret = -1;
1138                 goto done;
1139         }
1140
1141         cmd->size = cpu_to_le16((u16) (pos - (u8 *) passo) + S_DS_GEN);
1142
1143         /* set the capability info */
1144         tmpcap = (bss->capability & CAPINFO_MASK);
1145         if (bss->mode == IW_MODE_INFRA)
1146                 tmpcap |= WLAN_CAPABILITY_ESS;
1147         passo->capability = cpu_to_le16(tmpcap);
1148         lbs_deb_assoc("ASSOC_CMD: capability 0x%04x\n", tmpcap);
1149
1150 done:
1151         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1152         return ret;
1153 }
1154
1155 int lbs_cmd_80211_ad_hoc_start(struct lbs_private *priv,
1156                                  struct cmd_ds_command *cmd, void *pdata_buf)
1157 {
1158         struct cmd_ds_802_11_ad_hoc_start *adhs = &cmd->params.ads;
1159         int ret = 0;
1160         int cmdappendsize = 0;
1161         struct assoc_request *assoc_req = pdata_buf;
1162         u16 tmpcap = 0;
1163         size_t ratesize = 0;
1164
1165         lbs_deb_enter(LBS_DEB_JOIN);
1166
1167         if (!priv) {
1168                 ret = -1;
1169                 goto done;
1170         }
1171
1172         cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_START);
1173
1174         /*
1175          * Fill in the parameters for 2 data structures:
1176          *   1. cmd_ds_802_11_ad_hoc_start command
1177          *   2. priv->scantable[i]
1178          *
1179          * Driver will fill up SSID, bsstype,IBSS param, Physical Param,
1180          *   probe delay, and cap info.
1181          *
1182          * Firmware will fill up beacon period, DTIM, Basic rates
1183          *   and operational rates.
1184          */
1185
1186         memset(adhs->ssid, 0, IW_ESSID_MAX_SIZE);
1187         memcpy(adhs->ssid, assoc_req->ssid, assoc_req->ssid_len);
1188
1189         lbs_deb_join("ADHOC_S_CMD: SSID '%s', ssid length %u\n",
1190                 escape_essid(assoc_req->ssid, assoc_req->ssid_len),
1191                 assoc_req->ssid_len);
1192
1193         /* set the BSS type */
1194         adhs->bsstype = CMD_BSS_TYPE_IBSS;
1195         priv->mode = IW_MODE_ADHOC;
1196         if (priv->beacon_period == 0)
1197                 priv->beacon_period = MRVDRV_BEACON_INTERVAL;
1198         adhs->beaconperiod = cpu_to_le16(priv->beacon_period);
1199
1200         /* set Physical param set */
1201 #define DS_PARA_IE_ID   3
1202 #define DS_PARA_IE_LEN  1
1203
1204         adhs->phyparamset.dsparamset.elementid = DS_PARA_IE_ID;
1205         adhs->phyparamset.dsparamset.len = DS_PARA_IE_LEN;
1206
1207         WARN_ON(!assoc_req->channel);
1208
1209         lbs_deb_join("ADHOC_S_CMD: Creating ADHOC on channel %d\n",
1210                      assoc_req->channel);
1211
1212         adhs->phyparamset.dsparamset.currentchan = assoc_req->channel;
1213
1214         /* set IBSS param set */
1215 #define IBSS_PARA_IE_ID   6
1216 #define IBSS_PARA_IE_LEN  2
1217
1218         adhs->ssparamset.ibssparamset.elementid = IBSS_PARA_IE_ID;
1219         adhs->ssparamset.ibssparamset.len = IBSS_PARA_IE_LEN;
1220         adhs->ssparamset.ibssparamset.atimwindow = 0;
1221
1222         /* set capability info */
1223         tmpcap = WLAN_CAPABILITY_IBSS;
1224         if (assoc_req->secinfo.wep_enabled) {
1225                 lbs_deb_join("ADHOC_S_CMD: WEP enabled, "
1226                         "setting privacy on\n");
1227                 tmpcap |= WLAN_CAPABILITY_PRIVACY;
1228         } else {
1229                 lbs_deb_join("ADHOC_S_CMD: WEP disabled, "
1230                         "setting privacy off\n");
1231         }
1232         adhs->capability = cpu_to_le16(tmpcap);
1233
1234         /* probedelay */
1235         adhs->probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
1236
1237         memset(adhs->rates, 0, sizeof(adhs->rates));
1238         ratesize = min(sizeof(adhs->rates), sizeof(lbs_bg_rates));
1239         memcpy(adhs->rates, lbs_bg_rates, ratesize);
1240
1241         /* Copy the ad-hoc creating rates into Current BSS state structure */
1242         memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
1243         memcpy(&priv->curbssparams.rates, &adhs->rates, ratesize);
1244
1245         /* Set MSB on basic rates as the firmware requires, but _after_
1246          * copying to current bss rates.
1247          */
1248         lbs_set_basic_rate_flags(adhs->rates, ratesize);
1249
1250         lbs_deb_join("ADHOC_S_CMD: rates=%02x %02x %02x %02x \n",
1251                adhs->rates[0], adhs->rates[1], adhs->rates[2], adhs->rates[3]);
1252
1253         lbs_deb_join("ADHOC_S_CMD: AD HOC Start command is ready\n");
1254
1255         if (lbs_create_dnld_countryinfo_11d(priv)) {
1256                 lbs_deb_join("ADHOC_S_CMD: dnld_countryinfo_11d failed\n");
1257                 ret = -1;
1258                 goto done;
1259         }
1260
1261         cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_start) +
1262                                 S_DS_GEN + cmdappendsize);
1263
1264         ret = 0;
1265 done:
1266         lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
1267         return ret;
1268 }
1269
1270 int lbs_cmd_80211_ad_hoc_stop(struct cmd_ds_command *cmd)
1271 {
1272         cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_STOP);
1273         cmd->size = cpu_to_le16(S_DS_GEN);
1274
1275         return 0;
1276 }
1277
1278 int lbs_cmd_80211_ad_hoc_join(struct lbs_private *priv,
1279                                 struct cmd_ds_command *cmd, void *pdata_buf)
1280 {
1281         struct cmd_ds_802_11_ad_hoc_join *join_cmd = &cmd->params.adj;
1282         struct assoc_request *assoc_req = pdata_buf;
1283         struct bss_descriptor *bss = &assoc_req->bss;
1284         int cmdappendsize = 0;
1285         int ret = 0;
1286         u16 ratesize = 0;
1287         DECLARE_MAC_BUF(mac);
1288
1289         lbs_deb_enter(LBS_DEB_JOIN);
1290
1291         cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_JOIN);
1292
1293         join_cmd->bss.type = CMD_BSS_TYPE_IBSS;
1294         join_cmd->bss.beaconperiod = cpu_to_le16(bss->beaconperiod);
1295
1296         memcpy(&join_cmd->bss.bssid, &bss->bssid, ETH_ALEN);
1297         memcpy(&join_cmd->bss.ssid, &bss->ssid, bss->ssid_len);
1298
1299         memcpy(&join_cmd->bss.phyparamset, &bss->phyparamset,
1300                sizeof(union ieeetypes_phyparamset));
1301
1302         memcpy(&join_cmd->bss.ssparamset, &bss->ssparamset,
1303                sizeof(union IEEEtypes_ssparamset));
1304
1305         join_cmd->bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK);
1306         lbs_deb_join("ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n",
1307                bss->capability, CAPINFO_MASK);
1308
1309         /* information on BSSID descriptor passed to FW */
1310         lbs_deb_join(
1311                "ADHOC_J_CMD: BSSID = %s, SSID = '%s'\n",
1312                print_mac(mac, join_cmd->bss.bssid),
1313                join_cmd->bss.ssid);
1314
1315         /* failtimeout */
1316         join_cmd->failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT);
1317
1318         /* probedelay */
1319         join_cmd->probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
1320
1321         priv->curbssparams.channel = bss->channel;
1322
1323         /* Copy Data rates from the rates recorded in scan response */
1324         memset(join_cmd->bss.rates, 0, sizeof(join_cmd->bss.rates));
1325         ratesize = min_t(u16, sizeof(join_cmd->bss.rates), MAX_RATES);
1326         memcpy(join_cmd->bss.rates, bss->rates, ratesize);
1327         if (get_common_rates(priv, join_cmd->bss.rates, &ratesize)) {
1328                 lbs_deb_join("ADHOC_J_CMD: get_common_rates returns error.\n");
1329                 ret = -1;
1330                 goto done;
1331         }
1332
1333         /* Copy the ad-hoc creating rates into Current BSS state structure */
1334         memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
1335         memcpy(&priv->curbssparams.rates, join_cmd->bss.rates, ratesize);
1336
1337         /* Set MSB on basic rates as the firmware requires, but _after_
1338          * copying to current bss rates.
1339          */
1340         lbs_set_basic_rate_flags(join_cmd->bss.rates, ratesize);
1341
1342         join_cmd->bss.ssparamset.ibssparamset.atimwindow =
1343             cpu_to_le16(bss->atimwindow);
1344
1345         if (assoc_req->secinfo.wep_enabled) {
1346                 u16 tmp = le16_to_cpu(join_cmd->bss.capability);
1347                 tmp |= WLAN_CAPABILITY_PRIVACY;
1348                 join_cmd->bss.capability = cpu_to_le16(tmp);
1349         }
1350
1351         if (priv->psmode == LBS802_11POWERMODEMAX_PSP) {
1352                 /* wake up first */
1353                 __le32 Localpsmode;
1354
1355                 Localpsmode = cpu_to_le32(LBS802_11POWERMODECAM);
1356                 ret = lbs_prepare_and_send_command(priv,
1357                                             CMD_802_11_PS_MODE,
1358                                             CMD_ACT_SET,
1359                                             0, 0, &Localpsmode);
1360
1361                 if (ret) {
1362                         ret = -1;
1363                         goto done;
1364                 }
1365         }
1366
1367         if (lbs_parse_dnld_countryinfo_11d(priv, bss)) {
1368                 ret = -1;
1369                 goto done;
1370         }
1371
1372         cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_join) +
1373                                 S_DS_GEN + cmdappendsize);
1374
1375 done:
1376         lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
1377         return ret;
1378 }
1379
1380 int lbs_ret_80211_associate(struct lbs_private *priv,
1381                               struct cmd_ds_command *resp)
1382 {
1383         int ret = 0;
1384         union iwreq_data wrqu;
1385         struct ieeetypes_assocrsp *passocrsp;
1386         struct bss_descriptor *bss;
1387         u16 status_code;
1388
1389         lbs_deb_enter(LBS_DEB_ASSOC);
1390
1391         if (!priv->in_progress_assoc_req) {
1392                 lbs_deb_assoc("ASSOC_RESP: no in-progress assoc request\n");
1393                 ret = -1;
1394                 goto done;
1395         }
1396         bss = &priv->in_progress_assoc_req->bss;
1397
1398         passocrsp = (struct ieeetypes_assocrsp *) &resp->params;
1399
1400         /*
1401          * Older FW versions map the IEEE 802.11 Status Code in the association
1402          * response to the following values returned in passocrsp->statuscode:
1403          *
1404          *    IEEE Status Code                Marvell Status Code
1405          *    0                       ->      0x0000 ASSOC_RESULT_SUCCESS
1406          *    13                      ->      0x0004 ASSOC_RESULT_AUTH_REFUSED
1407          *    14                      ->      0x0004 ASSOC_RESULT_AUTH_REFUSED
1408          *    15                      ->      0x0004 ASSOC_RESULT_AUTH_REFUSED
1409          *    16                      ->      0x0004 ASSOC_RESULT_AUTH_REFUSED
1410          *    others                  ->      0x0003 ASSOC_RESULT_REFUSED
1411          *
1412          * Other response codes:
1413          *    0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused)
1414          *    0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for
1415          *                                    association response from the AP)
1416          */
1417
1418         status_code = le16_to_cpu(passocrsp->statuscode);
1419         switch (status_code) {
1420         case 0x00:
1421                 break;
1422         case 0x01:
1423                 lbs_deb_assoc("ASSOC_RESP: invalid parameters\n");
1424                 break;
1425         case 0x02:
1426                 lbs_deb_assoc("ASSOC_RESP: internal timer "
1427                         "expired while waiting for the AP\n");
1428                 break;
1429         case 0x03:
1430                 lbs_deb_assoc("ASSOC_RESP: association "
1431                         "refused by AP\n");
1432                 break;
1433         case 0x04:
1434                 lbs_deb_assoc("ASSOC_RESP: authentication "
1435                         "refused by AP\n");
1436                 break;
1437         default:
1438                 lbs_deb_assoc("ASSOC_RESP: failure reason 0x%02x "
1439                         " unknown\n", status_code);
1440                 break;
1441         }
1442
1443         if (status_code) {
1444                 lbs_mac_event_disconnected(priv);
1445                 ret = -1;
1446                 goto done;
1447         }
1448
1449         lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_RESP", (void *)&resp->params,
1450                 le16_to_cpu(resp->size) - S_DS_GEN);
1451
1452         /* Send a Media Connected event, according to the Spec */
1453         priv->connect_status = LBS_CONNECTED;
1454
1455         /* Update current SSID and BSSID */
1456         memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE);
1457         priv->curbssparams.ssid_len = bss->ssid_len;
1458         memcpy(priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
1459
1460         priv->SNR[TYPE_RXPD][TYPE_AVG] = 0;
1461         priv->NF[TYPE_RXPD][TYPE_AVG] = 0;
1462
1463         memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR));
1464         memset(priv->rawNF, 0x00, sizeof(priv->rawNF));
1465         priv->nextSNRNF = 0;
1466         priv->numSNRNF = 0;
1467
1468         netif_carrier_on(priv->dev);
1469         if (!priv->tx_pending_len)
1470                 netif_wake_queue(priv->dev);
1471
1472         memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN);
1473         wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1474         wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
1475
1476 done:
1477         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1478         return ret;
1479 }
1480
1481 int lbs_ret_80211_disassociate(struct lbs_private *priv)
1482 {
1483         lbs_deb_enter(LBS_DEB_JOIN);
1484
1485         lbs_mac_event_disconnected(priv);
1486
1487         lbs_deb_leave(LBS_DEB_JOIN);
1488         return 0;
1489 }
1490
1491 int lbs_ret_80211_ad_hoc_start(struct lbs_private *priv,
1492                                  struct cmd_ds_command *resp)
1493 {
1494         int ret = 0;
1495         u16 command = le16_to_cpu(resp->command);
1496         u16 result = le16_to_cpu(resp->result);
1497         struct cmd_ds_802_11_ad_hoc_result *padhocresult;
1498         union iwreq_data wrqu;
1499         struct bss_descriptor *bss;
1500         DECLARE_MAC_BUF(mac);
1501
1502         lbs_deb_enter(LBS_DEB_JOIN);
1503
1504         padhocresult = &resp->params.result;
1505
1506         lbs_deb_join("ADHOC_RESP: size = %d\n", le16_to_cpu(resp->size));
1507         lbs_deb_join("ADHOC_RESP: command = %x\n", command);
1508         lbs_deb_join("ADHOC_RESP: result = %x\n", result);
1509
1510         if (!priv->in_progress_assoc_req) {
1511                 lbs_deb_join("ADHOC_RESP: no in-progress association "
1512                         "request\n");
1513                 ret = -1;
1514                 goto done;
1515         }
1516         bss = &priv->in_progress_assoc_req->bss;
1517
1518         /*
1519          * Join result code 0 --> SUCCESS
1520          */
1521         if (result) {
1522                 lbs_deb_join("ADHOC_RESP: failed\n");
1523                 if (priv->connect_status == LBS_CONNECTED)
1524                         lbs_mac_event_disconnected(priv);
1525                 ret = -1;
1526                 goto done;
1527         }
1528
1529         /*
1530          * Now the join cmd should be successful
1531          * If BSSID has changed use SSID to compare instead of BSSID
1532          */
1533         lbs_deb_join("ADHOC_RESP: associated to '%s'\n",
1534                 escape_essid(bss->ssid, bss->ssid_len));
1535
1536         /* Send a Media Connected event, according to the Spec */
1537         priv->connect_status = LBS_CONNECTED;
1538
1539         if (command == CMD_RET(CMD_802_11_AD_HOC_START)) {
1540                 /* Update the created network descriptor with the new BSSID */
1541                 memcpy(bss->bssid, padhocresult->bssid, ETH_ALEN);
1542         }
1543
1544         /* Set the BSSID from the joined/started descriptor */
1545         memcpy(&priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
1546
1547         /* Set the new SSID to current SSID */
1548         memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE);
1549         priv->curbssparams.ssid_len = bss->ssid_len;
1550
1551         netif_carrier_on(priv->dev);
1552         if (!priv->tx_pending_len)
1553                 netif_wake_queue(priv->dev);
1554
1555         memset(&wrqu, 0, sizeof(wrqu));
1556         memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN);
1557         wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1558         wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
1559
1560         lbs_deb_join("ADHOC_RESP: - Joined/Started Ad Hoc\n");
1561         lbs_deb_join("ADHOC_RESP: channel = %d\n", priv->curbssparams.channel);
1562         lbs_deb_join("ADHOC_RESP: BSSID = %s\n",
1563                      print_mac(mac, padhocresult->bssid));
1564
1565 done:
1566         lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
1567         return ret;
1568 }
1569
1570 int lbs_ret_80211_ad_hoc_stop(struct lbs_private *priv)
1571 {
1572         lbs_deb_enter(LBS_DEB_JOIN);
1573
1574         lbs_mac_event_disconnected(priv);
1575
1576         lbs_deb_leave(LBS_DEB_JOIN);
1577         return 0;
1578 }