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