Merge branch 'irq-threaded-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / drivers / net / wireless / libertas / assoc.c
1 /* Copyright (C) 2006, Red Hat, Inc. */
2
3 #include <linux/types.h>
4 #include <linux/etherdevice.h>
5 #include <linux/ieee80211.h>
6 #include <linux/if_arp.h>
7 #include <net/lib80211.h>
8
9 #include "assoc.h"
10 #include "decl.h"
11 #include "host.h"
12 #include "scan.h"
13 #include "cmd.h"
14
15 static const u8 bssid_any[ETH_ALEN]  __attribute__ ((aligned (2))) =
16         { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
17 static const u8 bssid_off[ETH_ALEN]  __attribute__ ((aligned (2))) =
18         { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
19
20 /* The firmware needs the following bits masked out of the beacon-derived
21  * capability field when associating/joining to a BSS:
22  *  9 (QoS), 11 (APSD), 12 (unused), 14 (unused), 15 (unused)
23  */
24 #define CAPINFO_MASK    (~(0xda00))
25
26
27 /**
28  *  @brief This function finds common rates between rates and card rates.
29  *
30  * It will fill common rates in rates as output if found.
31  *
32  * NOTE: Setting the MSB of the basic rates need to be taken
33  *   care, either before or after calling this function
34  *
35  *  @param priv     A pointer to struct lbs_private structure
36  *  @param rates       the buffer which keeps input and output
37  *  @param rates_size  the size of rate1 buffer; new size of buffer on return
38  *
39  *  @return            0 on success, or -1 on error
40  */
41 static int get_common_rates(struct lbs_private *priv,
42         u8 *rates,
43         u16 *rates_size)
44 {
45         u8 *card_rates = lbs_bg_rates;
46         size_t num_card_rates = sizeof(lbs_bg_rates);
47         int ret = 0, i, j;
48         u8 tmp[30];
49         size_t tmp_size = 0;
50
51         /* For each rate in card_rates that exists in rate1, copy to tmp */
52         for (i = 0; card_rates[i] && (i < num_card_rates); i++) {
53                 for (j = 0; rates[j] && (j < *rates_size); j++) {
54                         if (rates[j] == card_rates[i])
55                                 tmp[tmp_size++] = card_rates[i];
56                 }
57         }
58
59         lbs_deb_hex(LBS_DEB_JOIN, "AP rates    ", rates, *rates_size);
60         lbs_deb_hex(LBS_DEB_JOIN, "card rates  ", card_rates, num_card_rates);
61         lbs_deb_hex(LBS_DEB_JOIN, "common rates", tmp, tmp_size);
62         lbs_deb_join("TX data rate 0x%02x\n", priv->cur_rate);
63
64         if (!priv->enablehwauto) {
65                 for (i = 0; i < tmp_size; i++) {
66                         if (tmp[i] == priv->cur_rate)
67                                 goto done;
68                 }
69                 lbs_pr_alert("Previously set fixed data rate %#x isn't "
70                        "compatible with the network.\n", priv->cur_rate);
71                 ret = -1;
72                 goto done;
73         }
74         ret = 0;
75
76 done:
77         memset(rates, 0, *rates_size);
78         *rates_size = min_t(int, tmp_size, *rates_size);
79         memcpy(rates, tmp, *rates_size);
80         return ret;
81 }
82
83
84 /**
85  *  @brief Sets the MSB on basic rates as the firmware requires
86  *
87  * Scan through an array and set the MSB for basic data rates.
88  *
89  *  @param rates     buffer of data rates
90  *  @param len       size of buffer
91  */
92 static void lbs_set_basic_rate_flags(u8 *rates, size_t len)
93 {
94         int i;
95
96         for (i = 0; i < len; i++) {
97                 if (rates[i] == 0x02 || rates[i] == 0x04 ||
98                     rates[i] == 0x0b || rates[i] == 0x16)
99                         rates[i] |= 0x80;
100         }
101 }
102
103
104 static u8 iw_auth_to_ieee_auth(u8 auth)
105 {
106         if (auth == IW_AUTH_ALG_OPEN_SYSTEM)
107                 return 0x00;
108         else if (auth == IW_AUTH_ALG_SHARED_KEY)
109                 return 0x01;
110         else if (auth == IW_AUTH_ALG_LEAP)
111                 return 0x80;
112
113         lbs_deb_join("%s: invalid auth alg 0x%X\n", __func__, auth);
114         return 0;
115 }
116
117 /**
118  *  @brief This function prepares the authenticate command.  AUTHENTICATE only
119  *  sets the authentication suite for future associations, as the firmware
120  *  handles authentication internally during the ASSOCIATE command.
121  *
122  *  @param priv      A pointer to struct lbs_private structure
123  *  @param bssid     The peer BSSID with which to authenticate
124  *  @param auth      The authentication mode to use (from wireless.h)
125  *
126  *  @return         0 or -1
127  */
128 static int lbs_set_authentication(struct lbs_private *priv, u8 bssid[6], u8 auth)
129 {
130         struct cmd_ds_802_11_authenticate cmd;
131         int ret = -1;
132         DECLARE_MAC_BUF(mac);
133
134         lbs_deb_enter(LBS_DEB_JOIN);
135
136         cmd.hdr.size = cpu_to_le16(sizeof(cmd));
137         memcpy(cmd.bssid, bssid, ETH_ALEN);
138
139         cmd.authtype = iw_auth_to_ieee_auth(auth);
140
141         lbs_deb_join("AUTH_CMD: BSSID %s, auth 0x%x\n",
142                 print_mac(mac, bssid), cmd.authtype);
143
144         ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd);
145
146         lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
147         return ret;
148 }
149
150
151 static int lbs_assoc_post(struct lbs_private *priv,
152                           struct cmd_ds_802_11_associate_response *resp)
153 {
154         int ret = 0;
155         union iwreq_data wrqu;
156         struct bss_descriptor *bss;
157         u16 status_code;
158
159         lbs_deb_enter(LBS_DEB_ASSOC);
160
161         if (!priv->in_progress_assoc_req) {
162                 lbs_deb_assoc("ASSOC_RESP: no in-progress assoc request\n");
163                 ret = -1;
164                 goto done;
165         }
166         bss = &priv->in_progress_assoc_req->bss;
167
168         /*
169          * Older FW versions map the IEEE 802.11 Status Code in the association
170          * response to the following values returned in resp->statuscode:
171          *
172          *    IEEE Status Code                Marvell Status Code
173          *    0                       ->      0x0000 ASSOC_RESULT_SUCCESS
174          *    13                      ->      0x0004 ASSOC_RESULT_AUTH_REFUSED
175          *    14                      ->      0x0004 ASSOC_RESULT_AUTH_REFUSED
176          *    15                      ->      0x0004 ASSOC_RESULT_AUTH_REFUSED
177          *    16                      ->      0x0004 ASSOC_RESULT_AUTH_REFUSED
178          *    others                  ->      0x0003 ASSOC_RESULT_REFUSED
179          *
180          * Other response codes:
181          *    0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused)
182          *    0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for
183          *                                    association response from the AP)
184          */
185
186         status_code = le16_to_cpu(resp->statuscode);
187         if (priv->fwrelease < 0x09000000) {
188                 switch (status_code) {
189                 case 0x00:
190                         break;
191                 case 0x01:
192                         lbs_deb_assoc("ASSOC_RESP: invalid parameters\n");
193                         break;
194                 case 0x02:
195                         lbs_deb_assoc("ASSOC_RESP: internal timer "
196                                 "expired while waiting for the AP\n");
197                         break;
198                 case 0x03:
199                         lbs_deb_assoc("ASSOC_RESP: association "
200                                 "refused by AP\n");
201                         break;
202                 case 0x04:
203                         lbs_deb_assoc("ASSOC_RESP: authentication "
204                                 "refused by AP\n");
205                         break;
206                 default:
207                         lbs_deb_assoc("ASSOC_RESP: failure reason 0x%02x "
208                                 " unknown\n", status_code);
209                         break;
210                 }
211         } else {
212                 /* v9+ returns the AP's association response */
213                 lbs_deb_assoc("ASSOC_RESP: failure reason 0x%02x\n", status_code);
214         }
215
216         if (status_code) {
217                 lbs_mac_event_disconnected(priv);
218                 ret = -1;
219                 goto done;
220         }
221
222         lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_RESP",
223                     (void *) (resp + sizeof (resp->hdr)),
224                     le16_to_cpu(resp->hdr.size) - sizeof (resp->hdr));
225
226         /* Send a Media Connected event, according to the Spec */
227         priv->connect_status = LBS_CONNECTED;
228
229         /* Update current SSID and BSSID */
230         memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE);
231         priv->curbssparams.ssid_len = bss->ssid_len;
232         memcpy(priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
233
234         priv->SNR[TYPE_RXPD][TYPE_AVG] = 0;
235         priv->NF[TYPE_RXPD][TYPE_AVG] = 0;
236
237         memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR));
238         memset(priv->rawNF, 0x00, sizeof(priv->rawNF));
239         priv->nextSNRNF = 0;
240         priv->numSNRNF = 0;
241
242         netif_carrier_on(priv->dev);
243         if (!priv->tx_pending_len)
244                 netif_wake_queue(priv->dev);
245
246         memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN);
247         wrqu.ap_addr.sa_family = ARPHRD_ETHER;
248         wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
249
250 done:
251         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
252         return ret;
253 }
254
255 /**
256  *  @brief This function prepares an association-class command.
257  *
258  *  @param priv      A pointer to struct lbs_private structure
259  *  @param assoc_req The association request describing the BSS to associate
260  *                   or reassociate with
261  *  @param command   The actual command, either CMD_802_11_ASSOCIATE or
262  *                   CMD_802_11_REASSOCIATE
263  *
264  *  @return         0 or -1
265  */
266 static int lbs_associate(struct lbs_private *priv,
267                          struct assoc_request *assoc_req,
268                          u16 command)
269 {
270         struct cmd_ds_802_11_associate cmd;
271         int ret = 0;
272         struct bss_descriptor *bss = &assoc_req->bss;
273         u8 *pos = &(cmd.iebuf[0]);
274         u16 tmpcap, tmplen, tmpauth;
275         struct mrvl_ie_ssid_param_set *ssid;
276         struct mrvl_ie_ds_param_set *ds;
277         struct mrvl_ie_cf_param_set *cf;
278         struct mrvl_ie_rates_param_set *rates;
279         struct mrvl_ie_rsn_param_set *rsn;
280         struct mrvl_ie_auth_type *auth;
281
282         lbs_deb_enter(LBS_DEB_ASSOC);
283
284         BUG_ON((command != CMD_802_11_ASSOCIATE) &&
285                 (command != CMD_802_11_REASSOCIATE));
286
287         memset(&cmd, 0, sizeof(cmd));
288         cmd.hdr.command = cpu_to_le16(command);
289
290         /* Fill in static fields */
291         memcpy(cmd.bssid, bss->bssid, ETH_ALEN);
292         cmd.listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL);
293
294         /* Capability info */
295         tmpcap = (bss->capability & CAPINFO_MASK);
296         if (bss->mode == IW_MODE_INFRA)
297                 tmpcap |= WLAN_CAPABILITY_ESS;
298         cmd.capability = cpu_to_le16(tmpcap);
299         lbs_deb_assoc("ASSOC_CMD: capability 0x%04x\n", tmpcap);
300
301         /* SSID */
302         ssid = (struct mrvl_ie_ssid_param_set *) pos;
303         ssid->header.type = cpu_to_le16(TLV_TYPE_SSID);
304         tmplen = bss->ssid_len;
305         ssid->header.len = cpu_to_le16(tmplen);
306         memcpy(ssid->ssid, bss->ssid, tmplen);
307         pos += sizeof(ssid->header) + tmplen;
308
309         ds = (struct mrvl_ie_ds_param_set *) pos;
310         ds->header.type = cpu_to_le16(TLV_TYPE_PHY_DS);
311         ds->header.len = cpu_to_le16(1);
312         ds->channel = bss->phy.ds.channel;
313         pos += sizeof(ds->header) + 1;
314
315         cf = (struct mrvl_ie_cf_param_set *) pos;
316         cf->header.type = cpu_to_le16(TLV_TYPE_CF);
317         tmplen = sizeof(*cf) - sizeof (cf->header);
318         cf->header.len = cpu_to_le16(tmplen);
319         /* IE payload should be zeroed, firmware fills it in for us */
320         pos += sizeof(*cf);
321
322         rates = (struct mrvl_ie_rates_param_set *) pos;
323         rates->header.type = cpu_to_le16(TLV_TYPE_RATES);
324         memcpy(&rates->rates, &bss->rates, MAX_RATES);
325         tmplen = MAX_RATES;
326         if (get_common_rates(priv, rates->rates, &tmplen)) {
327                 ret = -1;
328                 goto done;
329         }
330         pos += sizeof(rates->header) + tmplen;
331         rates->header.len = cpu_to_le16(tmplen);
332         lbs_deb_assoc("ASSOC_CMD: num rates %u\n", tmplen);
333
334         /* Copy the infra. association rates into Current BSS state structure */
335         memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
336         memcpy(&priv->curbssparams.rates, &rates->rates, tmplen);
337
338         /* Set MSB on basic rates as the firmware requires, but _after_
339          * copying to current bss rates.
340          */
341         lbs_set_basic_rate_flags(rates->rates, tmplen);
342
343         /* Firmware v9+ indicate authentication suites as a TLV */
344         if (priv->fwrelease >= 0x09000000) {
345                 DECLARE_MAC_BUF(mac);
346
347                 auth = (struct mrvl_ie_auth_type *) pos;
348                 auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
349                 auth->header.len = cpu_to_le16(2);
350                 tmpauth = iw_auth_to_ieee_auth(priv->secinfo.auth_mode);
351                 auth->auth = cpu_to_le16(tmpauth);
352                 pos += sizeof(auth->header) + 2;
353
354                 lbs_deb_join("AUTH_CMD: BSSID %s, auth 0x%x\n",
355                         print_mac(mac, bss->bssid), priv->secinfo.auth_mode);
356         }
357
358         /* WPA/WPA2 IEs */
359         if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
360                 rsn = (struct mrvl_ie_rsn_param_set *) pos;
361                 /* WPA_IE or WPA2_IE */
362                 rsn->header.type = cpu_to_le16((u16) assoc_req->wpa_ie[0]);
363                 tmplen = (u16) assoc_req->wpa_ie[1];
364                 rsn->header.len = cpu_to_le16(tmplen);
365                 memcpy(rsn->rsnie, &assoc_req->wpa_ie[2], tmplen);
366                 lbs_deb_hex(LBS_DEB_JOIN, "ASSOC_CMD: WPA/RSN IE", (u8 *) rsn,
367                         sizeof(rsn->header) + tmplen);
368                 pos += sizeof(rsn->header) + tmplen;
369         }
370
371         cmd.hdr.size = cpu_to_le16((sizeof(cmd) - sizeof(cmd.iebuf)) +
372                                    (u16)(pos - (u8 *) &cmd.iebuf));
373
374         /* update curbssparams */
375         priv->curbssparams.channel = bss->phy.ds.channel;
376
377         if (lbs_parse_dnld_countryinfo_11d(priv, bss)) {
378                 ret = -1;
379                 goto done;
380         }
381
382         ret = lbs_cmd_with_response(priv, command, &cmd);
383         if (ret == 0) {
384                 ret = lbs_assoc_post(priv,
385                         (struct cmd_ds_802_11_associate_response *) &cmd);
386         }
387
388 done:
389         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
390         return ret;
391 }
392
393 /**
394  *  @brief Associate to a specific BSS discovered in a scan
395  *
396  *  @param priv      A pointer to struct lbs_private structure
397  *  @param assoc_req The association request describing the BSS to associate with
398  *
399  *  @return          0-success, otherwise fail
400  */
401 static int lbs_try_associate(struct lbs_private *priv,
402         struct assoc_request *assoc_req)
403 {
404         int ret;
405         u8 preamble = RADIO_PREAMBLE_LONG;
406
407         lbs_deb_enter(LBS_DEB_ASSOC);
408
409         /* FW v9 and higher indicate authentication suites as a TLV in the
410          * association command, not as a separate authentication command.
411          */
412         if (priv->fwrelease < 0x09000000) {
413                 ret = lbs_set_authentication(priv, assoc_req->bss.bssid,
414                                              priv->secinfo.auth_mode);
415                 if (ret)
416                         goto out;
417         }
418
419         /* Use short preamble only when both the BSS and firmware support it */
420         if ((priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
421             (assoc_req->bss.capability & WLAN_CAPABILITY_SHORT_PREAMBLE))
422                 preamble = RADIO_PREAMBLE_SHORT;
423
424         ret = lbs_set_radio(priv, preamble, 1);
425         if (ret)
426                 goto out;
427
428         ret = lbs_associate(priv, assoc_req, CMD_802_11_ASSOCIATE);
429
430 out:
431         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
432         return ret;
433 }
434
435 static int lbs_adhoc_post(struct lbs_private *priv,
436                           struct cmd_ds_802_11_ad_hoc_result *resp)
437 {
438         int ret = 0;
439         u16 command = le16_to_cpu(resp->hdr.command);
440         u16 result = le16_to_cpu(resp->hdr.result);
441         union iwreq_data wrqu;
442         struct bss_descriptor *bss;
443         DECLARE_SSID_BUF(ssid);
444
445         lbs_deb_enter(LBS_DEB_JOIN);
446
447         if (!priv->in_progress_assoc_req) {
448                 lbs_deb_join("ADHOC_RESP: no in-progress association "
449                         "request\n");
450                 ret = -1;
451                 goto done;
452         }
453         bss = &priv->in_progress_assoc_req->bss;
454
455         /*
456          * Join result code 0 --> SUCCESS
457          */
458         if (result) {
459                 lbs_deb_join("ADHOC_RESP: failed (result 0x%X)\n", result);
460                 if (priv->connect_status == LBS_CONNECTED)
461                         lbs_mac_event_disconnected(priv);
462                 ret = -1;
463                 goto done;
464         }
465
466         /* Send a Media Connected event, according to the Spec */
467         priv->connect_status = LBS_CONNECTED;
468
469         if (command == CMD_RET(CMD_802_11_AD_HOC_START)) {
470                 /* Update the created network descriptor with the new BSSID */
471                 memcpy(bss->bssid, resp->bssid, ETH_ALEN);
472         }
473
474         /* Set the BSSID from the joined/started descriptor */
475         memcpy(&priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
476
477         /* Set the new SSID to current SSID */
478         memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE);
479         priv->curbssparams.ssid_len = bss->ssid_len;
480
481         netif_carrier_on(priv->dev);
482         if (!priv->tx_pending_len)
483                 netif_wake_queue(priv->dev);
484
485         memset(&wrqu, 0, sizeof(wrqu));
486         memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN);
487         wrqu.ap_addr.sa_family = ARPHRD_ETHER;
488         wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
489
490         lbs_deb_join("ADHOC_RESP: Joined/started '%s', BSSID %pM, channel %d\n",
491                      print_ssid(ssid, bss->ssid, bss->ssid_len),
492                      priv->curbssparams.bssid,
493                      priv->curbssparams.channel);
494
495 done:
496         lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
497         return ret;
498 }
499
500 /**
501  *  @brief Join an adhoc network found in a previous scan
502  *
503  *  @param priv         A pointer to struct lbs_private structure
504  *  @param assoc_req    The association request describing the BSS to join
505  *
506  *  @return             0 on success, error on failure
507  */
508 static int lbs_adhoc_join(struct lbs_private *priv,
509         struct assoc_request *assoc_req)
510 {
511         struct cmd_ds_802_11_ad_hoc_join cmd;
512         struct bss_descriptor *bss = &assoc_req->bss;
513         u8 preamble = RADIO_PREAMBLE_LONG;
514         DECLARE_SSID_BUF(ssid);
515         u16 ratesize = 0;
516         int ret = 0;
517
518         lbs_deb_enter(LBS_DEB_ASSOC);
519
520         lbs_deb_join("current SSID '%s', ssid length %u\n",
521                 print_ssid(ssid, priv->curbssparams.ssid,
522                 priv->curbssparams.ssid_len),
523                 priv->curbssparams.ssid_len);
524         lbs_deb_join("requested ssid '%s', ssid length %u\n",
525                 print_ssid(ssid, bss->ssid, bss->ssid_len),
526                 bss->ssid_len);
527
528         /* check if the requested SSID is already joined */
529         if (priv->curbssparams.ssid_len &&
530             !lbs_ssid_cmp(priv->curbssparams.ssid,
531                         priv->curbssparams.ssid_len,
532                         bss->ssid, bss->ssid_len) &&
533             (priv->mode == IW_MODE_ADHOC) &&
534             (priv->connect_status == LBS_CONNECTED)) {
535                 union iwreq_data wrqu;
536
537                 lbs_deb_join("ADHOC_J_CMD: New ad-hoc SSID is the same as "
538                         "current, not attempting to re-join");
539
540                 /* Send the re-association event though, because the association
541                  * request really was successful, even if just a null-op.
542                  */
543                 memset(&wrqu, 0, sizeof(wrqu));
544                 memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid,
545                        ETH_ALEN);
546                 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
547                 wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
548                 goto out;
549         }
550
551         /* Use short preamble only when both the BSS and firmware support it */
552         if ((priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
553             (bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)) {
554                 lbs_deb_join("AdhocJoin: Short preamble\n");
555                 preamble = RADIO_PREAMBLE_SHORT;
556         }
557
558         ret = lbs_set_radio(priv, preamble, 1);
559         if (ret)
560                 goto out;
561
562         lbs_deb_join("AdhocJoin: channel = %d\n", assoc_req->channel);
563         lbs_deb_join("AdhocJoin: band = %c\n", assoc_req->band);
564
565         priv->adhoccreate = 0;
566         priv->curbssparams.channel = bss->channel;
567
568         /* Build the join command */
569         memset(&cmd, 0, sizeof(cmd));
570         cmd.hdr.size = cpu_to_le16(sizeof(cmd));
571
572         cmd.bss.type = CMD_BSS_TYPE_IBSS;
573         cmd.bss.beaconperiod = cpu_to_le16(bss->beaconperiod);
574
575         memcpy(&cmd.bss.bssid, &bss->bssid, ETH_ALEN);
576         memcpy(&cmd.bss.ssid, &bss->ssid, bss->ssid_len);
577
578         memcpy(&cmd.bss.ds, &bss->phy.ds, sizeof(struct ieee_ie_ds_param_set));
579
580         memcpy(&cmd.bss.ibss, &bss->ss.ibss,
581                sizeof(struct ieee_ie_ibss_param_set));
582
583         cmd.bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK);
584         lbs_deb_join("ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n",
585                bss->capability, CAPINFO_MASK);
586
587         /* information on BSSID descriptor passed to FW */
588         lbs_deb_join("ADHOC_J_CMD: BSSID = %pM, SSID = '%s'\n",
589                         cmd.bss.bssid, cmd.bss.ssid);
590
591         /* Only v8 and below support setting these */
592         if (priv->fwrelease < 0x09000000) {
593                 /* failtimeout */
594                 cmd.failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT);
595                 /* probedelay */
596                 cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
597         }
598
599         /* Copy Data rates from the rates recorded in scan response */
600         memset(cmd.bss.rates, 0, sizeof(cmd.bss.rates));
601         ratesize = min_t(u16, sizeof(cmd.bss.rates), MAX_RATES);
602         memcpy(cmd.bss.rates, bss->rates, ratesize);
603         if (get_common_rates(priv, cmd.bss.rates, &ratesize)) {
604                 lbs_deb_join("ADHOC_JOIN: get_common_rates returned error.\n");
605                 ret = -1;
606                 goto out;
607         }
608
609         /* Copy the ad-hoc creation rates into Current BSS state structure */
610         memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
611         memcpy(&priv->curbssparams.rates, cmd.bss.rates, ratesize);
612
613         /* Set MSB on basic rates as the firmware requires, but _after_
614          * copying to current bss rates.
615          */
616         lbs_set_basic_rate_flags(cmd.bss.rates, ratesize);
617
618         cmd.bss.ibss.atimwindow = bss->atimwindow;
619
620         if (assoc_req->secinfo.wep_enabled) {
621                 u16 tmp = le16_to_cpu(cmd.bss.capability);
622                 tmp |= WLAN_CAPABILITY_PRIVACY;
623                 cmd.bss.capability = cpu_to_le16(tmp);
624         }
625
626         if (priv->psmode == LBS802_11POWERMODEMAX_PSP) {
627                 __le32 local_ps_mode = cpu_to_le32(LBS802_11POWERMODECAM);
628
629                 /* wake up first */
630                 ret = lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE,
631                                                    CMD_ACT_SET, 0, 0,
632                                                    &local_ps_mode);
633                 if (ret) {
634                         ret = -1;
635                         goto out;
636                 }
637         }
638
639         if (lbs_parse_dnld_countryinfo_11d(priv, bss)) {
640                 ret = -1;
641                 goto out;
642         }
643
644         ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd);
645         if (ret == 0) {
646                 ret = lbs_adhoc_post(priv,
647                                      (struct cmd_ds_802_11_ad_hoc_result *)&cmd);
648         }
649
650 out:
651         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
652         return ret;
653 }
654
655 /**
656  *  @brief Start an Adhoc Network
657  *
658  *  @param priv         A pointer to struct lbs_private structure
659  *  @param assoc_req    The association request describing the BSS to start
660  *
661  *  @return             0 on success, error on failure
662  */
663 static int lbs_adhoc_start(struct lbs_private *priv,
664         struct assoc_request *assoc_req)
665 {
666         struct cmd_ds_802_11_ad_hoc_start cmd;
667         u8 preamble = RADIO_PREAMBLE_LONG;
668         size_t ratesize = 0;
669         u16 tmpcap = 0;
670         int ret = 0;
671         DECLARE_SSID_BUF(ssid);
672
673         lbs_deb_enter(LBS_DEB_ASSOC);
674
675         if (priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) {
676                 lbs_deb_join("ADHOC_START: Will use short preamble\n");
677                 preamble = RADIO_PREAMBLE_SHORT;
678         }
679
680         ret = lbs_set_radio(priv, preamble, 1);
681         if (ret)
682                 goto out;
683
684         /* Build the start command */
685         memset(&cmd, 0, sizeof(cmd));
686         cmd.hdr.size = cpu_to_le16(sizeof(cmd));
687
688         memcpy(cmd.ssid, assoc_req->ssid, assoc_req->ssid_len);
689
690         lbs_deb_join("ADHOC_START: SSID '%s', ssid length %u\n",
691                 print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len),
692                 assoc_req->ssid_len);
693
694         cmd.bsstype = CMD_BSS_TYPE_IBSS;
695
696         if (priv->beacon_period == 0)
697                 priv->beacon_period = MRVDRV_BEACON_INTERVAL;
698         cmd.beaconperiod = cpu_to_le16(priv->beacon_period);
699
700         WARN_ON(!assoc_req->channel);
701
702         /* set Physical parameter set */
703         cmd.ds.header.id = WLAN_EID_DS_PARAMS;
704         cmd.ds.header.len = 1;
705         cmd.ds.channel = assoc_req->channel;
706
707         /* set IBSS parameter set */
708         cmd.ibss.header.id = WLAN_EID_IBSS_PARAMS;
709         cmd.ibss.header.len = 2;
710         cmd.ibss.atimwindow = cpu_to_le16(0);
711
712         /* set capability info */
713         tmpcap = WLAN_CAPABILITY_IBSS;
714         if (assoc_req->secinfo.wep_enabled ||
715             assoc_req->secinfo.WPAenabled ||
716             assoc_req->secinfo.WPA2enabled) {
717                 lbs_deb_join("ADHOC_START: WEP/WPA enabled, privacy on\n");
718                 tmpcap |= WLAN_CAPABILITY_PRIVACY;
719         } else
720                 lbs_deb_join("ADHOC_START: WEP disabled, privacy off\n");
721
722         cmd.capability = cpu_to_le16(tmpcap);
723
724         /* Only v8 and below support setting probe delay */
725         if (priv->fwrelease < 0x09000000)
726                 cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
727
728         ratesize = min(sizeof(cmd.rates), sizeof(lbs_bg_rates));
729         memcpy(cmd.rates, lbs_bg_rates, ratesize);
730
731         /* Copy the ad-hoc creating rates into Current BSS state structure */
732         memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
733         memcpy(&priv->curbssparams.rates, &cmd.rates, ratesize);
734
735         /* Set MSB on basic rates as the firmware requires, but _after_
736          * copying to current bss rates.
737          */
738         lbs_set_basic_rate_flags(cmd.rates, ratesize);
739
740         lbs_deb_join("ADHOC_START: rates=%02x %02x %02x %02x\n",
741                cmd.rates[0], cmd.rates[1], cmd.rates[2], cmd.rates[3]);
742
743         if (lbs_create_dnld_countryinfo_11d(priv)) {
744                 lbs_deb_join("ADHOC_START: dnld_countryinfo_11d failed\n");
745                 ret = -1;
746                 goto out;
747         }
748
749         lbs_deb_join("ADHOC_START: Starting Ad-Hoc BSS on channel %d, band %d\n",
750                      assoc_req->channel, assoc_req->band);
751
752         priv->adhoccreate = 1;
753         priv->mode = IW_MODE_ADHOC;
754
755         ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_START, &cmd);
756         if (ret == 0)
757                 ret = lbs_adhoc_post(priv,
758                                      (struct cmd_ds_802_11_ad_hoc_result *)&cmd);
759
760 out:
761         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
762         return ret;
763 }
764
765 /**
766  *  @brief Stop and Ad-Hoc network and exit Ad-Hoc mode
767  *
768  *  @param priv         A pointer to struct lbs_private structure
769  *  @return             0 on success, or an error
770  */
771 int lbs_adhoc_stop(struct lbs_private *priv)
772 {
773         struct cmd_ds_802_11_ad_hoc_stop cmd;
774         int ret;
775
776         lbs_deb_enter(LBS_DEB_JOIN);
777
778         memset(&cmd, 0, sizeof (cmd));
779         cmd.hdr.size = cpu_to_le16 (sizeof (cmd));
780
781         ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd);
782
783         /* Clean up everything even if there was an error */
784         lbs_mac_event_disconnected(priv);
785
786         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
787         return ret;
788 }
789
790 static inline int match_bss_no_security(struct lbs_802_11_security *secinfo,
791                                         struct bss_descriptor *match_bss)
792 {
793         if (!secinfo->wep_enabled  && !secinfo->WPAenabled
794             && !secinfo->WPA2enabled
795             && match_bss->wpa_ie[0] != WLAN_EID_GENERIC
796             && match_bss->rsn_ie[0] != WLAN_EID_RSN
797             && !(match_bss->capability & WLAN_CAPABILITY_PRIVACY))
798                 return 1;
799         else
800                 return 0;
801 }
802
803 static inline int match_bss_static_wep(struct lbs_802_11_security *secinfo,
804                                        struct bss_descriptor *match_bss)
805 {
806         if (secinfo->wep_enabled && !secinfo->WPAenabled
807             && !secinfo->WPA2enabled
808             && (match_bss->capability & WLAN_CAPABILITY_PRIVACY))
809                 return 1;
810         else
811                 return 0;
812 }
813
814 static inline int match_bss_wpa(struct lbs_802_11_security *secinfo,
815                                 struct bss_descriptor *match_bss)
816 {
817         if (!secinfo->wep_enabled && secinfo->WPAenabled
818             && (match_bss->wpa_ie[0] == WLAN_EID_GENERIC)
819             /* privacy bit may NOT be set in some APs like LinkSys WRT54G
820             && (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */
821            )
822                 return 1;
823         else
824                 return 0;
825 }
826
827 static inline int match_bss_wpa2(struct lbs_802_11_security *secinfo,
828                                  struct bss_descriptor *match_bss)
829 {
830         if (!secinfo->wep_enabled && secinfo->WPA2enabled &&
831             (match_bss->rsn_ie[0] == WLAN_EID_RSN)
832             /* privacy bit may NOT be set in some APs like LinkSys WRT54G
833             (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */
834            )
835                 return 1;
836         else
837                 return 0;
838 }
839
840 static inline int match_bss_dynamic_wep(struct lbs_802_11_security *secinfo,
841                                         struct bss_descriptor *match_bss)
842 {
843         if (!secinfo->wep_enabled && !secinfo->WPAenabled
844             && !secinfo->WPA2enabled
845             && (match_bss->wpa_ie[0] != WLAN_EID_GENERIC)
846             && (match_bss->rsn_ie[0] != WLAN_EID_RSN)
847             && (match_bss->capability & WLAN_CAPABILITY_PRIVACY))
848                 return 1;
849         else
850                 return 0;
851 }
852
853 /**
854  *  @brief Check if a scanned network compatible with the driver settings
855  *
856  *   WEP     WPA     WPA2    ad-hoc  encrypt                      Network
857  * enabled enabled  enabled   AES     mode   privacy  WPA  WPA2  Compatible
858  *    0       0        0       0      NONE      0      0    0   yes No security
859  *    1       0        0       0      NONE      1      0    0   yes Static WEP
860  *    0       1        0       0       x        1x     1    x   yes WPA
861  *    0       0        1       0       x        1x     x    1   yes WPA2
862  *    0       0        0       1      NONE      1      0    0   yes Ad-hoc AES
863  *    0       0        0       0     !=NONE     1      0    0   yes Dynamic WEP
864  *
865  *
866  *  @param priv A pointer to struct lbs_private
867  *  @param index   Index in scantable to check against current driver settings
868  *  @param mode    Network mode: Infrastructure or IBSS
869  *
870  *  @return        Index in scantable, or error code if negative
871  */
872 static int is_network_compatible(struct lbs_private *priv,
873                                  struct bss_descriptor *bss, uint8_t mode)
874 {
875         int matched = 0;
876
877         lbs_deb_enter(LBS_DEB_SCAN);
878
879         if (bss->mode != mode)
880                 goto done;
881
882         matched = match_bss_no_security(&priv->secinfo, bss);
883         if (matched)
884                 goto done;
885         matched = match_bss_static_wep(&priv->secinfo, bss);
886         if (matched)
887                 goto done;
888         matched = match_bss_wpa(&priv->secinfo, bss);
889         if (matched) {
890                 lbs_deb_scan("is_network_compatible() WPA: wpa_ie 0x%x "
891                              "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s "
892                              "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0],
893                              priv->secinfo.wep_enabled ? "e" : "d",
894                              priv->secinfo.WPAenabled ? "e" : "d",
895                              priv->secinfo.WPA2enabled ? "e" : "d",
896                              (bss->capability & WLAN_CAPABILITY_PRIVACY));
897                 goto done;
898         }
899         matched = match_bss_wpa2(&priv->secinfo, bss);
900         if (matched) {
901                 lbs_deb_scan("is_network_compatible() WPA2: wpa_ie 0x%x "
902                              "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s "
903                              "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0],
904                              priv->secinfo.wep_enabled ? "e" : "d",
905                              priv->secinfo.WPAenabled ? "e" : "d",
906                              priv->secinfo.WPA2enabled ? "e" : "d",
907                              (bss->capability & WLAN_CAPABILITY_PRIVACY));
908                 goto done;
909         }
910         matched = match_bss_dynamic_wep(&priv->secinfo, bss);
911         if (matched) {
912                 lbs_deb_scan("is_network_compatible() dynamic WEP: "
913                              "wpa_ie 0x%x wpa2_ie 0x%x privacy 0x%x\n",
914                              bss->wpa_ie[0], bss->rsn_ie[0],
915                              (bss->capability & WLAN_CAPABILITY_PRIVACY));
916                 goto done;
917         }
918
919         /* bss security settings don't match those configured on card */
920         lbs_deb_scan("is_network_compatible() FAILED: wpa_ie 0x%x "
921                      "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s privacy 0x%x\n",
922                      bss->wpa_ie[0], bss->rsn_ie[0],
923                      priv->secinfo.wep_enabled ? "e" : "d",
924                      priv->secinfo.WPAenabled ? "e" : "d",
925                      priv->secinfo.WPA2enabled ? "e" : "d",
926                      (bss->capability & WLAN_CAPABILITY_PRIVACY));
927
928 done:
929         lbs_deb_leave_args(LBS_DEB_SCAN, "matched: %d", matched);
930         return matched;
931 }
932
933 /**
934  *  @brief This function finds a specific compatible BSSID in the scan list
935  *
936  *  Used in association code
937  *
938  *  @param priv  A pointer to struct lbs_private
939  *  @param bssid    BSSID to find in the scan list
940  *  @param mode     Network mode: Infrastructure or IBSS
941  *
942  *  @return         index in BSSID list, or error return code (< 0)
943  */
944 static struct bss_descriptor *lbs_find_bssid_in_list(struct lbs_private *priv,
945                                               uint8_t *bssid, uint8_t mode)
946 {
947         struct bss_descriptor *iter_bss;
948         struct bss_descriptor *found_bss = NULL;
949
950         lbs_deb_enter(LBS_DEB_SCAN);
951
952         if (!bssid)
953                 goto out;
954
955         lbs_deb_hex(LBS_DEB_SCAN, "looking for", bssid, ETH_ALEN);
956
957         /* Look through the scan table for a compatible match.  The loop will
958          *   continue past a matched bssid that is not compatible in case there
959          *   is an AP with multiple SSIDs assigned to the same BSSID
960          */
961         mutex_lock(&priv->lock);
962         list_for_each_entry(iter_bss, &priv->network_list, list) {
963                 if (compare_ether_addr(iter_bss->bssid, bssid))
964                         continue; /* bssid doesn't match */
965                 switch (mode) {
966                 case IW_MODE_INFRA:
967                 case IW_MODE_ADHOC:
968                         if (!is_network_compatible(priv, iter_bss, mode))
969                                 break;
970                         found_bss = iter_bss;
971                         break;
972                 default:
973                         found_bss = iter_bss;
974                         break;
975                 }
976         }
977         mutex_unlock(&priv->lock);
978
979 out:
980         lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss);
981         return found_bss;
982 }
983
984 /**
985  *  @brief This function finds ssid in ssid list.
986  *
987  *  Used in association code
988  *
989  *  @param priv  A pointer to struct lbs_private
990  *  @param ssid     SSID to find in the list
991  *  @param bssid    BSSID to qualify the SSID selection (if provided)
992  *  @param mode     Network mode: Infrastructure or IBSS
993  *
994  *  @return         index in BSSID list
995  */
996 static struct bss_descriptor *lbs_find_ssid_in_list(struct lbs_private *priv,
997                                              uint8_t *ssid, uint8_t ssid_len,
998                                              uint8_t *bssid, uint8_t mode,
999                                              int channel)
1000 {
1001         u32 bestrssi = 0;
1002         struct bss_descriptor *iter_bss = NULL;
1003         struct bss_descriptor *found_bss = NULL;
1004         struct bss_descriptor *tmp_oldest = NULL;
1005
1006         lbs_deb_enter(LBS_DEB_SCAN);
1007
1008         mutex_lock(&priv->lock);
1009
1010         list_for_each_entry(iter_bss, &priv->network_list, list) {
1011                 if (!tmp_oldest ||
1012                     (iter_bss->last_scanned < tmp_oldest->last_scanned))
1013                         tmp_oldest = iter_bss;
1014
1015                 if (lbs_ssid_cmp(iter_bss->ssid, iter_bss->ssid_len,
1016                                  ssid, ssid_len) != 0)
1017                         continue; /* ssid doesn't match */
1018                 if (bssid && compare_ether_addr(iter_bss->bssid, bssid) != 0)
1019                         continue; /* bssid doesn't match */
1020                 if ((channel > 0) && (iter_bss->channel != channel))
1021                         continue; /* channel doesn't match */
1022
1023                 switch (mode) {
1024                 case IW_MODE_INFRA:
1025                 case IW_MODE_ADHOC:
1026                         if (!is_network_compatible(priv, iter_bss, mode))
1027                                 break;
1028
1029                         if (bssid) {
1030                                 /* Found requested BSSID */
1031                                 found_bss = iter_bss;
1032                                 goto out;
1033                         }
1034
1035                         if (SCAN_RSSI(iter_bss->rssi) > bestrssi) {
1036                                 bestrssi = SCAN_RSSI(iter_bss->rssi);
1037                                 found_bss = iter_bss;
1038                         }
1039                         break;
1040                 case IW_MODE_AUTO:
1041                 default:
1042                         if (SCAN_RSSI(iter_bss->rssi) > bestrssi) {
1043                                 bestrssi = SCAN_RSSI(iter_bss->rssi);
1044                                 found_bss = iter_bss;
1045                         }
1046                         break;
1047                 }
1048         }
1049
1050 out:
1051         mutex_unlock(&priv->lock);
1052         lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss);
1053         return found_bss;
1054 }
1055
1056 static int assoc_helper_essid(struct lbs_private *priv,
1057                               struct assoc_request * assoc_req)
1058 {
1059         int ret = 0;
1060         struct bss_descriptor * bss;
1061         int channel = -1;
1062         DECLARE_SSID_BUF(ssid);
1063
1064         lbs_deb_enter(LBS_DEB_ASSOC);
1065
1066         /* FIXME: take channel into account when picking SSIDs if a channel
1067          * is set.
1068          */
1069
1070         if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
1071                 channel = assoc_req->channel;
1072
1073         lbs_deb_assoc("SSID '%s' requested\n",
1074                       print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len));
1075         if (assoc_req->mode == IW_MODE_INFRA) {
1076                 lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
1077                         assoc_req->ssid_len);
1078
1079                 bss = lbs_find_ssid_in_list(priv, assoc_req->ssid,
1080                                 assoc_req->ssid_len, NULL, IW_MODE_INFRA, channel);
1081                 if (bss != NULL) {
1082                         memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
1083                         ret = lbs_try_associate(priv, assoc_req);
1084                 } else {
1085                         lbs_deb_assoc("SSID not found; cannot associate\n");
1086                 }
1087         } else if (assoc_req->mode == IW_MODE_ADHOC) {
1088                 /* Scan for the network, do not save previous results.  Stale
1089                  *   scan data will cause us to join a non-existant adhoc network
1090                  */
1091                 lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
1092                         assoc_req->ssid_len);
1093
1094                 /* Search for the requested SSID in the scan table */
1095                 bss = lbs_find_ssid_in_list(priv, assoc_req->ssid,
1096                                 assoc_req->ssid_len, NULL, IW_MODE_ADHOC, channel);
1097                 if (bss != NULL) {
1098                         lbs_deb_assoc("SSID found, will join\n");
1099                         memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
1100                         lbs_adhoc_join(priv, assoc_req);
1101                 } else {
1102                         /* else send START command */
1103                         lbs_deb_assoc("SSID not found, creating adhoc network\n");
1104                         memcpy(&assoc_req->bss.ssid, &assoc_req->ssid,
1105                                 IW_ESSID_MAX_SIZE);
1106                         assoc_req->bss.ssid_len = assoc_req->ssid_len;
1107                         lbs_adhoc_start(priv, assoc_req);
1108                 }
1109         }
1110
1111         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1112         return ret;
1113 }
1114
1115
1116 static int assoc_helper_bssid(struct lbs_private *priv,
1117                               struct assoc_request * assoc_req)
1118 {
1119         int ret = 0;
1120         struct bss_descriptor * bss;
1121
1122         lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID %pM", assoc_req->bssid);
1123
1124         /* Search for index position in list for requested MAC */
1125         bss = lbs_find_bssid_in_list(priv, assoc_req->bssid,
1126                             assoc_req->mode);
1127         if (bss == NULL) {
1128                 lbs_deb_assoc("ASSOC: WAP: BSSID %pM not found, "
1129                         "cannot associate.\n", assoc_req->bssid);
1130                 goto out;
1131         }
1132
1133         memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
1134         if (assoc_req->mode == IW_MODE_INFRA) {
1135                 ret = lbs_try_associate(priv, assoc_req);
1136                 lbs_deb_assoc("ASSOC: lbs_try_associate(bssid) returned %d\n",
1137                               ret);
1138         } else if (assoc_req->mode == IW_MODE_ADHOC) {
1139                 lbs_adhoc_join(priv, assoc_req);
1140         }
1141
1142 out:
1143         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1144         return ret;
1145 }
1146
1147
1148 static int assoc_helper_associate(struct lbs_private *priv,
1149                                   struct assoc_request * assoc_req)
1150 {
1151         int ret = 0, done = 0;
1152
1153         lbs_deb_enter(LBS_DEB_ASSOC);
1154
1155         /* If we're given and 'any' BSSID, try associating based on SSID */
1156
1157         if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
1158                 if (compare_ether_addr(bssid_any, assoc_req->bssid)
1159                     && compare_ether_addr(bssid_off, assoc_req->bssid)) {
1160                         ret = assoc_helper_bssid(priv, assoc_req);
1161                         done = 1;
1162                 }
1163         }
1164
1165         if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
1166                 ret = assoc_helper_essid(priv, assoc_req);
1167         }
1168
1169         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1170         return ret;
1171 }
1172
1173
1174 static int assoc_helper_mode(struct lbs_private *priv,
1175                              struct assoc_request * assoc_req)
1176 {
1177         int ret = 0;
1178
1179         lbs_deb_enter(LBS_DEB_ASSOC);
1180
1181         if (assoc_req->mode == priv->mode)
1182                 goto done;
1183
1184         if (assoc_req->mode == IW_MODE_INFRA) {
1185                 if (priv->psstate != PS_STATE_FULL_POWER)
1186                         lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
1187                 priv->psmode = LBS802_11POWERMODECAM;
1188         }
1189
1190         priv->mode = assoc_req->mode;
1191         ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, assoc_req->mode);
1192
1193 done:
1194         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1195         return ret;
1196 }
1197
1198 static int assoc_helper_channel(struct lbs_private *priv,
1199                                 struct assoc_request * assoc_req)
1200 {
1201         int ret = 0;
1202
1203         lbs_deb_enter(LBS_DEB_ASSOC);
1204
1205         ret = lbs_update_channel(priv);
1206         if (ret) {
1207                 lbs_deb_assoc("ASSOC: channel: error getting channel.\n");
1208                 goto done;
1209         }
1210
1211         if (assoc_req->channel == priv->curbssparams.channel)
1212                 goto done;
1213
1214         if (priv->mesh_dev) {
1215                 /* Change mesh channel first; 21.p21 firmware won't let
1216                    you change channel otherwise (even though it'll return
1217                    an error to this */
1218                 lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP,
1219                                 assoc_req->channel);
1220         }
1221
1222         lbs_deb_assoc("ASSOC: channel: %d -> %d\n",
1223                       priv->curbssparams.channel, assoc_req->channel);
1224
1225         ret = lbs_set_channel(priv, assoc_req->channel);
1226         if (ret < 0)
1227                 lbs_deb_assoc("ASSOC: channel: error setting channel.\n");
1228
1229         /* FIXME: shouldn't need to grab the channel _again_ after setting
1230          * it since the firmware is supposed to return the new channel, but
1231          * whatever... */
1232         ret = lbs_update_channel(priv);
1233         if (ret) {
1234                 lbs_deb_assoc("ASSOC: channel: error getting channel.\n");
1235                 goto done;
1236         }
1237
1238         if (assoc_req->channel != priv->curbssparams.channel) {
1239                 lbs_deb_assoc("ASSOC: channel: failed to update channel to %d\n",
1240                               assoc_req->channel);
1241                 goto restore_mesh;
1242         }
1243
1244         if (   assoc_req->secinfo.wep_enabled
1245             &&   (assoc_req->wep_keys[0].len
1246                || assoc_req->wep_keys[1].len
1247                || assoc_req->wep_keys[2].len
1248                || assoc_req->wep_keys[3].len)) {
1249                 /* Make sure WEP keys are re-sent to firmware */
1250                 set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
1251         }
1252
1253         /* Must restart/rejoin adhoc networks after channel change */
1254         set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
1255
1256  restore_mesh:
1257         if (priv->mesh_dev)
1258                 lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
1259                                 priv->curbssparams.channel);
1260
1261  done:
1262         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1263         return ret;
1264 }
1265
1266
1267 static int assoc_helper_wep_keys(struct lbs_private *priv,
1268                                  struct assoc_request *assoc_req)
1269 {
1270         int i;
1271         int ret = 0;
1272
1273         lbs_deb_enter(LBS_DEB_ASSOC);
1274
1275         /* Set or remove WEP keys */
1276         if (assoc_req->wep_keys[0].len || assoc_req->wep_keys[1].len ||
1277             assoc_req->wep_keys[2].len || assoc_req->wep_keys[3].len)
1278                 ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_ADD, assoc_req);
1279         else
1280                 ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_REMOVE, assoc_req);
1281
1282         if (ret)
1283                 goto out;
1284
1285         /* enable/disable the MAC's WEP packet filter */
1286         if (assoc_req->secinfo.wep_enabled)
1287                 priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE;
1288         else
1289                 priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE;
1290
1291         lbs_set_mac_control(priv);
1292
1293         mutex_lock(&priv->lock);
1294
1295         /* Copy WEP keys into priv wep key fields */
1296         for (i = 0; i < 4; i++) {
1297                 memcpy(&priv->wep_keys[i], &assoc_req->wep_keys[i],
1298                        sizeof(struct enc_key));
1299         }
1300         priv->wep_tx_keyidx = assoc_req->wep_tx_keyidx;
1301
1302         mutex_unlock(&priv->lock);
1303
1304 out:
1305         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1306         return ret;
1307 }
1308
1309 static int assoc_helper_secinfo(struct lbs_private *priv,
1310                                 struct assoc_request * assoc_req)
1311 {
1312         int ret = 0;
1313         uint16_t do_wpa;
1314         uint16_t rsn = 0;
1315
1316         lbs_deb_enter(LBS_DEB_ASSOC);
1317
1318         memcpy(&priv->secinfo, &assoc_req->secinfo,
1319                 sizeof(struct lbs_802_11_security));
1320
1321         lbs_set_mac_control(priv);
1322
1323         /* If RSN is already enabled, don't try to enable it again, since
1324          * ENABLE_RSN resets internal state machines and will clobber the
1325          * 4-way WPA handshake.
1326          */
1327
1328         /* Get RSN enabled/disabled */
1329         ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_GET, &rsn);
1330         if (ret) {
1331                 lbs_deb_assoc("Failed to get RSN status: %d\n", ret);
1332                 goto out;
1333         }
1334
1335         /* Don't re-enable RSN if it's already enabled */
1336         do_wpa = assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled;
1337         if (do_wpa == rsn)
1338                 goto out;
1339
1340         /* Set RSN enabled/disabled */
1341         ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_SET, &do_wpa);
1342
1343 out:
1344         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1345         return ret;
1346 }
1347
1348
1349 static int assoc_helper_wpa_keys(struct lbs_private *priv,
1350                                  struct assoc_request * assoc_req)
1351 {
1352         int ret = 0;
1353         unsigned int flags = assoc_req->flags;
1354
1355         lbs_deb_enter(LBS_DEB_ASSOC);
1356
1357         /* Work around older firmware bug where WPA unicast and multicast
1358          * keys must be set independently.  Seen in SDIO parts with firmware
1359          * version 5.0.11p0.
1360          */
1361
1362         if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
1363                 clear_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
1364                 ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
1365                 assoc_req->flags = flags;
1366         }
1367
1368         if (ret)
1369                 goto out;
1370
1371         if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
1372                 clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
1373
1374                 ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
1375                 assoc_req->flags = flags;
1376         }
1377
1378 out:
1379         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1380         return ret;
1381 }
1382
1383
1384 static int assoc_helper_wpa_ie(struct lbs_private *priv,
1385                                struct assoc_request * assoc_req)
1386 {
1387         int ret = 0;
1388
1389         lbs_deb_enter(LBS_DEB_ASSOC);
1390
1391         if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
1392                 memcpy(&priv->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len);
1393                 priv->wpa_ie_len = assoc_req->wpa_ie_len;
1394         } else {
1395                 memset(&priv->wpa_ie, 0, MAX_WPA_IE_LEN);
1396                 priv->wpa_ie_len = 0;
1397         }
1398
1399         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1400         return ret;
1401 }
1402
1403
1404 static int should_deauth_infrastructure(struct lbs_private *priv,
1405                                         struct assoc_request * assoc_req)
1406 {
1407         int ret = 0;
1408
1409         if (priv->connect_status != LBS_CONNECTED)
1410                 return 0;
1411
1412         lbs_deb_enter(LBS_DEB_ASSOC);
1413         if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
1414                 lbs_deb_assoc("Deauthenticating due to new SSID\n");
1415                 ret = 1;
1416                 goto out;
1417         }
1418
1419         if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
1420                 if (priv->secinfo.auth_mode != assoc_req->secinfo.auth_mode) {
1421                         lbs_deb_assoc("Deauthenticating due to new security\n");
1422                         ret = 1;
1423                         goto out;
1424                 }
1425         }
1426
1427         if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
1428                 lbs_deb_assoc("Deauthenticating due to new BSSID\n");
1429                 ret = 1;
1430                 goto out;
1431         }
1432
1433         if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
1434                 lbs_deb_assoc("Deauthenticating due to channel switch\n");
1435                 ret = 1;
1436                 goto out;
1437         }
1438
1439         /* FIXME: deal with 'auto' mode somehow */
1440         if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
1441                 if (assoc_req->mode != IW_MODE_INFRA) {
1442                         lbs_deb_assoc("Deauthenticating due to leaving "
1443                                 "infra mode\n");
1444                         ret = 1;
1445                         goto out;
1446                 }
1447         }
1448
1449 out:
1450         lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1451         return ret;
1452 }
1453
1454
1455 static int should_stop_adhoc(struct lbs_private *priv,
1456                              struct assoc_request * assoc_req)
1457 {
1458         lbs_deb_enter(LBS_DEB_ASSOC);
1459
1460         if (priv->connect_status != LBS_CONNECTED)
1461                 return 0;
1462
1463         if (lbs_ssid_cmp(priv->curbssparams.ssid,
1464                               priv->curbssparams.ssid_len,
1465                               assoc_req->ssid, assoc_req->ssid_len) != 0)
1466                 return 1;
1467
1468         /* FIXME: deal with 'auto' mode somehow */
1469         if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
1470                 if (assoc_req->mode != IW_MODE_ADHOC)
1471                         return 1;
1472         }
1473
1474         if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
1475                 if (assoc_req->channel != priv->curbssparams.channel)
1476                         return 1;
1477         }
1478
1479         lbs_deb_leave(LBS_DEB_ASSOC);
1480         return 0;
1481 }
1482
1483
1484 /**
1485  *  @brief This function finds the best SSID in the Scan List
1486  *
1487  *  Search the scan table for the best SSID that also matches the current
1488  *   adapter network preference (infrastructure or adhoc)
1489  *
1490  *  @param priv  A pointer to struct lbs_private
1491  *
1492  *  @return         index in BSSID list
1493  */
1494 static struct bss_descriptor *lbs_find_best_ssid_in_list(
1495         struct lbs_private *priv, uint8_t mode)
1496 {
1497         uint8_t bestrssi = 0;
1498         struct bss_descriptor *iter_bss;
1499         struct bss_descriptor *best_bss = NULL;
1500
1501         lbs_deb_enter(LBS_DEB_SCAN);
1502
1503         mutex_lock(&priv->lock);
1504
1505         list_for_each_entry(iter_bss, &priv->network_list, list) {
1506                 switch (mode) {
1507                 case IW_MODE_INFRA:
1508                 case IW_MODE_ADHOC:
1509                         if (!is_network_compatible(priv, iter_bss, mode))
1510                                 break;
1511                         if (SCAN_RSSI(iter_bss->rssi) <= bestrssi)
1512                                 break;
1513                         bestrssi = SCAN_RSSI(iter_bss->rssi);
1514                         best_bss = iter_bss;
1515                         break;
1516                 case IW_MODE_AUTO:
1517                 default:
1518                         if (SCAN_RSSI(iter_bss->rssi) <= bestrssi)
1519                                 break;
1520                         bestrssi = SCAN_RSSI(iter_bss->rssi);
1521                         best_bss = iter_bss;
1522                         break;
1523                 }
1524         }
1525
1526         mutex_unlock(&priv->lock);
1527         lbs_deb_leave_args(LBS_DEB_SCAN, "best_bss %p", best_bss);
1528         return best_bss;
1529 }
1530
1531 /**
1532  *  @brief Find the best AP
1533  *
1534  *  Used from association worker.
1535  *
1536  *  @param priv         A pointer to struct lbs_private structure
1537  *  @param pSSID        A pointer to AP's ssid
1538  *
1539  *  @return             0--success, otherwise--fail
1540  */
1541 static int lbs_find_best_network_ssid(struct lbs_private *priv,
1542         uint8_t *out_ssid, uint8_t *out_ssid_len, uint8_t preferred_mode,
1543         uint8_t *out_mode)
1544 {
1545         int ret = -1;
1546         struct bss_descriptor *found;
1547
1548         lbs_deb_enter(LBS_DEB_SCAN);
1549
1550         priv->scan_ssid_len = 0;
1551         lbs_scan_networks(priv, 1);
1552         if (priv->surpriseremoved)
1553                 goto out;
1554
1555         found = lbs_find_best_ssid_in_list(priv, preferred_mode);
1556         if (found && (found->ssid_len > 0)) {
1557                 memcpy(out_ssid, &found->ssid, IW_ESSID_MAX_SIZE);
1558                 *out_ssid_len = found->ssid_len;
1559                 *out_mode = found->mode;
1560                 ret = 0;
1561         }
1562
1563 out:
1564         lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
1565         return ret;
1566 }
1567
1568
1569 void lbs_association_worker(struct work_struct *work)
1570 {
1571         struct lbs_private *priv = container_of(work, struct lbs_private,
1572                 assoc_work.work);
1573         struct assoc_request * assoc_req = NULL;
1574         int ret = 0;
1575         int find_any_ssid = 0;
1576         DECLARE_SSID_BUF(ssid);
1577
1578         lbs_deb_enter(LBS_DEB_ASSOC);
1579
1580         mutex_lock(&priv->lock);
1581         assoc_req = priv->pending_assoc_req;
1582         priv->pending_assoc_req = NULL;
1583         priv->in_progress_assoc_req = assoc_req;
1584         mutex_unlock(&priv->lock);
1585
1586         if (!assoc_req)
1587                 goto done;
1588
1589         lbs_deb_assoc(
1590                 "Association Request:\n"
1591                 "    flags:     0x%08lx\n"
1592                 "    SSID:      '%s'\n"
1593                 "    chann:     %d\n"
1594                 "    band:      %d\n"
1595                 "    mode:      %d\n"
1596                 "    BSSID:     %pM\n"
1597                 "    secinfo:  %s%s%s\n"
1598                 "    auth_mode: %d\n",
1599                 assoc_req->flags,
1600                 print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len),
1601                 assoc_req->channel, assoc_req->band, assoc_req->mode,
1602                 assoc_req->bssid,
1603                 assoc_req->secinfo.WPAenabled ? " WPA" : "",
1604                 assoc_req->secinfo.WPA2enabled ? " WPA2" : "",
1605                 assoc_req->secinfo.wep_enabled ? " WEP" : "",
1606                 assoc_req->secinfo.auth_mode);
1607
1608         /* If 'any' SSID was specified, find an SSID to associate with */
1609         if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)
1610             && !assoc_req->ssid_len)
1611                 find_any_ssid = 1;
1612
1613         /* But don't use 'any' SSID if there's a valid locked BSSID to use */
1614         if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
1615                 if (compare_ether_addr(assoc_req->bssid, bssid_any)
1616                     && compare_ether_addr(assoc_req->bssid, bssid_off))
1617                         find_any_ssid = 0;
1618         }
1619
1620         if (find_any_ssid) {
1621                 u8 new_mode = assoc_req->mode;
1622
1623                 ret = lbs_find_best_network_ssid(priv, assoc_req->ssid,
1624                                 &assoc_req->ssid_len, assoc_req->mode, &new_mode);
1625                 if (ret) {
1626                         lbs_deb_assoc("Could not find best network\n");
1627                         ret = -ENETUNREACH;
1628                         goto out;
1629                 }
1630
1631                 /* Ensure we switch to the mode of the AP */
1632                 if (assoc_req->mode == IW_MODE_AUTO) {
1633                         set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
1634                         assoc_req->mode = new_mode;
1635                 }
1636         }
1637
1638         /*
1639          * Check if the attributes being changing require deauthentication
1640          * from the currently associated infrastructure access point.
1641          */
1642         if (priv->mode == IW_MODE_INFRA) {
1643                 if (should_deauth_infrastructure(priv, assoc_req)) {
1644                         ret = lbs_cmd_80211_deauthenticate(priv,
1645                                                            priv->curbssparams.bssid,
1646                                                            WLAN_REASON_DEAUTH_LEAVING);
1647                         if (ret) {
1648                                 lbs_deb_assoc("Deauthentication due to new "
1649                                         "configuration request failed: %d\n",
1650                                         ret);
1651                         }
1652                 }
1653         } else if (priv->mode == IW_MODE_ADHOC) {
1654                 if (should_stop_adhoc(priv, assoc_req)) {
1655                         ret = lbs_adhoc_stop(priv);
1656                         if (ret) {
1657                                 lbs_deb_assoc("Teardown of AdHoc network due to "
1658                                         "new configuration request failed: %d\n",
1659                                         ret);
1660                         }
1661
1662                 }
1663         }
1664
1665         /* Send the various configuration bits to the firmware */
1666         if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
1667                 ret = assoc_helper_mode(priv, assoc_req);
1668                 if (ret)
1669                         goto out;
1670         }
1671
1672         if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
1673                 ret = assoc_helper_channel(priv, assoc_req);
1674                 if (ret)
1675                         goto out;
1676         }
1677
1678         if (   test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)
1679             || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) {
1680                 ret = assoc_helper_wep_keys(priv, assoc_req);
1681                 if (ret)
1682                         goto out;
1683         }
1684
1685         if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
1686                 ret = assoc_helper_secinfo(priv, assoc_req);
1687                 if (ret)
1688                         goto out;
1689         }
1690
1691         if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
1692                 ret = assoc_helper_wpa_ie(priv, assoc_req);
1693                 if (ret)
1694                         goto out;
1695         }
1696
1697         if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)
1698             || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
1699                 ret = assoc_helper_wpa_keys(priv, assoc_req);
1700                 if (ret)
1701                         goto out;
1702         }
1703
1704         /* SSID/BSSID should be the _last_ config option set, because they
1705          * trigger the association attempt.
1706          */
1707         if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)
1708             || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
1709                 int success = 1;
1710
1711                 ret = assoc_helper_associate(priv, assoc_req);
1712                 if (ret) {
1713                         lbs_deb_assoc("ASSOC: association unsuccessful: %d\n",
1714                                 ret);
1715                         success = 0;
1716                 }
1717
1718                 if (priv->connect_status != LBS_CONNECTED) {
1719                         lbs_deb_assoc("ASSOC: association unsuccessful, "
1720                                 "not connected\n");
1721                         success = 0;
1722                 }
1723
1724                 if (success) {
1725                         lbs_deb_assoc("associated to %pM\n",
1726                                 priv->curbssparams.bssid);
1727                         lbs_prepare_and_send_command(priv,
1728                                 CMD_802_11_RSSI,
1729                                 0, CMD_OPTION_WAITFORRSP, 0, NULL);
1730                 } else {
1731                         ret = -1;
1732                 }
1733         }
1734
1735 out:
1736         if (ret) {
1737                 lbs_deb_assoc("ASSOC: reconfiguration attempt unsuccessful: %d\n",
1738                         ret);
1739         }
1740
1741         mutex_lock(&priv->lock);
1742         priv->in_progress_assoc_req = NULL;
1743         mutex_unlock(&priv->lock);
1744         kfree(assoc_req);
1745
1746 done:
1747         lbs_deb_leave(LBS_DEB_ASSOC);
1748 }
1749
1750
1751 /*
1752  * Caller MUST hold any necessary locks
1753  */
1754 struct assoc_request *lbs_get_association_request(struct lbs_private *priv)
1755 {
1756         struct assoc_request * assoc_req;
1757
1758         lbs_deb_enter(LBS_DEB_ASSOC);
1759         if (!priv->pending_assoc_req) {
1760                 priv->pending_assoc_req = kzalloc(sizeof(struct assoc_request),
1761                                                      GFP_KERNEL);
1762                 if (!priv->pending_assoc_req) {
1763                         lbs_pr_info("Not enough memory to allocate association"
1764                                 " request!\n");
1765                         return NULL;
1766                 }
1767         }
1768
1769         /* Copy current configuration attributes to the association request,
1770          * but don't overwrite any that are already set.
1771          */
1772         assoc_req = priv->pending_assoc_req;
1773         if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
1774                 memcpy(&assoc_req->ssid, &priv->curbssparams.ssid,
1775                        IW_ESSID_MAX_SIZE);
1776                 assoc_req->ssid_len = priv->curbssparams.ssid_len;
1777         }
1778
1779         if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
1780                 assoc_req->channel = priv->curbssparams.channel;
1781
1782         if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags))
1783                 assoc_req->band = priv->curbssparams.band;
1784
1785         if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags))
1786                 assoc_req->mode = priv->mode;
1787
1788         if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
1789                 memcpy(&assoc_req->bssid, priv->curbssparams.bssid,
1790                         ETH_ALEN);
1791         }
1792
1793         if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) {
1794                 int i;
1795                 for (i = 0; i < 4; i++) {
1796                         memcpy(&assoc_req->wep_keys[i], &priv->wep_keys[i],
1797                                 sizeof(struct enc_key));
1798                 }
1799         }
1800
1801         if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags))
1802                 assoc_req->wep_tx_keyidx = priv->wep_tx_keyidx;
1803
1804         if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
1805                 memcpy(&assoc_req->wpa_mcast_key, &priv->wpa_mcast_key,
1806                         sizeof(struct enc_key));
1807         }
1808
1809         if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
1810                 memcpy(&assoc_req->wpa_unicast_key, &priv->wpa_unicast_key,
1811                         sizeof(struct enc_key));
1812         }
1813
1814         if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
1815                 memcpy(&assoc_req->secinfo, &priv->secinfo,
1816                         sizeof(struct lbs_802_11_security));
1817         }
1818
1819         if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
1820                 memcpy(&assoc_req->wpa_ie, &priv->wpa_ie,
1821                         MAX_WPA_IE_LEN);
1822                 assoc_req->wpa_ie_len = priv->wpa_ie_len;
1823         }
1824
1825         lbs_deb_leave(LBS_DEB_ASSOC);
1826         return assoc_req;
1827 }
1828
1829
1830 /**
1831  *  @brief Deauthenticate from a specific BSS
1832  *
1833  *  @param priv        A pointer to struct lbs_private structure
1834  *  @param bssid       The specific BSS to deauthenticate from
1835  *  @param reason      The 802.11 sec. 7.3.1.7 Reason Code for deauthenticating
1836  *
1837  *  @return            0 on success, error on failure
1838  */
1839 int lbs_cmd_80211_deauthenticate(struct lbs_private *priv, u8 bssid[ETH_ALEN],
1840                                  u16 reason)
1841 {
1842         struct cmd_ds_802_11_deauthenticate cmd;
1843         int ret;
1844
1845         lbs_deb_enter(LBS_DEB_JOIN);
1846
1847         memset(&cmd, 0, sizeof(cmd));
1848         cmd.hdr.size = cpu_to_le16(sizeof(cmd));
1849         memcpy(cmd.macaddr, &bssid[0], ETH_ALEN);
1850         cmd.reasoncode = cpu_to_le16(reason);
1851
1852         ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd);
1853
1854         /* Clean up everything even if there was an error; can't assume that
1855          * we're still authenticated to the AP after trying to deauth.
1856          */
1857         lbs_mac_event_disconnected(priv);
1858
1859         lbs_deb_leave(LBS_DEB_JOIN);
1860         return ret;
1861 }
1862