blob: e794e93a2297e6376d48836c6a0cc8ae8a7e1708 [file] [log] [blame]
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001/* Copyright (C) 2006, Red Hat, Inc. */
2
Dan Williams3cf209312007-05-25 17:28:30 -04003#include <linux/etherdevice.h>
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02004
5#include "assoc.h"
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02006#include "decl.h"
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02007#include "host.h"
Dan Williams2dd4b262007-12-11 16:54:15 -05008#include "cmd.h"
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02009
10
Ihar Hrachyshka5a6e0432008-01-25 14:15:00 +010011static const u8 bssid_any[ETH_ALEN] __attribute__ ((aligned (2))) =
12 { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
13static const u8 bssid_off[ETH_ALEN] __attribute__ ((aligned (2))) =
14 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020015
Holger Schurig697900a2008-04-02 16:27:10 +020016/* The firmware needs certain bits masked out of the beacon-derviced capability
17 * field when associating/joining to BSSs.
18 */
19#define CAPINFO_MASK (~(0xda00))
20
21
22
23/**
24 * @brief Associate to a specific BSS discovered in a scan
25 *
26 * @param priv A pointer to struct lbs_private structure
27 * @param pbssdesc Pointer to the BSS descriptor to associate with.
28 *
29 * @return 0-success, otherwise fail
30 */
31static int lbs_associate(struct lbs_private *priv,
32 struct assoc_request *assoc_req)
33{
34 int ret;
35
36 lbs_deb_enter(LBS_DEB_ASSOC);
37
38 ret = lbs_prepare_and_send_command(priv, CMD_802_11_AUTHENTICATE,
39 0, CMD_OPTION_WAITFORRSP,
40 0, assoc_req->bss.bssid);
41
42 if (ret)
43 goto done;
44
45 /* set preamble to firmware */
46 if ((priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
47 (assoc_req->bss.capability & WLAN_CAPABILITY_SHORT_PREAMBLE))
48 priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
49 else
50 priv->preamble = CMD_TYPE_LONG_PREAMBLE;
51
52 lbs_set_radio_control(priv);
53
54 ret = lbs_prepare_and_send_command(priv, CMD_802_11_ASSOCIATE,
55 0, CMD_OPTION_WAITFORRSP, 0, assoc_req);
56
57done:
58 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
59 return ret;
60}
61
62/**
63 * @brief Join an adhoc network found in a previous scan
64 *
65 * @param priv A pointer to struct lbs_private structure
66 * @param pbssdesc Pointer to a BSS descriptor found in a previous scan
67 * to attempt to join
68 *
69 * @return 0--success, -1--fail
70 */
71static int lbs_join_adhoc_network(struct lbs_private *priv,
72 struct assoc_request *assoc_req)
73{
74 struct bss_descriptor *bss = &assoc_req->bss;
75 int ret = 0;
76
77 lbs_deb_join("current SSID '%s', ssid length %u\n",
78 escape_essid(priv->curbssparams.ssid,
79 priv->curbssparams.ssid_len),
80 priv->curbssparams.ssid_len);
81 lbs_deb_join("requested ssid '%s', ssid length %u\n",
82 escape_essid(bss->ssid, bss->ssid_len),
83 bss->ssid_len);
84
85 /* check if the requested SSID is already joined */
86 if (priv->curbssparams.ssid_len &&
87 !lbs_ssid_cmp(priv->curbssparams.ssid,
88 priv->curbssparams.ssid_len,
89 bss->ssid, bss->ssid_len) &&
90 (priv->mode == IW_MODE_ADHOC) &&
91 (priv->connect_status == LBS_CONNECTED)) {
92 union iwreq_data wrqu;
93
94 lbs_deb_join("ADHOC_J_CMD: New ad-hoc SSID is the same as "
95 "current, not attempting to re-join");
96
97 /* Send the re-association event though, because the association
98 * request really was successful, even if just a null-op.
99 */
100 memset(&wrqu, 0, sizeof(wrqu));
101 memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid,
102 ETH_ALEN);
103 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
104 wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
105 goto out;
106 }
107
108 /* Use shortpreamble only when both creator and card supports
109 short preamble */
110 if (!(bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) ||
111 !(priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)) {
112 lbs_deb_join("AdhocJoin: Long preamble\n");
113 priv->preamble = CMD_TYPE_LONG_PREAMBLE;
114 } else {
115 lbs_deb_join("AdhocJoin: Short preamble\n");
116 priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
117 }
118
119 lbs_set_radio_control(priv);
120
121 lbs_deb_join("AdhocJoin: channel = %d\n", assoc_req->channel);
122 lbs_deb_join("AdhocJoin: band = %c\n", assoc_req->band);
123
124 priv->adhoccreate = 0;
125
126 ret = lbs_prepare_and_send_command(priv, CMD_802_11_AD_HOC_JOIN,
127 0, CMD_OPTION_WAITFORRSP,
128 OID_802_11_SSID, assoc_req);
129
130out:
131 return ret;
132}
133
134/**
135 * @brief Start an Adhoc Network
136 *
137 * @param priv A pointer to struct lbs_private structure
138 * @param adhocssid The ssid of the Adhoc Network
139 * @return 0--success, -1--fail
140 */
141static int lbs_start_adhoc_network(struct lbs_private *priv,
142 struct assoc_request *assoc_req)
143{
144 int ret = 0;
145
146 priv->adhoccreate = 1;
147
148 if (priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) {
149 lbs_deb_join("AdhocStart: Short preamble\n");
150 priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
151 } else {
152 lbs_deb_join("AdhocStart: Long preamble\n");
153 priv->preamble = CMD_TYPE_LONG_PREAMBLE;
154 }
155
156 lbs_set_radio_control(priv);
157
158 lbs_deb_join("AdhocStart: channel = %d\n", assoc_req->channel);
159 lbs_deb_join("AdhocStart: band = %d\n", assoc_req->band);
160
161 ret = lbs_prepare_and_send_command(priv, CMD_802_11_AD_HOC_START,
162 0, CMD_OPTION_WAITFORRSP, 0, assoc_req);
163
164 return ret;
165}
166
167int lbs_stop_adhoc_network(struct lbs_private *priv)
168{
169 return lbs_prepare_and_send_command(priv, CMD_802_11_AD_HOC_STOP,
170 0, CMD_OPTION_WAITFORRSP, 0, NULL);
171}
Dan Williamse76850d2007-05-25 17:09:41 -0400172
Holger Schurig69f90322007-11-23 15:43:44 +0100173static int assoc_helper_essid(struct lbs_private *priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200174 struct assoc_request * assoc_req)
175{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200176 int ret = 0;
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400177 struct bss_descriptor * bss;
Dan Williamsaeea0ab2007-05-25 22:30:48 -0400178 int channel = -1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200179
Holger Schurig9012b282007-05-25 11:27:16 -0400180 lbs_deb_enter(LBS_DEB_ASSOC);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200181
Dan Williamsef9a2642007-05-25 16:46:33 -0400182 /* FIXME: take channel into account when picking SSIDs if a channel
183 * is set.
184 */
185
Dan Williamsaeea0ab2007-05-25 22:30:48 -0400186 if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
187 channel = assoc_req->channel;
188
Holger Schurig0765af42007-10-15 12:55:56 +0200189 lbs_deb_assoc("SSID '%s' requested\n",
Dan Williamsd8efea22007-05-28 23:54:55 -0400190 escape_essid(assoc_req->ssid, assoc_req->ssid_len));
Dan Williams0dc5a292007-05-10 22:58:02 -0400191 if (assoc_req->mode == IW_MODE_INFRA) {
Holger Schurig10078322007-11-15 18:05:47 -0500192 lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
Holger Schurig52933d82008-03-05 07:05:32 +0100193 assoc_req->ssid_len);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200194
David Woodhouseaa21c002007-12-08 20:04:36 +0000195 bss = lbs_find_ssid_in_list(priv, assoc_req->ssid,
Dan Williamsd8efea22007-05-28 23:54:55 -0400196 assoc_req->ssid_len, NULL, IW_MODE_INFRA, channel);
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400197 if (bss != NULL) {
Dan Williamse76850d2007-05-25 17:09:41 -0400198 memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
Holger Schurig10078322007-11-15 18:05:47 -0500199 ret = lbs_associate(priv, assoc_req);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200200 } else {
Dan Williamsd8efea22007-05-28 23:54:55 -0400201 lbs_deb_assoc("SSID not found; cannot associate\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200202 }
Dan Williams0dc5a292007-05-10 22:58:02 -0400203 } else if (assoc_req->mode == IW_MODE_ADHOC) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200204 /* Scan for the network, do not save previous results. Stale
205 * scan data will cause us to join a non-existant adhoc network
206 */
Holger Schurig10078322007-11-15 18:05:47 -0500207 lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
Holger Schurig52933d82008-03-05 07:05:32 +0100208 assoc_req->ssid_len);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200209
210 /* Search for the requested SSID in the scan table */
David Woodhouseaa21c002007-12-08 20:04:36 +0000211 bss = lbs_find_ssid_in_list(priv, assoc_req->ssid,
Dan Williamsd8efea22007-05-28 23:54:55 -0400212 assoc_req->ssid_len, NULL, IW_MODE_ADHOC, channel);
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400213 if (bss != NULL) {
Dan Williamsd8efea22007-05-28 23:54:55 -0400214 lbs_deb_assoc("SSID found, will join\n");
Dan Williamse76850d2007-05-25 17:09:41 -0400215 memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
Holger Schurig10078322007-11-15 18:05:47 -0500216 lbs_join_adhoc_network(priv, assoc_req);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200217 } else {
218 /* else send START command */
Dan Williamsd8efea22007-05-28 23:54:55 -0400219 lbs_deb_assoc("SSID not found, creating adhoc network\n");
Dan Williamse76850d2007-05-25 17:09:41 -0400220 memcpy(&assoc_req->bss.ssid, &assoc_req->ssid,
Dan Williamsd8efea22007-05-28 23:54:55 -0400221 IW_ESSID_MAX_SIZE);
222 assoc_req->bss.ssid_len = assoc_req->ssid_len;
Holger Schurig10078322007-11-15 18:05:47 -0500223 lbs_start_adhoc_network(priv, assoc_req);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200224 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200225 }
226
Holger Schurig9012b282007-05-25 11:27:16 -0400227 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200228 return ret;
229}
230
231
Holger Schurig69f90322007-11-23 15:43:44 +0100232static int assoc_helper_bssid(struct lbs_private *priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200233 struct assoc_request * assoc_req)
234{
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400235 int ret = 0;
236 struct bss_descriptor * bss;
Joe Perches0795af52007-10-03 17:59:30 -0700237 DECLARE_MAC_BUF(mac);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200238
Joe Perches0795af52007-10-03 17:59:30 -0700239 lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID %s",
240 print_mac(mac, assoc_req->bssid));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200241
242 /* Search for index position in list for requested MAC */
David Woodhouseaa21c002007-12-08 20:04:36 +0000243 bss = lbs_find_bssid_in_list(priv, assoc_req->bssid,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200244 assoc_req->mode);
Dan Williamsfcdb53d2007-05-25 16:15:56 -0400245 if (bss == NULL) {
Joe Perches0795af52007-10-03 17:59:30 -0700246 lbs_deb_assoc("ASSOC: WAP: BSSID %s not found, "
247 "cannot associate.\n", print_mac(mac, assoc_req->bssid));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200248 goto out;
249 }
250
Dan Williamse76850d2007-05-25 17:09:41 -0400251 memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
Dan Williams0dc5a292007-05-10 22:58:02 -0400252 if (assoc_req->mode == IW_MODE_INFRA) {
Holger Schurig10078322007-11-15 18:05:47 -0500253 ret = lbs_associate(priv, assoc_req);
254 lbs_deb_assoc("ASSOC: lbs_associate(bssid) returned %d\n", ret);
Dan Williams0dc5a292007-05-10 22:58:02 -0400255 } else if (assoc_req->mode == IW_MODE_ADHOC) {
Holger Schurig10078322007-11-15 18:05:47 -0500256 lbs_join_adhoc_network(priv, assoc_req);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200257 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200258
259out:
Holger Schurig9012b282007-05-25 11:27:16 -0400260 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200261 return ret;
262}
263
264
Holger Schurig69f90322007-11-23 15:43:44 +0100265static int assoc_helper_associate(struct lbs_private *priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200266 struct assoc_request * assoc_req)
267{
268 int ret = 0, done = 0;
269
Holger Schurig0765af42007-10-15 12:55:56 +0200270 lbs_deb_enter(LBS_DEB_ASSOC);
271
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200272 /* If we're given and 'any' BSSID, try associating based on SSID */
273
274 if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
Dan Williams3cf209312007-05-25 17:28:30 -0400275 if (compare_ether_addr(bssid_any, assoc_req->bssid)
276 && compare_ether_addr(bssid_off, assoc_req->bssid)) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200277 ret = assoc_helper_bssid(priv, assoc_req);
278 done = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200279 }
280 }
281
282 if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
283 ret = assoc_helper_essid(priv, assoc_req);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200284 }
285
Holger Schurig0765af42007-10-15 12:55:56 +0200286 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200287 return ret;
288}
289
290
Holger Schurig69f90322007-11-23 15:43:44 +0100291static int assoc_helper_mode(struct lbs_private *priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200292 struct assoc_request * assoc_req)
293{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200294 int ret = 0;
295
Holger Schurig9012b282007-05-25 11:27:16 -0400296 lbs_deb_enter(LBS_DEB_ASSOC);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200297
David Woodhouseaa21c002007-12-08 20:04:36 +0000298 if (assoc_req->mode == priv->mode)
Holger Schurig9012b282007-05-25 11:27:16 -0400299 goto done;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200300
Dan Williams0dc5a292007-05-10 22:58:02 -0400301 if (assoc_req->mode == IW_MODE_INFRA) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000302 if (priv->psstate != PS_STATE_FULL_POWER)
Holger Schurig10078322007-11-15 18:05:47 -0500303 lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
David Woodhouseaa21c002007-12-08 20:04:36 +0000304 priv->psmode = LBS802_11POWERMODECAM;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200305 }
306
David Woodhouseaa21c002007-12-08 20:04:36 +0000307 priv->mode = assoc_req->mode;
Holger Schurig10078322007-11-15 18:05:47 -0500308 ret = lbs_prepare_and_send_command(priv,
Dan Williams0aef64d2007-08-02 11:31:18 -0400309 CMD_802_11_SNMP_MIB,
310 0, CMD_OPTION_WAITFORRSP,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200311 OID_802_11_INFRASTRUCTURE_MODE,
David Woodhouse981f1872007-05-25 23:36:54 -0400312 /* Shoot me now */ (void *) (size_t) assoc_req->mode);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200313
Holger Schurig9012b282007-05-25 11:27:16 -0400314done:
315 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200316 return ret;
317}
318
319
David Woodhouse9f462572007-12-12 22:50:21 -0500320int lbs_update_channel(struct lbs_private *priv)
Dan Williamsef9a2642007-05-25 16:46:33 -0400321{
Holger Schurig0765af42007-10-15 12:55:56 +0200322 int ret;
Dan Williams2dd4b262007-12-11 16:54:15 -0500323
David Woodhoused1a469f2007-12-16 17:21:00 -0500324 /* the channel in f/w could be out of sync; get the current channel */
Holger Schurig0765af42007-10-15 12:55:56 +0200325 lbs_deb_enter(LBS_DEB_ASSOC);
Dan Williams2dd4b262007-12-11 16:54:15 -0500326
327 ret = lbs_get_channel(priv);
David Woodhoused1a469f2007-12-16 17:21:00 -0500328 if (ret > 0) {
329 priv->curbssparams.channel = ret;
330 ret = 0;
331 }
Holger Schurig0765af42007-10-15 12:55:56 +0200332 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
333 return ret;
Dan Williamsef9a2642007-05-25 16:46:33 -0400334}
335
Holger Schurig69f90322007-11-23 15:43:44 +0100336static int assoc_helper_channel(struct lbs_private *priv,
Dan Williamsef9a2642007-05-25 16:46:33 -0400337 struct assoc_request * assoc_req)
338{
Dan Williamsef9a2642007-05-25 16:46:33 -0400339 int ret = 0;
340
341 lbs_deb_enter(LBS_DEB_ASSOC);
342
David Woodhouse9f462572007-12-12 22:50:21 -0500343 ret = lbs_update_channel(priv);
David Woodhoused1a469f2007-12-16 17:21:00 -0500344 if (ret) {
David Woodhouse23d36ee2007-12-12 00:14:21 -0500345 lbs_deb_assoc("ASSOC: channel: error getting channel.\n");
David Woodhoused1a469f2007-12-16 17:21:00 -0500346 goto done;
Dan Williamsef9a2642007-05-25 16:46:33 -0400347 }
348
David Woodhouseaa21c002007-12-08 20:04:36 +0000349 if (assoc_req->channel == priv->curbssparams.channel)
Dan Williamsef9a2642007-05-25 16:46:33 -0400350 goto done;
351
David Woodhouse8642f1f2007-12-11 20:03:01 -0500352 if (priv->mesh_dev) {
David Woodhouse86062132007-12-13 00:32:36 -0500353 /* Change mesh channel first; 21.p21 firmware won't let
354 you change channel otherwise (even though it'll return
355 an error to this */
356 lbs_mesh_config(priv, 0, assoc_req->channel);
David Woodhouse8642f1f2007-12-11 20:03:01 -0500357 }
358
Dan Williamsef9a2642007-05-25 16:46:33 -0400359 lbs_deb_assoc("ASSOC: channel: %d -> %d\n",
David Woodhouse86062132007-12-13 00:32:36 -0500360 priv->curbssparams.channel, assoc_req->channel);
Dan Williamsef9a2642007-05-25 16:46:33 -0400361
Dan Williams2dd4b262007-12-11 16:54:15 -0500362 ret = lbs_set_channel(priv, assoc_req->channel);
363 if (ret < 0)
David Woodhouse23d36ee2007-12-12 00:14:21 -0500364 lbs_deb_assoc("ASSOC: channel: error setting channel.\n");
Dan Williamsef9a2642007-05-25 16:46:33 -0400365
Dan Williams2dd4b262007-12-11 16:54:15 -0500366 /* FIXME: shouldn't need to grab the channel _again_ after setting
367 * it since the firmware is supposed to return the new channel, but
368 * whatever... */
David Woodhouse9f462572007-12-12 22:50:21 -0500369 ret = lbs_update_channel(priv);
David Woodhoused1a469f2007-12-16 17:21:00 -0500370 if (ret) {
David Woodhouse23d36ee2007-12-12 00:14:21 -0500371 lbs_deb_assoc("ASSOC: channel: error getting channel.\n");
David Woodhoused1a469f2007-12-16 17:21:00 -0500372 goto done;
373 }
Dan Williamsef9a2642007-05-25 16:46:33 -0400374
David Woodhouseaa21c002007-12-08 20:04:36 +0000375 if (assoc_req->channel != priv->curbssparams.channel) {
David Woodhouse88ae2912007-12-11 19:57:05 -0500376 lbs_deb_assoc("ASSOC: channel: failed to update channel to %d\n",
Dan Williamsef9a2642007-05-25 16:46:33 -0400377 assoc_req->channel);
David Woodhouse8642f1f2007-12-11 20:03:01 -0500378 goto restore_mesh;
Dan Williamsef9a2642007-05-25 16:46:33 -0400379 }
380
381 if ( assoc_req->secinfo.wep_enabled
382 && (assoc_req->wep_keys[0].len
383 || assoc_req->wep_keys[1].len
384 || assoc_req->wep_keys[2].len
385 || assoc_req->wep_keys[3].len)) {
386 /* Make sure WEP keys are re-sent to firmware */
387 set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
388 }
389
390 /* Must restart/rejoin adhoc networks after channel change */
David Woodhouse23d36ee2007-12-12 00:14:21 -0500391 set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
Dan Williamsef9a2642007-05-25 16:46:33 -0400392
David Woodhouse8642f1f2007-12-11 20:03:01 -0500393 restore_mesh:
394 if (priv->mesh_dev)
David Woodhouse86062132007-12-13 00:32:36 -0500395 lbs_mesh_config(priv, 1, priv->curbssparams.channel);
David Woodhouse8642f1f2007-12-11 20:03:01 -0500396
397 done:
Dan Williamsef9a2642007-05-25 16:46:33 -0400398 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
399 return ret;
400}
401
402
Holger Schurig69f90322007-11-23 15:43:44 +0100403static int assoc_helper_wep_keys(struct lbs_private *priv,
David Woodhousef70dd452007-12-18 00:18:05 -0500404 struct assoc_request *assoc_req)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200405{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200406 int i;
407 int ret = 0;
408
Holger Schurig9012b282007-05-25 11:27:16 -0400409 lbs_deb_enter(LBS_DEB_ASSOC);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200410
411 /* Set or remove WEP keys */
David Woodhousef70dd452007-12-18 00:18:05 -0500412 if (assoc_req->wep_keys[0].len || assoc_req->wep_keys[1].len ||
413 assoc_req->wep_keys[2].len || assoc_req->wep_keys[3].len)
414 ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_ADD, assoc_req);
415 else
416 ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_REMOVE, assoc_req);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200417
418 if (ret)
419 goto out;
420
421 /* enable/disable the MAC's WEP packet filter */
Dan Williams889c05b2007-05-10 22:57:23 -0400422 if (assoc_req->secinfo.wep_enabled)
Holger Schurigd9e97782008-03-12 16:06:43 +0100423 priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200424 else
Holger Schurigd9e97782008-03-12 16:06:43 +0100425 priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE;
David Woodhousef70dd452007-12-18 00:18:05 -0500426
Holger Schurigc97329e2008-03-18 11:20:21 +0100427 lbs_set_mac_control(priv);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200428
David Woodhouseaa21c002007-12-08 20:04:36 +0000429 mutex_lock(&priv->lock);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200430
David Woodhouseaa21c002007-12-08 20:04:36 +0000431 /* Copy WEP keys into priv wep key fields */
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200432 for (i = 0; i < 4; i++) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000433 memcpy(&priv->wep_keys[i], &assoc_req->wep_keys[i],
David Woodhousef70dd452007-12-18 00:18:05 -0500434 sizeof(struct enc_key));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200435 }
David Woodhouseaa21c002007-12-08 20:04:36 +0000436 priv->wep_tx_keyidx = assoc_req->wep_tx_keyidx;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200437
David Woodhouseaa21c002007-12-08 20:04:36 +0000438 mutex_unlock(&priv->lock);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200439
440out:
Holger Schurig9012b282007-05-25 11:27:16 -0400441 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200442 return ret;
443}
444
Holger Schurig69f90322007-11-23 15:43:44 +0100445static int assoc_helper_secinfo(struct lbs_private *priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200446 struct assoc_request * assoc_req)
447{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200448 int ret = 0;
David Woodhouse4f59abf2007-12-18 00:47:17 -0500449 uint16_t do_wpa;
450 uint16_t rsn = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200451
Holger Schurig9012b282007-05-25 11:27:16 -0400452 lbs_deb_enter(LBS_DEB_ASSOC);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200453
David Woodhouseaa21c002007-12-08 20:04:36 +0000454 memcpy(&priv->secinfo, &assoc_req->secinfo,
Holger Schurig10078322007-11-15 18:05:47 -0500455 sizeof(struct lbs_802_11_security));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200456
Holger Schurigc97329e2008-03-18 11:20:21 +0100457 lbs_set_mac_control(priv);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200458
Dan Williams18c96c342007-06-18 12:01:12 -0400459 /* If RSN is already enabled, don't try to enable it again, since
460 * ENABLE_RSN resets internal state machines and will clobber the
461 * 4-way WPA handshake.
462 */
463
464 /* Get RSN enabled/disabled */
David Woodhouse4f59abf2007-12-18 00:47:17 -0500465 ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_GET, &rsn);
Dan Williams18c96c342007-06-18 12:01:12 -0400466 if (ret) {
David Woodhouse23d36ee2007-12-12 00:14:21 -0500467 lbs_deb_assoc("Failed to get RSN status: %d\n", ret);
Dan Williams18c96c342007-06-18 12:01:12 -0400468 goto out;
469 }
470
471 /* Don't re-enable RSN if it's already enabled */
David Woodhouse4f59abf2007-12-18 00:47:17 -0500472 do_wpa = assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled;
Dan Williams18c96c342007-06-18 12:01:12 -0400473 if (do_wpa == rsn)
474 goto out;
475
476 /* Set RSN enabled/disabled */
David Woodhouse4f59abf2007-12-18 00:47:17 -0500477 ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_SET, &do_wpa);
Dan Williams90a42212007-05-25 23:01:24 -0400478
479out:
Holger Schurig9012b282007-05-25 11:27:16 -0400480 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200481 return ret;
482}
483
484
Holger Schurig69f90322007-11-23 15:43:44 +0100485static int assoc_helper_wpa_keys(struct lbs_private *priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200486 struct assoc_request * assoc_req)
487{
488 int ret = 0;
Dan Williams2bcde512007-10-03 10:37:45 -0400489 unsigned int flags = assoc_req->flags;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200490
Holger Schurig9012b282007-05-25 11:27:16 -0400491 lbs_deb_enter(LBS_DEB_ASSOC);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200492
Dan Williams2bcde512007-10-03 10:37:45 -0400493 /* Work around older firmware bug where WPA unicast and multicast
494 * keys must be set independently. Seen in SDIO parts with firmware
495 * version 5.0.11p0.
496 */
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200497
Dan Williams2bcde512007-10-03 10:37:45 -0400498 if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
499 clear_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
David Woodhouse9e1228d2008-03-03 12:15:39 +0100500 ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
Dan Williams2bcde512007-10-03 10:37:45 -0400501 assoc_req->flags = flags;
502 }
503
504 if (ret)
505 goto out;
506
507 if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
508 clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
509
David Woodhouse9e1228d2008-03-03 12:15:39 +0100510 ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
Dan Williams2bcde512007-10-03 10:37:45 -0400511 assoc_req->flags = flags;
512 }
513
514out:
Holger Schurig9012b282007-05-25 11:27:16 -0400515 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200516 return ret;
517}
518
519
Holger Schurig69f90322007-11-23 15:43:44 +0100520static int assoc_helper_wpa_ie(struct lbs_private *priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200521 struct assoc_request * assoc_req)
522{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200523 int ret = 0;
524
Holger Schurig9012b282007-05-25 11:27:16 -0400525 lbs_deb_enter(LBS_DEB_ASSOC);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200526
527 if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000528 memcpy(&priv->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len);
529 priv->wpa_ie_len = assoc_req->wpa_ie_len;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200530 } else {
David Woodhouseaa21c002007-12-08 20:04:36 +0000531 memset(&priv->wpa_ie, 0, MAX_WPA_IE_LEN);
532 priv->wpa_ie_len = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200533 }
534
Holger Schurig9012b282007-05-25 11:27:16 -0400535 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200536 return ret;
537}
538
539
David Woodhouseaa21c002007-12-08 20:04:36 +0000540static int should_deauth_infrastructure(struct lbs_private *priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200541 struct assoc_request * assoc_req)
542{
Holger Schurig0765af42007-10-15 12:55:56 +0200543 int ret = 0;
544
David Woodhouseaa21c002007-12-08 20:04:36 +0000545 if (priv->connect_status != LBS_CONNECTED)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200546 return 0;
547
Holger Schurig52507c22008-01-28 17:25:53 +0100548 lbs_deb_enter(LBS_DEB_ASSOC);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200549 if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
Holger Schurig0765af42007-10-15 12:55:56 +0200550 lbs_deb_assoc("Deauthenticating due to new SSID\n");
551 ret = 1;
552 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200553 }
554
555 if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000556 if (priv->secinfo.auth_mode != assoc_req->secinfo.auth_mode) {
Holger Schurig0765af42007-10-15 12:55:56 +0200557 lbs_deb_assoc("Deauthenticating due to new security\n");
558 ret = 1;
559 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200560 }
561 }
562
563 if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
Holger Schurig0765af42007-10-15 12:55:56 +0200564 lbs_deb_assoc("Deauthenticating due to new BSSID\n");
565 ret = 1;
566 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200567 }
568
Luis Carlos Cobo Rusfff47f12007-05-30 12:16:13 -0400569 if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
Holger Schurig0765af42007-10-15 12:55:56 +0200570 lbs_deb_assoc("Deauthenticating due to channel switch\n");
571 ret = 1;
572 goto out;
Luis Carlos Cobo Rusfff47f12007-05-30 12:16:13 -0400573 }
574
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200575 /* FIXME: deal with 'auto' mode somehow */
576 if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
Holger Schurig0765af42007-10-15 12:55:56 +0200577 if (assoc_req->mode != IW_MODE_INFRA) {
578 lbs_deb_assoc("Deauthenticating due to leaving "
579 "infra mode\n");
580 ret = 1;
581 goto out;
582 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200583 }
584
Holger Schurig0765af42007-10-15 12:55:56 +0200585out:
586 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
Holger Schurig52507c22008-01-28 17:25:53 +0100587 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200588}
589
590
David Woodhouseaa21c002007-12-08 20:04:36 +0000591static int should_stop_adhoc(struct lbs_private *priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200592 struct assoc_request * assoc_req)
593{
Holger Schurig0765af42007-10-15 12:55:56 +0200594 lbs_deb_enter(LBS_DEB_ASSOC);
595
David Woodhouseaa21c002007-12-08 20:04:36 +0000596 if (priv->connect_status != LBS_CONNECTED)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200597 return 0;
598
David Woodhouseaa21c002007-12-08 20:04:36 +0000599 if (lbs_ssid_cmp(priv->curbssparams.ssid,
600 priv->curbssparams.ssid_len,
Dan Williamsd8efea22007-05-28 23:54:55 -0400601 assoc_req->ssid, assoc_req->ssid_len) != 0)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200602 return 1;
603
604 /* FIXME: deal with 'auto' mode somehow */
605 if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
Dan Williams0dc5a292007-05-10 22:58:02 -0400606 if (assoc_req->mode != IW_MODE_ADHOC)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200607 return 1;
608 }
609
Dan Williamsef9a2642007-05-25 16:46:33 -0400610 if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000611 if (assoc_req->channel != priv->curbssparams.channel)
Dan Williamsef9a2642007-05-25 16:46:33 -0400612 return 1;
613 }
614
Holger Schurig0765af42007-10-15 12:55:56 +0200615 lbs_deb_leave(LBS_DEB_ASSOC);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200616 return 0;
617}
618
619
Holger Schurig10078322007-11-15 18:05:47 -0500620void lbs_association_worker(struct work_struct *work)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200621{
Holger Schurig69f90322007-11-23 15:43:44 +0100622 struct lbs_private *priv = container_of(work, struct lbs_private,
623 assoc_work.work);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200624 struct assoc_request * assoc_req = NULL;
625 int ret = 0;
626 int find_any_ssid = 0;
Joe Perches0795af52007-10-03 17:59:30 -0700627 DECLARE_MAC_BUF(mac);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200628
Holger Schurig9012b282007-05-25 11:27:16 -0400629 lbs_deb_enter(LBS_DEB_ASSOC);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200630
David Woodhouseaa21c002007-12-08 20:04:36 +0000631 mutex_lock(&priv->lock);
632 assoc_req = priv->pending_assoc_req;
633 priv->pending_assoc_req = NULL;
634 priv->in_progress_assoc_req = assoc_req;
635 mutex_unlock(&priv->lock);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200636
Holger Schurig9012b282007-05-25 11:27:16 -0400637 if (!assoc_req)
638 goto done;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200639
Holger Schurig0765af42007-10-15 12:55:56 +0200640 lbs_deb_assoc(
641 "Association Request:\n"
642 " flags: 0x%08lx\n"
643 " SSID: '%s'\n"
644 " chann: %d\n"
645 " band: %d\n"
646 " mode: %d\n"
647 " BSSID: %s\n"
648 " secinfo: %s%s%s\n"
649 " auth_mode: %d\n",
650 assoc_req->flags,
651 escape_essid(assoc_req->ssid, assoc_req->ssid_len),
652 assoc_req->channel, assoc_req->band, assoc_req->mode,
653 print_mac(mac, assoc_req->bssid),
654 assoc_req->secinfo.WPAenabled ? " WPA" : "",
655 assoc_req->secinfo.WPA2enabled ? " WPA2" : "",
656 assoc_req->secinfo.wep_enabled ? " WEP" : "",
657 assoc_req->secinfo.auth_mode);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200658
659 /* If 'any' SSID was specified, find an SSID to associate with */
660 if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)
Dan Williamsd8efea22007-05-28 23:54:55 -0400661 && !assoc_req->ssid_len)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200662 find_any_ssid = 1;
663
664 /* But don't use 'any' SSID if there's a valid locked BSSID to use */
665 if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
Dan Williams3cf209312007-05-25 17:28:30 -0400666 if (compare_ether_addr(assoc_req->bssid, bssid_any)
667 && compare_ether_addr(assoc_req->bssid, bssid_off))
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200668 find_any_ssid = 0;
669 }
670
671 if (find_any_ssid) {
Dan Williams0dc5a292007-05-10 22:58:02 -0400672 u8 new_mode;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200673
Holger Schurig10078322007-11-15 18:05:47 -0500674 ret = lbs_find_best_network_ssid(priv, assoc_req->ssid,
Dan Williamsd8efea22007-05-28 23:54:55 -0400675 &assoc_req->ssid_len, assoc_req->mode, &new_mode);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200676 if (ret) {
Holger Schurig9012b282007-05-25 11:27:16 -0400677 lbs_deb_assoc("Could not find best network\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200678 ret = -ENETUNREACH;
679 goto out;
680 }
681
682 /* Ensure we switch to the mode of the AP */
Dan Williams0dc5a292007-05-10 22:58:02 -0400683 if (assoc_req->mode == IW_MODE_AUTO) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200684 set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
685 assoc_req->mode = new_mode;
686 }
687 }
688
689 /*
690 * Check if the attributes being changing require deauthentication
691 * from the currently associated infrastructure access point.
692 */
David Woodhouseaa21c002007-12-08 20:04:36 +0000693 if (priv->mode == IW_MODE_INFRA) {
694 if (should_deauth_infrastructure(priv, assoc_req)) {
Holger Schurig10078322007-11-15 18:05:47 -0500695 ret = lbs_send_deauthentication(priv);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200696 if (ret) {
Holger Schurig9012b282007-05-25 11:27:16 -0400697 lbs_deb_assoc("Deauthentication due to new "
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200698 "configuration request failed: %d\n",
699 ret);
700 }
701 }
David Woodhouseaa21c002007-12-08 20:04:36 +0000702 } else if (priv->mode == IW_MODE_ADHOC) {
703 if (should_stop_adhoc(priv, assoc_req)) {
Holger Schurig10078322007-11-15 18:05:47 -0500704 ret = lbs_stop_adhoc_network(priv);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200705 if (ret) {
Holger Schurig9012b282007-05-25 11:27:16 -0400706 lbs_deb_assoc("Teardown of AdHoc network due to "
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200707 "new configuration request failed: %d\n",
708 ret);
709 }
710
711 }
712 }
713
714 /* Send the various configuration bits to the firmware */
715 if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
716 ret = assoc_helper_mode(priv, assoc_req);
Holger Schurig0765af42007-10-15 12:55:56 +0200717 if (ret)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200718 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200719 }
720
Dan Williamsef9a2642007-05-25 16:46:33 -0400721 if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
722 ret = assoc_helper_channel(priv, assoc_req);
Holger Schurig0765af42007-10-15 12:55:56 +0200723 if (ret)
Dan Williamsef9a2642007-05-25 16:46:33 -0400724 goto out;
Dan Williamsef9a2642007-05-25 16:46:33 -0400725 }
726
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200727 if ( test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)
728 || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) {
729 ret = assoc_helper_wep_keys(priv, assoc_req);
Holger Schurig0765af42007-10-15 12:55:56 +0200730 if (ret)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200731 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200732 }
733
734 if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
735 ret = assoc_helper_secinfo(priv, assoc_req);
Holger Schurig0765af42007-10-15 12:55:56 +0200736 if (ret)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200737 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200738 }
739
740 if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
741 ret = assoc_helper_wpa_ie(priv, assoc_req);
Holger Schurig0765af42007-10-15 12:55:56 +0200742 if (ret)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200743 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200744 }
745
746 if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)
747 || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
748 ret = assoc_helper_wpa_keys(priv, assoc_req);
Holger Schurig0765af42007-10-15 12:55:56 +0200749 if (ret)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200750 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200751 }
752
753 /* SSID/BSSID should be the _last_ config option set, because they
754 * trigger the association attempt.
755 */
756 if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)
757 || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
758 int success = 1;
759
760 ret = assoc_helper_associate(priv, assoc_req);
761 if (ret) {
Holger Schurig91843462007-11-28 14:05:02 +0100762 lbs_deb_assoc("ASSOC: association unsuccessful: %d\n",
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200763 ret);
764 success = 0;
765 }
766
David Woodhouseaa21c002007-12-08 20:04:36 +0000767 if (priv->connect_status != LBS_CONNECTED) {
Holger Schurig91843462007-11-28 14:05:02 +0100768 lbs_deb_assoc("ASSOC: association unsuccessful, "
769 "not connected\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200770 success = 0;
771 }
772
773 if (success) {
Holger Schurig52507c22008-01-28 17:25:53 +0100774 lbs_deb_assoc("associated to %s\n",
David Woodhouseaa21c002007-12-08 20:04:36 +0000775 print_mac(mac, priv->curbssparams.bssid));
Holger Schurig10078322007-11-15 18:05:47 -0500776 lbs_prepare_and_send_command(priv,
Dan Williams0aef64d2007-08-02 11:31:18 -0400777 CMD_802_11_RSSI,
778 0, CMD_OPTION_WAITFORRSP, 0, NULL);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200779 } else {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200780 ret = -1;
781 }
782 }
783
784out:
785 if (ret) {
Holger Schurig9012b282007-05-25 11:27:16 -0400786 lbs_deb_assoc("ASSOC: reconfiguration attempt unsuccessful: %d\n",
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200787 ret);
788 }
Dan Williamse76850d2007-05-25 17:09:41 -0400789
David Woodhouseaa21c002007-12-08 20:04:36 +0000790 mutex_lock(&priv->lock);
791 priv->in_progress_assoc_req = NULL;
792 mutex_unlock(&priv->lock);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200793 kfree(assoc_req);
Holger Schurig9012b282007-05-25 11:27:16 -0400794
795done:
796 lbs_deb_leave(LBS_DEB_ASSOC);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200797}
798
799
800/*
801 * Caller MUST hold any necessary locks
802 */
David Woodhouseaa21c002007-12-08 20:04:36 +0000803struct assoc_request *lbs_get_association_request(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200804{
805 struct assoc_request * assoc_req;
806
Holger Schurig0765af42007-10-15 12:55:56 +0200807 lbs_deb_enter(LBS_DEB_ASSOC);
David Woodhouseaa21c002007-12-08 20:04:36 +0000808 if (!priv->pending_assoc_req) {
809 priv->pending_assoc_req = kzalloc(sizeof(struct assoc_request),
Dan Williamse76850d2007-05-25 17:09:41 -0400810 GFP_KERNEL);
David Woodhouseaa21c002007-12-08 20:04:36 +0000811 if (!priv->pending_assoc_req) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200812 lbs_pr_info("Not enough memory to allocate association"
813 " request!\n");
814 return NULL;
815 }
816 }
817
818 /* Copy current configuration attributes to the association request,
819 * but don't overwrite any that are already set.
820 */
David Woodhouseaa21c002007-12-08 20:04:36 +0000821 assoc_req = priv->pending_assoc_req;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200822 if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000823 memcpy(&assoc_req->ssid, &priv->curbssparams.ssid,
Dan Williamsd8efea22007-05-28 23:54:55 -0400824 IW_ESSID_MAX_SIZE);
David Woodhouseaa21c002007-12-08 20:04:36 +0000825 assoc_req->ssid_len = priv->curbssparams.ssid_len;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200826 }
827
828 if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
David Woodhouseaa21c002007-12-08 20:04:36 +0000829 assoc_req->channel = priv->curbssparams.channel;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200830
Dan Williamse76850d2007-05-25 17:09:41 -0400831 if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags))
David Woodhouseaa21c002007-12-08 20:04:36 +0000832 assoc_req->band = priv->curbssparams.band;
Dan Williamse76850d2007-05-25 17:09:41 -0400833
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200834 if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags))
David Woodhouseaa21c002007-12-08 20:04:36 +0000835 assoc_req->mode = priv->mode;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200836
837 if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000838 memcpy(&assoc_req->bssid, priv->curbssparams.bssid,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200839 ETH_ALEN);
840 }
841
842 if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) {
843 int i;
844 for (i = 0; i < 4; i++) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000845 memcpy(&assoc_req->wep_keys[i], &priv->wep_keys[i],
Dan Williams1443b652007-08-02 10:45:55 -0400846 sizeof(struct enc_key));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200847 }
848 }
849
850 if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags))
David Woodhouseaa21c002007-12-08 20:04:36 +0000851 assoc_req->wep_tx_keyidx = priv->wep_tx_keyidx;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200852
853 if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000854 memcpy(&assoc_req->wpa_mcast_key, &priv->wpa_mcast_key,
Dan Williams1443b652007-08-02 10:45:55 -0400855 sizeof(struct enc_key));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200856 }
857
858 if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000859 memcpy(&assoc_req->wpa_unicast_key, &priv->wpa_unicast_key,
Dan Williams1443b652007-08-02 10:45:55 -0400860 sizeof(struct enc_key));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200861 }
862
863 if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000864 memcpy(&assoc_req->secinfo, &priv->secinfo,
Holger Schurig10078322007-11-15 18:05:47 -0500865 sizeof(struct lbs_802_11_security));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200866 }
867
868 if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000869 memcpy(&assoc_req->wpa_ie, &priv->wpa_ie,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200870 MAX_WPA_IE_LEN);
David Woodhouseaa21c002007-12-08 20:04:36 +0000871 assoc_req->wpa_ie_len = priv->wpa_ie_len;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200872 }
873
Holger Schurig0765af42007-10-15 12:55:56 +0200874 lbs_deb_leave(LBS_DEB_ASSOC);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200875 return assoc_req;
876}
Holger Schurig697900a2008-04-02 16:27:10 +0200877
878
879/**
880 * @brief This function finds common rates between rate1 and card rates.
881 *
882 * It will fill common rates in rate1 as output if found.
883 *
884 * NOTE: Setting the MSB of the basic rates need to be taken
885 * care, either before or after calling this function
886 *
887 * @param priv A pointer to struct lbs_private structure
888 * @param rate1 the buffer which keeps input and output
889 * @param rate1_size the size of rate1 buffer; new size of buffer on return
890 *
891 * @return 0 or -1
892 */
893static int get_common_rates(struct lbs_private *priv,
894 u8 *rates,
895 u16 *rates_size)
896{
897 u8 *card_rates = lbs_bg_rates;
898 size_t num_card_rates = sizeof(lbs_bg_rates);
899 int ret = 0, i, j;
900 u8 tmp[30];
901 size_t tmp_size = 0;
902
903 /* For each rate in card_rates that exists in rate1, copy to tmp */
904 for (i = 0; card_rates[i] && (i < num_card_rates); i++) {
905 for (j = 0; rates[j] && (j < *rates_size); j++) {
906 if (rates[j] == card_rates[i])
907 tmp[tmp_size++] = card_rates[i];
908 }
909 }
910
911 lbs_deb_hex(LBS_DEB_JOIN, "AP rates ", rates, *rates_size);
912 lbs_deb_hex(LBS_DEB_JOIN, "card rates ", card_rates, num_card_rates);
913 lbs_deb_hex(LBS_DEB_JOIN, "common rates", tmp, tmp_size);
914 lbs_deb_join("TX data rate 0x%02x\n", priv->cur_rate);
915
916 if (!priv->auto_rate) {
917 for (i = 0; i < tmp_size; i++) {
918 if (tmp[i] == priv->cur_rate)
919 goto done;
920 }
921 lbs_pr_alert("Previously set fixed data rate %#x isn't "
922 "compatible with the network.\n", priv->cur_rate);
923 ret = -1;
924 goto done;
925 }
926 ret = 0;
927
928done:
929 memset(rates, 0, *rates_size);
930 *rates_size = min_t(int, tmp_size, *rates_size);
931 memcpy(rates, tmp, *rates_size);
932 return ret;
933}
934
935
936/**
937 * @brief Sets the MSB on basic rates as the firmware requires
938 *
939 * Scan through an array and set the MSB for basic data rates.
940 *
941 * @param rates buffer of data rates
942 * @param len size of buffer
943 */
944static void lbs_set_basic_rate_flags(u8 *rates, size_t len)
945{
946 int i;
947
948 for (i = 0; i < len; i++) {
949 if (rates[i] == 0x02 || rates[i] == 0x04 ||
950 rates[i] == 0x0b || rates[i] == 0x16)
951 rates[i] |= 0x80;
952 }
953}
954
955/**
956 * @brief Send Deauthentication Request
957 *
958 * @param priv A pointer to struct lbs_private structure
959 * @return 0--success, -1--fail
960 */
961int lbs_send_deauthentication(struct lbs_private *priv)
962{
963 return lbs_prepare_and_send_command(priv, CMD_802_11_DEAUTHENTICATE,
964 0, CMD_OPTION_WAITFORRSP, 0, NULL);
965}
966
967/**
968 * @brief This function prepares command of authenticate.
969 *
970 * @param priv A pointer to struct lbs_private structure
971 * @param cmd A pointer to cmd_ds_command structure
972 * @param pdata_buf Void cast of pointer to a BSSID to authenticate with
973 *
974 * @return 0 or -1
975 */
976int lbs_cmd_80211_authenticate(struct lbs_private *priv,
977 struct cmd_ds_command *cmd,
978 void *pdata_buf)
979{
980 struct cmd_ds_802_11_authenticate *pauthenticate = &cmd->params.auth;
981 int ret = -1;
982 u8 *bssid = pdata_buf;
983 DECLARE_MAC_BUF(mac);
984
985 lbs_deb_enter(LBS_DEB_JOIN);
986
987 cmd->command = cpu_to_le16(CMD_802_11_AUTHENTICATE);
988 cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_authenticate)
989 + S_DS_GEN);
990
991 /* translate auth mode to 802.11 defined wire value */
992 switch (priv->secinfo.auth_mode) {
993 case IW_AUTH_ALG_OPEN_SYSTEM:
994 pauthenticate->authtype = 0x00;
995 break;
996 case IW_AUTH_ALG_SHARED_KEY:
997 pauthenticate->authtype = 0x01;
998 break;
999 case IW_AUTH_ALG_LEAP:
1000 pauthenticate->authtype = 0x80;
1001 break;
1002 default:
1003 lbs_deb_join("AUTH_CMD: invalid auth alg 0x%X\n",
1004 priv->secinfo.auth_mode);
1005 goto out;
1006 }
1007
1008 memcpy(pauthenticate->macaddr, bssid, ETH_ALEN);
1009
1010 lbs_deb_join("AUTH_CMD: BSSID %s, auth 0x%x\n",
1011 print_mac(mac, bssid), pauthenticate->authtype);
1012 ret = 0;
1013
1014out:
1015 lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
1016 return ret;
1017}
1018
1019int lbs_cmd_80211_deauthenticate(struct lbs_private *priv,
1020 struct cmd_ds_command *cmd)
1021{
1022 struct cmd_ds_802_11_deauthenticate *dauth = &cmd->params.deauth;
1023
1024 lbs_deb_enter(LBS_DEB_JOIN);
1025
1026 cmd->command = cpu_to_le16(CMD_802_11_DEAUTHENTICATE);
1027 cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_deauthenticate) +
1028 S_DS_GEN);
1029
1030 /* set AP MAC address */
1031 memmove(dauth->macaddr, priv->curbssparams.bssid, ETH_ALEN);
1032
1033 /* Reason code 3 = Station is leaving */
1034#define REASON_CODE_STA_LEAVING 3
1035 dauth->reasoncode = cpu_to_le16(REASON_CODE_STA_LEAVING);
1036
1037 lbs_deb_leave(LBS_DEB_JOIN);
1038 return 0;
1039}
1040
1041int lbs_cmd_80211_associate(struct lbs_private *priv,
1042 struct cmd_ds_command *cmd, void *pdata_buf)
1043{
1044 struct cmd_ds_802_11_associate *passo = &cmd->params.associate;
1045 int ret = 0;
1046 struct assoc_request *assoc_req = pdata_buf;
1047 struct bss_descriptor *bss = &assoc_req->bss;
1048 u8 *pos;
1049 u16 tmpcap, tmplen;
1050 struct mrvlietypes_ssidparamset *ssid;
1051 struct mrvlietypes_phyparamset *phy;
1052 struct mrvlietypes_ssparamset *ss;
1053 struct mrvlietypes_ratesparamset *rates;
1054 struct mrvlietypes_rsnparamset *rsn;
1055
1056 lbs_deb_enter(LBS_DEB_ASSOC);
1057
1058 pos = (u8 *) passo;
1059
1060 if (!priv) {
1061 ret = -1;
1062 goto done;
1063 }
1064
1065 cmd->command = cpu_to_le16(CMD_802_11_ASSOCIATE);
1066
1067 memcpy(passo->peerstaaddr, bss->bssid, sizeof(passo->peerstaaddr));
1068 pos += sizeof(passo->peerstaaddr);
1069
1070 /* set the listen interval */
1071 passo->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL);
1072
1073 pos += sizeof(passo->capability);
1074 pos += sizeof(passo->listeninterval);
1075 pos += sizeof(passo->bcnperiod);
1076 pos += sizeof(passo->dtimperiod);
1077
1078 ssid = (struct mrvlietypes_ssidparamset *) pos;
1079 ssid->header.type = cpu_to_le16(TLV_TYPE_SSID);
1080 tmplen = bss->ssid_len;
1081 ssid->header.len = cpu_to_le16(tmplen);
1082 memcpy(ssid->ssid, bss->ssid, tmplen);
1083 pos += sizeof(ssid->header) + tmplen;
1084
1085 phy = (struct mrvlietypes_phyparamset *) pos;
1086 phy->header.type = cpu_to_le16(TLV_TYPE_PHY_DS);
1087 tmplen = sizeof(phy->fh_ds.dsparamset);
1088 phy->header.len = cpu_to_le16(tmplen);
1089 memcpy(&phy->fh_ds.dsparamset,
1090 &bss->phyparamset.dsparamset.currentchan,
1091 tmplen);
1092 pos += sizeof(phy->header) + tmplen;
1093
1094 ss = (struct mrvlietypes_ssparamset *) pos;
1095 ss->header.type = cpu_to_le16(TLV_TYPE_CF);
1096 tmplen = sizeof(ss->cf_ibss.cfparamset);
1097 ss->header.len = cpu_to_le16(tmplen);
1098 pos += sizeof(ss->header) + tmplen;
1099
1100 rates = (struct mrvlietypes_ratesparamset *) pos;
1101 rates->header.type = cpu_to_le16(TLV_TYPE_RATES);
1102 memcpy(&rates->rates, &bss->rates, MAX_RATES);
1103 tmplen = MAX_RATES;
1104 if (get_common_rates(priv, rates->rates, &tmplen)) {
1105 ret = -1;
1106 goto done;
1107 }
1108 pos += sizeof(rates->header) + tmplen;
1109 rates->header.len = cpu_to_le16(tmplen);
1110 lbs_deb_assoc("ASSOC_CMD: num rates %u\n", tmplen);
1111
1112 /* Copy the infra. association rates into Current BSS state structure */
1113 memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
1114 memcpy(&priv->curbssparams.rates, &rates->rates, tmplen);
1115
1116 /* Set MSB on basic rates as the firmware requires, but _after_
1117 * copying to current bss rates.
1118 */
1119 lbs_set_basic_rate_flags(rates->rates, tmplen);
1120
1121 if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
1122 rsn = (struct mrvlietypes_rsnparamset *) pos;
1123 /* WPA_IE or WPA2_IE */
1124 rsn->header.type = cpu_to_le16((u16) assoc_req->wpa_ie[0]);
1125 tmplen = (u16) assoc_req->wpa_ie[1];
1126 rsn->header.len = cpu_to_le16(tmplen);
1127 memcpy(rsn->rsnie, &assoc_req->wpa_ie[2], tmplen);
1128 lbs_deb_hex(LBS_DEB_JOIN, "ASSOC_CMD: RSN IE", (u8 *) rsn,
1129 sizeof(rsn->header) + tmplen);
1130 pos += sizeof(rsn->header) + tmplen;
1131 }
1132
1133 /* update curbssparams */
1134 priv->curbssparams.channel = bss->phyparamset.dsparamset.currentchan;
1135
1136 if (lbs_parse_dnld_countryinfo_11d(priv, bss)) {
1137 ret = -1;
1138 goto done;
1139 }
1140
1141 cmd->size = cpu_to_le16((u16) (pos - (u8 *) passo) + S_DS_GEN);
1142
1143 /* set the capability info */
1144 tmpcap = (bss->capability & CAPINFO_MASK);
1145 if (bss->mode == IW_MODE_INFRA)
1146 tmpcap |= WLAN_CAPABILITY_ESS;
1147 passo->capability = cpu_to_le16(tmpcap);
1148 lbs_deb_assoc("ASSOC_CMD: capability 0x%04x\n", tmpcap);
1149
1150done:
1151 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1152 return ret;
1153}
1154
1155int lbs_cmd_80211_ad_hoc_start(struct lbs_private *priv,
1156 struct cmd_ds_command *cmd, void *pdata_buf)
1157{
1158 struct cmd_ds_802_11_ad_hoc_start *adhs = &cmd->params.ads;
1159 int ret = 0;
1160 int cmdappendsize = 0;
1161 struct assoc_request *assoc_req = pdata_buf;
1162 u16 tmpcap = 0;
1163 size_t ratesize = 0;
1164
1165 lbs_deb_enter(LBS_DEB_JOIN);
1166
1167 if (!priv) {
1168 ret = -1;
1169 goto done;
1170 }
1171
1172 cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_START);
1173
1174 /*
1175 * Fill in the parameters for 2 data structures:
1176 * 1. cmd_ds_802_11_ad_hoc_start command
1177 * 2. priv->scantable[i]
1178 *
1179 * Driver will fill up SSID, bsstype,IBSS param, Physical Param,
1180 * probe delay, and cap info.
1181 *
1182 * Firmware will fill up beacon period, DTIM, Basic rates
1183 * and operational rates.
1184 */
1185
1186 memset(adhs->ssid, 0, IW_ESSID_MAX_SIZE);
1187 memcpy(adhs->ssid, assoc_req->ssid, assoc_req->ssid_len);
1188
1189 lbs_deb_join("ADHOC_S_CMD: SSID '%s', ssid length %u\n",
1190 escape_essid(assoc_req->ssid, assoc_req->ssid_len),
1191 assoc_req->ssid_len);
1192
1193 /* set the BSS type */
1194 adhs->bsstype = CMD_BSS_TYPE_IBSS;
1195 priv->mode = IW_MODE_ADHOC;
1196 if (priv->beacon_period == 0)
1197 priv->beacon_period = MRVDRV_BEACON_INTERVAL;
1198 adhs->beaconperiod = cpu_to_le16(priv->beacon_period);
1199
1200 /* set Physical param set */
1201#define DS_PARA_IE_ID 3
1202#define DS_PARA_IE_LEN 1
1203
1204 adhs->phyparamset.dsparamset.elementid = DS_PARA_IE_ID;
1205 adhs->phyparamset.dsparamset.len = DS_PARA_IE_LEN;
1206
1207 WARN_ON(!assoc_req->channel);
1208
1209 lbs_deb_join("ADHOC_S_CMD: Creating ADHOC on channel %d\n",
1210 assoc_req->channel);
1211
1212 adhs->phyparamset.dsparamset.currentchan = assoc_req->channel;
1213
1214 /* set IBSS param set */
1215#define IBSS_PARA_IE_ID 6
1216#define IBSS_PARA_IE_LEN 2
1217
1218 adhs->ssparamset.ibssparamset.elementid = IBSS_PARA_IE_ID;
1219 adhs->ssparamset.ibssparamset.len = IBSS_PARA_IE_LEN;
1220 adhs->ssparamset.ibssparamset.atimwindow = 0;
1221
1222 /* set capability info */
1223 tmpcap = WLAN_CAPABILITY_IBSS;
1224 if (assoc_req->secinfo.wep_enabled) {
1225 lbs_deb_join("ADHOC_S_CMD: WEP enabled, "
1226 "setting privacy on\n");
1227 tmpcap |= WLAN_CAPABILITY_PRIVACY;
1228 } else {
1229 lbs_deb_join("ADHOC_S_CMD: WEP disabled, "
1230 "setting privacy off\n");
1231 }
1232 adhs->capability = cpu_to_le16(tmpcap);
1233
1234 /* probedelay */
1235 adhs->probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
1236
1237 memset(adhs->rates, 0, sizeof(adhs->rates));
1238 ratesize = min(sizeof(adhs->rates), sizeof(lbs_bg_rates));
1239 memcpy(adhs->rates, lbs_bg_rates, ratesize);
1240
1241 /* Copy the ad-hoc creating rates into Current BSS state structure */
1242 memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
1243 memcpy(&priv->curbssparams.rates, &adhs->rates, ratesize);
1244
1245 /* Set MSB on basic rates as the firmware requires, but _after_
1246 * copying to current bss rates.
1247 */
1248 lbs_set_basic_rate_flags(adhs->rates, ratesize);
1249
1250 lbs_deb_join("ADHOC_S_CMD: rates=%02x %02x %02x %02x \n",
1251 adhs->rates[0], adhs->rates[1], adhs->rates[2], adhs->rates[3]);
1252
1253 lbs_deb_join("ADHOC_S_CMD: AD HOC Start command is ready\n");
1254
1255 if (lbs_create_dnld_countryinfo_11d(priv)) {
1256 lbs_deb_join("ADHOC_S_CMD: dnld_countryinfo_11d failed\n");
1257 ret = -1;
1258 goto done;
1259 }
1260
1261 cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_start) +
1262 S_DS_GEN + cmdappendsize);
1263
1264 ret = 0;
1265done:
1266 lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
1267 return ret;
1268}
1269
1270int lbs_cmd_80211_ad_hoc_stop(struct cmd_ds_command *cmd)
1271{
1272 cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_STOP);
1273 cmd->size = cpu_to_le16(S_DS_GEN);
1274
1275 return 0;
1276}
1277
1278int lbs_cmd_80211_ad_hoc_join(struct lbs_private *priv,
1279 struct cmd_ds_command *cmd, void *pdata_buf)
1280{
1281 struct cmd_ds_802_11_ad_hoc_join *join_cmd = &cmd->params.adj;
1282 struct assoc_request *assoc_req = pdata_buf;
1283 struct bss_descriptor *bss = &assoc_req->bss;
1284 int cmdappendsize = 0;
1285 int ret = 0;
1286 u16 ratesize = 0;
1287 DECLARE_MAC_BUF(mac);
1288
1289 lbs_deb_enter(LBS_DEB_JOIN);
1290
1291 cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_JOIN);
1292
1293 join_cmd->bss.type = CMD_BSS_TYPE_IBSS;
1294 join_cmd->bss.beaconperiod = cpu_to_le16(bss->beaconperiod);
1295
1296 memcpy(&join_cmd->bss.bssid, &bss->bssid, ETH_ALEN);
1297 memcpy(&join_cmd->bss.ssid, &bss->ssid, bss->ssid_len);
1298
1299 memcpy(&join_cmd->bss.phyparamset, &bss->phyparamset,
1300 sizeof(union ieeetypes_phyparamset));
1301
1302 memcpy(&join_cmd->bss.ssparamset, &bss->ssparamset,
1303 sizeof(union IEEEtypes_ssparamset));
1304
1305 join_cmd->bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK);
1306 lbs_deb_join("ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n",
1307 bss->capability, CAPINFO_MASK);
1308
1309 /* information on BSSID descriptor passed to FW */
1310 lbs_deb_join(
1311 "ADHOC_J_CMD: BSSID = %s, SSID = '%s'\n",
1312 print_mac(mac, join_cmd->bss.bssid),
1313 join_cmd->bss.ssid);
1314
1315 /* failtimeout */
1316 join_cmd->failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT);
1317
1318 /* probedelay */
1319 join_cmd->probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
1320
1321 priv->curbssparams.channel = bss->channel;
1322
1323 /* Copy Data rates from the rates recorded in scan response */
1324 memset(join_cmd->bss.rates, 0, sizeof(join_cmd->bss.rates));
1325 ratesize = min_t(u16, sizeof(join_cmd->bss.rates), MAX_RATES);
1326 memcpy(join_cmd->bss.rates, bss->rates, ratesize);
1327 if (get_common_rates(priv, join_cmd->bss.rates, &ratesize)) {
1328 lbs_deb_join("ADHOC_J_CMD: get_common_rates returns error.\n");
1329 ret = -1;
1330 goto done;
1331 }
1332
1333 /* Copy the ad-hoc creating rates into Current BSS state structure */
1334 memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
1335 memcpy(&priv->curbssparams.rates, join_cmd->bss.rates, ratesize);
1336
1337 /* Set MSB on basic rates as the firmware requires, but _after_
1338 * copying to current bss rates.
1339 */
1340 lbs_set_basic_rate_flags(join_cmd->bss.rates, ratesize);
1341
1342 join_cmd->bss.ssparamset.ibssparamset.atimwindow =
1343 cpu_to_le16(bss->atimwindow);
1344
1345 if (assoc_req->secinfo.wep_enabled) {
1346 u16 tmp = le16_to_cpu(join_cmd->bss.capability);
1347 tmp |= WLAN_CAPABILITY_PRIVACY;
1348 join_cmd->bss.capability = cpu_to_le16(tmp);
1349 }
1350
1351 if (priv->psmode == LBS802_11POWERMODEMAX_PSP) {
1352 /* wake up first */
1353 __le32 Localpsmode;
1354
1355 Localpsmode = cpu_to_le32(LBS802_11POWERMODECAM);
1356 ret = lbs_prepare_and_send_command(priv,
1357 CMD_802_11_PS_MODE,
1358 CMD_ACT_SET,
1359 0, 0, &Localpsmode);
1360
1361 if (ret) {
1362 ret = -1;
1363 goto done;
1364 }
1365 }
1366
1367 if (lbs_parse_dnld_countryinfo_11d(priv, bss)) {
1368 ret = -1;
1369 goto done;
1370 }
1371
1372 cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_join) +
1373 S_DS_GEN + cmdappendsize);
1374
1375done:
1376 lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
1377 return ret;
1378}
1379
1380int lbs_ret_80211_associate(struct lbs_private *priv,
1381 struct cmd_ds_command *resp)
1382{
1383 int ret = 0;
1384 union iwreq_data wrqu;
1385 struct ieeetypes_assocrsp *passocrsp;
1386 struct bss_descriptor *bss;
1387 u16 status_code;
1388
1389 lbs_deb_enter(LBS_DEB_ASSOC);
1390
1391 if (!priv->in_progress_assoc_req) {
1392 lbs_deb_assoc("ASSOC_RESP: no in-progress assoc request\n");
1393 ret = -1;
1394 goto done;
1395 }
1396 bss = &priv->in_progress_assoc_req->bss;
1397
1398 passocrsp = (struct ieeetypes_assocrsp *) &resp->params;
1399
1400 /*
1401 * Older FW versions map the IEEE 802.11 Status Code in the association
1402 * response to the following values returned in passocrsp->statuscode:
1403 *
1404 * IEEE Status Code Marvell Status Code
1405 * 0 -> 0x0000 ASSOC_RESULT_SUCCESS
1406 * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
1407 * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
1408 * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
1409 * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
1410 * others -> 0x0003 ASSOC_RESULT_REFUSED
1411 *
1412 * Other response codes:
1413 * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused)
1414 * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for
1415 * association response from the AP)
1416 */
1417
1418 status_code = le16_to_cpu(passocrsp->statuscode);
1419 switch (status_code) {
1420 case 0x00:
1421 break;
1422 case 0x01:
1423 lbs_deb_assoc("ASSOC_RESP: invalid parameters\n");
1424 break;
1425 case 0x02:
1426 lbs_deb_assoc("ASSOC_RESP: internal timer "
1427 "expired while waiting for the AP\n");
1428 break;
1429 case 0x03:
1430 lbs_deb_assoc("ASSOC_RESP: association "
1431 "refused by AP\n");
1432 break;
1433 case 0x04:
1434 lbs_deb_assoc("ASSOC_RESP: authentication "
1435 "refused by AP\n");
1436 break;
1437 default:
1438 lbs_deb_assoc("ASSOC_RESP: failure reason 0x%02x "
1439 " unknown\n", status_code);
1440 break;
1441 }
1442
1443 if (status_code) {
1444 lbs_mac_event_disconnected(priv);
1445 ret = -1;
1446 goto done;
1447 }
1448
1449 lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_RESP", (void *)&resp->params,
1450 le16_to_cpu(resp->size) - S_DS_GEN);
1451
1452 /* Send a Media Connected event, according to the Spec */
1453 priv->connect_status = LBS_CONNECTED;
1454
1455 /* Update current SSID and BSSID */
1456 memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE);
1457 priv->curbssparams.ssid_len = bss->ssid_len;
1458 memcpy(priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
1459
1460 priv->SNR[TYPE_RXPD][TYPE_AVG] = 0;
1461 priv->NF[TYPE_RXPD][TYPE_AVG] = 0;
1462
1463 memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR));
1464 memset(priv->rawNF, 0x00, sizeof(priv->rawNF));
1465 priv->nextSNRNF = 0;
1466 priv->numSNRNF = 0;
1467
1468 netif_carrier_on(priv->dev);
1469 if (!priv->tx_pending_len)
1470 netif_wake_queue(priv->dev);
1471
1472 memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN);
1473 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1474 wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
1475
1476done:
1477 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
1478 return ret;
1479}
1480
1481int lbs_ret_80211_disassociate(struct lbs_private *priv)
1482{
1483 lbs_deb_enter(LBS_DEB_JOIN);
1484
1485 lbs_mac_event_disconnected(priv);
1486
1487 lbs_deb_leave(LBS_DEB_JOIN);
1488 return 0;
1489}
1490
1491int lbs_ret_80211_ad_hoc_start(struct lbs_private *priv,
1492 struct cmd_ds_command *resp)
1493{
1494 int ret = 0;
1495 u16 command = le16_to_cpu(resp->command);
1496 u16 result = le16_to_cpu(resp->result);
1497 struct cmd_ds_802_11_ad_hoc_result *padhocresult;
1498 union iwreq_data wrqu;
1499 struct bss_descriptor *bss;
1500 DECLARE_MAC_BUF(mac);
1501
1502 lbs_deb_enter(LBS_DEB_JOIN);
1503
1504 padhocresult = &resp->params.result;
1505
1506 lbs_deb_join("ADHOC_RESP: size = %d\n", le16_to_cpu(resp->size));
1507 lbs_deb_join("ADHOC_RESP: command = %x\n", command);
1508 lbs_deb_join("ADHOC_RESP: result = %x\n", result);
1509
1510 if (!priv->in_progress_assoc_req) {
1511 lbs_deb_join("ADHOC_RESP: no in-progress association "
1512 "request\n");
1513 ret = -1;
1514 goto done;
1515 }
1516 bss = &priv->in_progress_assoc_req->bss;
1517
1518 /*
1519 * Join result code 0 --> SUCCESS
1520 */
1521 if (result) {
1522 lbs_deb_join("ADHOC_RESP: failed\n");
1523 if (priv->connect_status == LBS_CONNECTED)
1524 lbs_mac_event_disconnected(priv);
1525 ret = -1;
1526 goto done;
1527 }
1528
1529 /*
1530 * Now the join cmd should be successful
1531 * If BSSID has changed use SSID to compare instead of BSSID
1532 */
1533 lbs_deb_join("ADHOC_RESP: associated to '%s'\n",
1534 escape_essid(bss->ssid, bss->ssid_len));
1535
1536 /* Send a Media Connected event, according to the Spec */
1537 priv->connect_status = LBS_CONNECTED;
1538
1539 if (command == CMD_RET(CMD_802_11_AD_HOC_START)) {
1540 /* Update the created network descriptor with the new BSSID */
1541 memcpy(bss->bssid, padhocresult->bssid, ETH_ALEN);
1542 }
1543
1544 /* Set the BSSID from the joined/started descriptor */
1545 memcpy(&priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
1546
1547 /* Set the new SSID to current SSID */
1548 memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE);
1549 priv->curbssparams.ssid_len = bss->ssid_len;
1550
1551 netif_carrier_on(priv->dev);
1552 if (!priv->tx_pending_len)
1553 netif_wake_queue(priv->dev);
1554
1555 memset(&wrqu, 0, sizeof(wrqu));
1556 memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN);
1557 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1558 wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
1559
1560 lbs_deb_join("ADHOC_RESP: - Joined/Started Ad Hoc\n");
1561 lbs_deb_join("ADHOC_RESP: channel = %d\n", priv->curbssparams.channel);
1562 lbs_deb_join("ADHOC_RESP: BSSID = %s\n",
1563 print_mac(mac, padhocresult->bssid));
1564
1565done:
1566 lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
1567 return ret;
1568}
1569
1570int lbs_ret_80211_ad_hoc_stop(struct lbs_private *priv)
1571{
1572 lbs_deb_enter(LBS_DEB_JOIN);
1573
1574 lbs_mac_event_disconnected(priv);
1575
1576 lbs_deb_leave(LBS_DEB_JOIN);
1577 return 0;
1578}