]>
Commit | Line | Data |
---|---|---|
876c9d3a MT |
1 | /** |
2 | * This file contains the handling of command | |
3 | * responses as well as events generated by firmware. | |
4 | */ | |
5 | #include <linux/delay.h> | |
6 | #include <linux/if_arp.h> | |
7 | #include <linux/netdevice.h> | |
8 | ||
9 | #include <net/iw_handler.h> | |
10 | ||
11 | #include "host.h" | |
876c9d3a MT |
12 | #include "decl.h" |
13 | #include "defs.h" | |
14 | #include "dev.h" | |
15 | #include "join.h" | |
16 | #include "wext.h" | |
17 | ||
18 | /** | |
19 | * @brief This function handles disconnect event. it | |
20 | * reports disconnect to upper layer, clean tx/rx packets, | |
21 | * reset link state etc. | |
22 | * | |
69f9032d | 23 | * @param priv A pointer to struct lbs_private structure |
876c9d3a MT |
24 | * @return n/a |
25 | */ | |
69f9032d | 26 | void lbs_mac_event_disconnected(struct lbs_private *priv) |
876c9d3a | 27 | { |
876c9d3a MT |
28 | union iwreq_data wrqu; |
29 | ||
aa21c004 | 30 | if (priv->connect_status != LBS_CONNECTED) |
876c9d3a MT |
31 | return; |
32 | ||
91843463 | 33 | lbs_deb_enter(LBS_DEB_ASSOC); |
876c9d3a MT |
34 | |
35 | memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); | |
36 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | |
37 | ||
38 | /* | |
39 | * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. | |
40 | * It causes problem in the Supplicant | |
41 | */ | |
42 | ||
43 | msleep_interruptible(1000); | |
634b8f49 | 44 | wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); |
876c9d3a MT |
45 | |
46 | /* Free Tx and Rx packets */ | |
aa21c004 DW |
47 | kfree_skb(priv->currenttxskb); |
48 | priv->currenttxskb = NULL; | |
876c9d3a MT |
49 | |
50 | /* report disconnect to upper layer */ | |
634b8f49 HS |
51 | netif_stop_queue(priv->dev); |
52 | netif_carrier_off(priv->dev); | |
876c9d3a MT |
53 | |
54 | /* reset SNR/NF/RSSI values */ | |
aa21c004 DW |
55 | memset(priv->SNR, 0x00, sizeof(priv->SNR)); |
56 | memset(priv->NF, 0x00, sizeof(priv->NF)); | |
57 | memset(priv->RSSI, 0x00, sizeof(priv->RSSI)); | |
58 | memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR)); | |
59 | memset(priv->rawNF, 0x00, sizeof(priv->rawNF)); | |
60 | priv->nextSNRNF = 0; | |
61 | priv->numSNRNF = 0; | |
62 | priv->connect_status = LBS_DISCONNECTED; | |
876c9d3a | 63 | |
e76850d6 DW |
64 | /* Clear out associated SSID and BSSID since connection is |
65 | * no longer valid. | |
66 | */ | |
aa21c004 DW |
67 | memset(&priv->curbssparams.bssid, 0, ETH_ALEN); |
68 | memset(&priv->curbssparams.ssid, 0, IW_ESSID_MAX_SIZE); | |
69 | priv->curbssparams.ssid_len = 0; | |
876c9d3a | 70 | |
aa21c004 | 71 | if (priv->psstate != PS_STATE_FULL_POWER) { |
876c9d3a | 72 | /* make firmware to exit PS mode */ |
a6c8700f | 73 | lbs_deb_cmd("disconnected, so exit PS mode\n"); |
10078321 | 74 | lbs_ps_wakeup(priv, 0); |
876c9d3a | 75 | } |
a6c8700f | 76 | lbs_deb_leave(LBS_DEB_CMD); |
876c9d3a MT |
77 | } |
78 | ||
79 | /** | |
80 | * @brief This function handles MIC failure event. | |
81 | * | |
69f9032d | 82 | * @param priv A pointer to struct lbs_private structure |
876c9d3a MT |
83 | * @para event the event id |
84 | * @return n/a | |
85 | */ | |
69f9032d | 86 | static void handle_mic_failureevent(struct lbs_private *priv, u32 event) |
876c9d3a MT |
87 | { |
88 | char buf[50]; | |
89 | ||
a6c8700f | 90 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a MT |
91 | memset(buf, 0, sizeof(buf)); |
92 | ||
93 | sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication "); | |
94 | ||
95 | if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) { | |
96 | strcat(buf, "unicast "); | |
97 | } else { | |
98 | strcat(buf, "multicast "); | |
99 | } | |
100 | ||
10078321 | 101 | lbs_send_iwevcustom_event(priv, buf); |
a6c8700f | 102 | lbs_deb_leave(LBS_DEB_CMD); |
876c9d3a MT |
103 | } |
104 | ||
69f9032d | 105 | static int lbs_ret_reg_access(struct lbs_private *priv, |
876c9d3a MT |
106 | u16 type, struct cmd_ds_command *resp) |
107 | { | |
9012b28a | 108 | int ret = 0; |
876c9d3a | 109 | |
9012b28a | 110 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a MT |
111 | |
112 | switch (type) { | |
6b63cd0f | 113 | case CMD_RET(CMD_MAC_REG_ACCESS): |
876c9d3a | 114 | { |
981f187b | 115 | struct cmd_ds_mac_reg_access *reg = &resp->params.macreg; |
876c9d3a | 116 | |
aa21c004 DW |
117 | priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset); |
118 | priv->offsetvalue.value = le32_to_cpu(reg->value); | |
876c9d3a MT |
119 | break; |
120 | } | |
121 | ||
6b63cd0f | 122 | case CMD_RET(CMD_BBP_REG_ACCESS): |
876c9d3a | 123 | { |
981f187b | 124 | struct cmd_ds_bbp_reg_access *reg = &resp->params.bbpreg; |
876c9d3a | 125 | |
aa21c004 DW |
126 | priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset); |
127 | priv->offsetvalue.value = reg->value; | |
876c9d3a MT |
128 | break; |
129 | } | |
130 | ||
6b63cd0f | 131 | case CMD_RET(CMD_RF_REG_ACCESS): |
876c9d3a | 132 | { |
981f187b | 133 | struct cmd_ds_rf_reg_access *reg = &resp->params.rfreg; |
876c9d3a | 134 | |
aa21c004 DW |
135 | priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset); |
136 | priv->offsetvalue.value = reg->value; | |
876c9d3a MT |
137 | break; |
138 | } | |
139 | ||
140 | default: | |
9012b28a | 141 | ret = -1; |
876c9d3a MT |
142 | } |
143 | ||
8b17d723 | 144 | lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); |
9012b28a | 145 | return ret; |
876c9d3a MT |
146 | } |
147 | ||
69f9032d | 148 | static int lbs_ret_get_hw_spec(struct lbs_private *priv, |
876c9d3a MT |
149 | struct cmd_ds_command *resp) |
150 | { | |
151 | u32 i; | |
152 | struct cmd_ds_get_hw_spec *hwspec = &resp->params.hwspec; | |
876c9d3a | 153 | int ret = 0; |
0795af57 | 154 | DECLARE_MAC_BUF(mac); |
876c9d3a | 155 | |
9012b28a | 156 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a | 157 | |
aa21c004 | 158 | priv->fwcapinfo = le32_to_cpu(hwspec->fwcapinfo); |
876c9d3a | 159 | |
aa21c004 | 160 | memcpy(priv->fwreleasenumber, hwspec->fwreleasenumber, 4); |
876c9d3a | 161 | |
a6c8700f | 162 | lbs_deb_cmd("GET_HW_SPEC: firmware release %u.%u.%up%u\n", |
aa21c004 DW |
163 | priv->fwreleasenumber[2], priv->fwreleasenumber[1], |
164 | priv->fwreleasenumber[0], priv->fwreleasenumber[3]); | |
0795af57 JP |
165 | lbs_deb_cmd("GET_HW_SPEC: MAC addr %s\n", |
166 | print_mac(mac, hwspec->permanentaddr)); | |
a6c8700f | 167 | lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", |
876c9d3a MT |
168 | hwspec->hwifversion, hwspec->version); |
169 | ||
70500f54 MV |
170 | /* Clamp region code to 8-bit since FW spec indicates that it should |
171 | * only ever be 8-bit, even though the field size is 16-bit. Some firmware | |
172 | * returns non-zero high 8 bits here. | |
173 | */ | |
aa21c004 | 174 | priv->regioncode = le16_to_cpu(hwspec->regioncode) & 0xFF; |
876c9d3a MT |
175 | |
176 | for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { | |
177 | /* use the region code to search for the index */ | |
aa21c004 | 178 | if (priv->regioncode == lbs_region_code_to_index[i]) { |
876c9d3a MT |
179 | break; |
180 | } | |
181 | } | |
182 | ||
183 | /* if it's unidentified region code, use the default (USA) */ | |
184 | if (i >= MRVDRV_MAX_REGION_CODE) { | |
aa21c004 | 185 | priv->regioncode = 0x10; |
981f187b | 186 | lbs_pr_info("unidentified region code; using the default (USA)\n"); |
876c9d3a MT |
187 | } |
188 | ||
aa21c004 DW |
189 | if (priv->current_addr[0] == 0xff) |
190 | memmove(priv->current_addr, hwspec->permanentaddr, ETH_ALEN); | |
876c9d3a | 191 | |
aa21c004 | 192 | memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN); |
78523daa | 193 | if (priv->mesh_dev) |
aa21c004 | 194 | memcpy(priv->mesh_dev->dev_addr, priv->current_addr, ETH_ALEN); |
876c9d3a | 195 | |
aa21c004 | 196 | if (lbs_set_regiontable(priv, priv->regioncode, 0)) { |
876c9d3a MT |
197 | ret = -1; |
198 | goto done; | |
199 | } | |
200 | ||
10078321 | 201 | if (lbs_set_universaltable(priv, 0)) { |
876c9d3a MT |
202 | ret = -1; |
203 | goto done; | |
204 | } | |
205 | ||
9012b28a HS |
206 | done: |
207 | lbs_deb_enter_args(LBS_DEB_CMD, "ret %d", ret); | |
876c9d3a MT |
208 | return ret; |
209 | } | |
210 | ||
69f9032d | 211 | static int lbs_ret_802_11_sleep_params(struct lbs_private *priv, |
876c9d3a MT |
212 | struct cmd_ds_command *resp) |
213 | { | |
214 | struct cmd_ds_802_11_sleep_params *sp = &resp->params.sleep_params; | |
876c9d3a | 215 | |
9012b28a | 216 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a | 217 | |
a6c8700f HS |
218 | lbs_deb_cmd("error 0x%x, offset 0x%x, stabletime 0x%x, calcontrol 0x%x " |
219 | "extsleepclk 0x%x\n", le16_to_cpu(sp->error), | |
981f187b DW |
220 | le16_to_cpu(sp->offset), le16_to_cpu(sp->stabletime), |
221 | sp->calcontrol, sp->externalsleepclk); | |
222 | ||
aa21c004 DW |
223 | priv->sp.sp_error = le16_to_cpu(sp->error); |
224 | priv->sp.sp_offset = le16_to_cpu(sp->offset); | |
225 | priv->sp.sp_stabletime = le16_to_cpu(sp->stabletime); | |
226 | priv->sp.sp_calcontrol = sp->calcontrol; | |
227 | priv->sp.sp_extsleepclk = sp->externalsleepclk; | |
228 | priv->sp.sp_reserved = le16_to_cpu(sp->reserved); | |
876c9d3a | 229 | |
9012b28a | 230 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a MT |
231 | return 0; |
232 | } | |
233 | ||
69f9032d | 234 | static int lbs_ret_802_11_stat(struct lbs_private *priv, |
876c9d3a MT |
235 | struct cmd_ds_command *resp) |
236 | { | |
a6c8700f | 237 | lbs_deb_enter(LBS_DEB_CMD); |
aa21c004 | 238 | /* currently priv->wlan802_11Stat is unused |
876c9d3a MT |
239 | |
240 | struct cmd_ds_802_11_get_stat *p11Stat = &resp->params.gstat; | |
876c9d3a MT |
241 | |
242 | // TODO Convert it to Big endian befor copy | |
aa21c004 | 243 | memcpy(&priv->wlan802_11Stat, |
876c9d3a MT |
244 | p11Stat, sizeof(struct cmd_ds_802_11_get_stat)); |
245 | */ | |
a6c8700f | 246 | lbs_deb_leave(LBS_DEB_CMD); |
876c9d3a MT |
247 | return 0; |
248 | } | |
249 | ||
69f9032d | 250 | static int lbs_ret_802_11_snmp_mib(struct lbs_private *priv, |
876c9d3a MT |
251 | struct cmd_ds_command *resp) |
252 | { | |
253 | struct cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; | |
254 | u16 oid = le16_to_cpu(smib->oid); | |
255 | u16 querytype = le16_to_cpu(smib->querytype); | |
256 | ||
9012b28a | 257 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a | 258 | |
a6c8700f | 259 | lbs_deb_cmd("SNMP_RESP: oid 0x%x, querytype 0x%x\n", oid, |
876c9d3a | 260 | querytype); |
a6c8700f | 261 | lbs_deb_cmd("SNMP_RESP: Buf size %d\n", le16_to_cpu(smib->bufsize)); |
876c9d3a | 262 | |
0aef64d7 | 263 | if (querytype == CMD_ACT_GET) { |
876c9d3a | 264 | switch (oid) { |
0aef64d7 | 265 | case FRAGTHRESH_I: |
aa21c004 | 266 | priv->fragthsd = |
981f187b | 267 | le16_to_cpu(*((__le16 *)(smib->value))); |
a6c8700f | 268 | lbs_deb_cmd("SNMP_RESP: frag threshold %u\n", |
aa21c004 | 269 | priv->fragthsd); |
876c9d3a | 270 | break; |
0aef64d7 | 271 | case RTSTHRESH_I: |
aa21c004 | 272 | priv->rtsthsd = |
981f187b | 273 | le16_to_cpu(*((__le16 *)(smib->value))); |
a6c8700f | 274 | lbs_deb_cmd("SNMP_RESP: rts threshold %u\n", |
aa21c004 | 275 | priv->rtsthsd); |
876c9d3a | 276 | break; |
0aef64d7 | 277 | case SHORT_RETRYLIM_I: |
aa21c004 | 278 | priv->txretrycount = |
981f187b | 279 | le16_to_cpu(*((__le16 *)(smib->value))); |
a6c8700f | 280 | lbs_deb_cmd("SNMP_RESP: tx retry count %u\n", |
aa21c004 | 281 | priv->rtsthsd); |
876c9d3a MT |
282 | break; |
283 | default: | |
284 | break; | |
285 | } | |
286 | } | |
287 | ||
9012b28a | 288 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a MT |
289 | return 0; |
290 | } | |
291 | ||
69f9032d | 292 | static int lbs_ret_802_11_key_material(struct lbs_private *priv, |
876c9d3a MT |
293 | struct cmd_ds_command *resp) |
294 | { | |
295 | struct cmd_ds_802_11_key_material *pkeymaterial = | |
296 | &resp->params.keymaterial; | |
876c9d3a MT |
297 | u16 action = le16_to_cpu(pkeymaterial->action); |
298 | ||
9012b28a | 299 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a MT |
300 | |
301 | /* Copy the returned key to driver private data */ | |
0aef64d7 | 302 | if (action == CMD_ACT_GET) { |
876c9d3a MT |
303 | u8 * buf_ptr = (u8 *) &pkeymaterial->keyParamSet; |
304 | u8 * resp_end = (u8 *) (resp + le16_to_cpu(resp->size)); | |
305 | ||
306 | while (buf_ptr < resp_end) { | |
307 | struct MrvlIEtype_keyParamSet * pkeyparamset = | |
308 | (struct MrvlIEtype_keyParamSet *) buf_ptr; | |
1443b653 | 309 | struct enc_key * pkey; |
876c9d3a | 310 | u16 param_set_len = le16_to_cpu(pkeyparamset->length); |
876c9d3a | 311 | u16 key_len = le16_to_cpu(pkeyparamset->keylen); |
1443b653 DW |
312 | u16 key_flags = le16_to_cpu(pkeyparamset->keyinfo); |
313 | u16 key_type = le16_to_cpu(pkeyparamset->keytypeid); | |
314 | u8 * end; | |
876c9d3a MT |
315 | |
316 | end = (u8 *) pkeyparamset + sizeof (pkeyparamset->type) | |
317 | + sizeof (pkeyparamset->length) | |
318 | + param_set_len; | |
319 | /* Make sure we don't access past the end of the IEs */ | |
320 | if (end > resp_end) | |
321 | break; | |
322 | ||
1443b653 | 323 | if (key_flags & KEY_INFO_WPA_UNICAST) |
aa21c004 | 324 | pkey = &priv->wpa_unicast_key; |
1443b653 | 325 | else if (key_flags & KEY_INFO_WPA_MCAST) |
aa21c004 | 326 | pkey = &priv->wpa_mcast_key; |
876c9d3a MT |
327 | else |
328 | break; | |
329 | ||
330 | /* Copy returned key into driver */ | |
1443b653 | 331 | memset(pkey, 0, sizeof(struct enc_key)); |
876c9d3a MT |
332 | if (key_len > sizeof(pkey->key)) |
333 | break; | |
1443b653 DW |
334 | pkey->type = key_type; |
335 | pkey->flags = key_flags; | |
336 | pkey->len = key_len; | |
876c9d3a MT |
337 | memcpy(pkey->key, pkeyparamset->key, pkey->len); |
338 | ||
339 | buf_ptr = end + 1; | |
340 | } | |
341 | } | |
342 | ||
9012b28a | 343 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a MT |
344 | return 0; |
345 | } | |
346 | ||
69f9032d | 347 | static int lbs_ret_802_11_mac_address(struct lbs_private *priv, |
876c9d3a MT |
348 | struct cmd_ds_command *resp) |
349 | { | |
350 | struct cmd_ds_802_11_mac_address *macadd = &resp->params.macadd; | |
876c9d3a | 351 | |
9012b28a | 352 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a | 353 | |
aa21c004 | 354 | memcpy(priv->current_addr, macadd->macadd, ETH_ALEN); |
876c9d3a | 355 | |
9012b28a | 356 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a MT |
357 | return 0; |
358 | } | |
359 | ||
69f9032d | 360 | static int lbs_ret_802_11_rf_tx_power(struct lbs_private *priv, |
876c9d3a MT |
361 | struct cmd_ds_command *resp) |
362 | { | |
363 | struct cmd_ds_802_11_rf_tx_power *rtp = &resp->params.txp; | |
876c9d3a | 364 | |
9012b28a | 365 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a | 366 | |
aa21c004 | 367 | priv->txpowerlevel = le16_to_cpu(rtp->currentlevel); |
876c9d3a | 368 | |
aa21c004 | 369 | lbs_deb_cmd("TX power currently %d\n", priv->txpowerlevel); |
876c9d3a | 370 | |
a6c8700f | 371 | lbs_deb_leave(LBS_DEB_CMD); |
876c9d3a MT |
372 | return 0; |
373 | } | |
374 | ||
69f9032d | 375 | static int lbs_ret_802_11_rate_adapt_rateset(struct lbs_private *priv, |
876c9d3a MT |
376 | struct cmd_ds_command *resp) |
377 | { | |
981f187b | 378 | struct cmd_ds_802_11_rate_adapt_rateset *rates = &resp->params.rateset; |
876c9d3a | 379 | |
9012b28a | 380 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a | 381 | |
0aef64d7 | 382 | if (rates->action == CMD_ACT_GET) { |
aa21c004 DW |
383 | priv->enablehwauto = le16_to_cpu(rates->enablehwauto); |
384 | priv->ratebitmap = le16_to_cpu(rates->bitmap); | |
876c9d3a MT |
385 | } |
386 | ||
a6c8700f | 387 | lbs_deb_leave(LBS_DEB_CMD); |
876c9d3a MT |
388 | return 0; |
389 | } | |
390 | ||
69f9032d | 391 | static int lbs_ret_802_11_data_rate(struct lbs_private *priv, |
876c9d3a MT |
392 | struct cmd_ds_command *resp) |
393 | { | |
394 | struct cmd_ds_802_11_data_rate *pdatarate = &resp->params.drate; | |
876c9d3a | 395 | |
9012b28a | 396 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a | 397 | |
a6c8700f | 398 | lbs_deb_hex(LBS_DEB_CMD, "DATA_RATE_RESP", (u8 *) pdatarate, |
8c512765 | 399 | sizeof(struct cmd_ds_802_11_data_rate)); |
876c9d3a | 400 | |
8c512765 DW |
401 | /* FIXME: get actual rates FW can do if this command actually returns |
402 | * all data rates supported. | |
403 | */ | |
aa21c004 DW |
404 | priv->cur_rate = lbs_fw_index_to_data_rate(pdatarate->rates[0]); |
405 | lbs_deb_cmd("DATA_RATE: current rate 0x%02x\n", priv->cur_rate); | |
876c9d3a | 406 | |
8c512765 | 407 | lbs_deb_leave(LBS_DEB_CMD); |
876c9d3a MT |
408 | return 0; |
409 | } | |
410 | ||
69f9032d | 411 | static int lbs_ret_802_11_rf_channel(struct lbs_private *priv, |
876c9d3a MT |
412 | struct cmd_ds_command *resp) |
413 | { | |
981f187b | 414 | struct cmd_ds_802_11_rf_channel *rfchannel = &resp->params.rfchannel; |
876c9d3a MT |
415 | u16 action = le16_to_cpu(rfchannel->action); |
416 | u16 newchannel = le16_to_cpu(rfchannel->currentchannel); | |
417 | ||
9012b28a | 418 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a | 419 | |
0aef64d7 | 420 | if (action == CMD_OPT_802_11_RF_CHANNEL_GET |
aa21c004 | 421 | && priv->curbssparams.channel != newchannel) { |
a6c8700f | 422 | lbs_deb_cmd("channel switch from %d to %d\n", |
aa21c004 | 423 | priv->curbssparams.channel, newchannel); |
876c9d3a MT |
424 | |
425 | /* Update the channel again */ | |
aa21c004 | 426 | priv->curbssparams.channel = newchannel; |
876c9d3a MT |
427 | } |
428 | ||
9012b28a | 429 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a MT |
430 | return 0; |
431 | } | |
432 | ||
69f9032d | 433 | static int lbs_ret_802_11_rssi(struct lbs_private *priv, |
876c9d3a MT |
434 | struct cmd_ds_command *resp) |
435 | { | |
436 | struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp; | |
876c9d3a | 437 | |
a6c8700f HS |
438 | lbs_deb_enter(LBS_DEB_CMD); |
439 | ||
876c9d3a | 440 | /* store the non average value */ |
aa21c004 DW |
441 | priv->SNR[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->SNR); |
442 | priv->NF[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->noisefloor); | |
876c9d3a | 443 | |
aa21c004 DW |
444 | priv->SNR[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgSNR); |
445 | priv->NF[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgnoisefloor); | |
876c9d3a | 446 | |
aa21c004 DW |
447 | priv->RSSI[TYPE_BEACON][TYPE_NOAVG] = |
448 | CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG], | |
449 | priv->NF[TYPE_BEACON][TYPE_NOAVG]); | |
876c9d3a | 450 | |
aa21c004 DW |
451 | priv->RSSI[TYPE_BEACON][TYPE_AVG] = |
452 | CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE, | |
453 | priv->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE); | |
876c9d3a | 454 | |
a6c8700f | 455 | lbs_deb_cmd("RSSI: beacon %d, avg %d\n", |
aa21c004 DW |
456 | priv->RSSI[TYPE_BEACON][TYPE_NOAVG], |
457 | priv->RSSI[TYPE_BEACON][TYPE_AVG]); | |
876c9d3a | 458 | |
a6c8700f | 459 | lbs_deb_leave(LBS_DEB_CMD); |
876c9d3a MT |
460 | return 0; |
461 | } | |
462 | ||
69f9032d | 463 | static int lbs_ret_802_11_eeprom_access(struct lbs_private *priv, |
876c9d3a MT |
464 | struct cmd_ds_command *resp) |
465 | { | |
10078321 | 466 | struct lbs_ioctl_regrdwr *pbuf; |
aa21c004 | 467 | pbuf = (struct lbs_ioctl_regrdwr *) priv->prdeeprom; |
876c9d3a | 468 | |
a6c8700f | 469 | lbs_deb_enter_args(LBS_DEB_CMD, "len %d", |
876c9d3a MT |
470 | le16_to_cpu(resp->params.rdeeprom.bytecount)); |
471 | if (pbuf->NOB < le16_to_cpu(resp->params.rdeeprom.bytecount)) { | |
472 | pbuf->NOB = 0; | |
a6c8700f | 473 | lbs_deb_cmd("EEPROM read length too big\n"); |
876c9d3a MT |
474 | return -1; |
475 | } | |
476 | pbuf->NOB = le16_to_cpu(resp->params.rdeeprom.bytecount); | |
477 | if (pbuf->NOB > 0) { | |
478 | ||
479 | memcpy(&pbuf->value, (u8 *) & resp->params.rdeeprom.value, | |
480 | le16_to_cpu(resp->params.rdeeprom.bytecount)); | |
a6c8700f | 481 | lbs_deb_hex(LBS_DEB_CMD, "EEPROM", (char *)&pbuf->value, |
876c9d3a MT |
482 | le16_to_cpu(resp->params.rdeeprom.bytecount)); |
483 | } | |
a6c8700f | 484 | lbs_deb_leave(LBS_DEB_CMD); |
876c9d3a MT |
485 | return 0; |
486 | } | |
487 | ||
69f9032d | 488 | static int lbs_ret_get_log(struct lbs_private *priv, |
876c9d3a MT |
489 | struct cmd_ds_command *resp) |
490 | { | |
981f187b | 491 | struct cmd_ds_802_11_get_log *logmessage = &resp->params.glog; |
876c9d3a | 492 | |
9012b28a | 493 | lbs_deb_enter(LBS_DEB_CMD); |
876c9d3a | 494 | |
981f187b | 495 | /* Stored little-endian */ |
aa21c004 | 496 | memcpy(&priv->logmsg, logmessage, sizeof(struct cmd_ds_802_11_get_log)); |
876c9d3a | 497 | |
a6c8700f | 498 | lbs_deb_leave(LBS_DEB_CMD); |
876c9d3a MT |
499 | return 0; |
500 | } | |
501 | ||
69f9032d | 502 | static int lbs_ret_802_11_enable_rsn(struct lbs_private *priv, |
18c96c34 DW |
503 | struct cmd_ds_command *resp) |
504 | { | |
505 | struct cmd_ds_802_11_enable_rsn *enable_rsn = &resp->params.enbrsn; | |
aa21c004 | 506 | u32 * pdata_buf = priv->cur_cmd->pdata_buf; |
18c96c34 DW |
507 | |
508 | lbs_deb_enter(LBS_DEB_CMD); | |
509 | ||
0aef64d7 | 510 | if (enable_rsn->action == cpu_to_le16(CMD_ACT_GET)) { |
18c96c34 DW |
511 | if (pdata_buf) |
512 | *pdata_buf = (u32) le16_to_cpu(enable_rsn->enable); | |
513 | } | |
514 | ||
a6c8700f | 515 | lbs_deb_leave(LBS_DEB_CMD); |
18c96c34 DW |
516 | return 0; |
517 | } | |
518 | ||
96287ac4 BD |
519 | static int lbs_ret_802_11_bcn_ctrl(struct lbs_private * priv, |
520 | struct cmd_ds_command *resp) | |
521 | { | |
522 | struct cmd_ds_802_11_beacon_control *bcn_ctrl = | |
523 | &resp->params.bcn_ctrl; | |
96287ac4 BD |
524 | |
525 | lbs_deb_enter(LBS_DEB_CMD); | |
526 | ||
527 | if (bcn_ctrl->action == CMD_ACT_GET) { | |
aa21c004 DW |
528 | priv->beacon_enable = (u8) le16_to_cpu(bcn_ctrl->beacon_enable); |
529 | priv->beacon_period = le16_to_cpu(bcn_ctrl->beacon_period); | |
96287ac4 BD |
530 | } |
531 | ||
532 | lbs_deb_enter(LBS_DEB_CMD); | |
533 | return 0; | |
534 | } | |
535 | ||
3a188649 HS |
536 | static int lbs_ret_802_11_subscribe_event(struct lbs_private *priv, |
537 | struct cmd_ds_command *resp) | |
538 | { | |
3a188649 HS |
539 | struct cmd_ds_802_11_subscribe_event *cmd_event = |
540 | &resp->params.subscribe_event; | |
541 | struct cmd_ds_802_11_subscribe_event *dst_event = | |
aa21c004 | 542 | priv->cur_cmd->pdata_buf; |
3a188649 HS |
543 | |
544 | lbs_deb_enter(LBS_DEB_CMD); | |
545 | ||
546 | if (dst_event->action == cpu_to_le16(CMD_ACT_GET)) { | |
c2df2efe | 547 | dst_event->events = cmd_event->events; |
3a188649 HS |
548 | memcpy(dst_event->tlv, cmd_event->tlv, sizeof(dst_event->tlv)); |
549 | } | |
550 | ||
551 | lbs_deb_leave(LBS_DEB_CMD); | |
552 | return 0; | |
553 | } | |
554 | ||
876c9d3a MT |
555 | static inline int handle_cmd_response(u16 respcmd, |
556 | struct cmd_ds_command *resp, | |
69f9032d | 557 | struct lbs_private *priv) |
876c9d3a MT |
558 | { |
559 | int ret = 0; | |
560 | unsigned long flags; | |
876c9d3a | 561 | |
a6c8700f HS |
562 | lbs_deb_enter(LBS_DEB_HOST); |
563 | ||
876c9d3a | 564 | switch (respcmd) { |
6b63cd0f HS |
565 | case CMD_RET(CMD_MAC_REG_ACCESS): |
566 | case CMD_RET(CMD_BBP_REG_ACCESS): | |
567 | case CMD_RET(CMD_RF_REG_ACCESS): | |
10078321 | 568 | ret = lbs_ret_reg_access(priv, respcmd, resp); |
876c9d3a MT |
569 | break; |
570 | ||
6b63cd0f | 571 | case CMD_RET(CMD_GET_HW_SPEC): |
10078321 | 572 | ret = lbs_ret_get_hw_spec(priv, resp); |
876c9d3a MT |
573 | break; |
574 | ||
6b63cd0f | 575 | case CMD_RET(CMD_802_11_SCAN): |
10078321 | 576 | ret = lbs_ret_80211_scan(priv, resp); |
876c9d3a MT |
577 | break; |
578 | ||
6b63cd0f | 579 | case CMD_RET(CMD_802_11_GET_LOG): |
10078321 | 580 | ret = lbs_ret_get_log(priv, resp); |
876c9d3a MT |
581 | break; |
582 | ||
0aef64d7 | 583 | case CMD_RET_802_11_ASSOCIATE: |
6b63cd0f HS |
584 | case CMD_RET(CMD_802_11_ASSOCIATE): |
585 | case CMD_RET(CMD_802_11_REASSOCIATE): | |
10078321 | 586 | ret = lbs_ret_80211_associate(priv, resp); |
876c9d3a MT |
587 | break; |
588 | ||
6b63cd0f HS |
589 | case CMD_RET(CMD_802_11_DISASSOCIATE): |
590 | case CMD_RET(CMD_802_11_DEAUTHENTICATE): | |
10078321 | 591 | ret = lbs_ret_80211_disassociate(priv, resp); |
876c9d3a MT |
592 | break; |
593 | ||
6b63cd0f HS |
594 | case CMD_RET(CMD_802_11_AD_HOC_START): |
595 | case CMD_RET(CMD_802_11_AD_HOC_JOIN): | |
10078321 | 596 | ret = lbs_ret_80211_ad_hoc_start(priv, resp); |
876c9d3a MT |
597 | break; |
598 | ||
6b63cd0f | 599 | case CMD_RET(CMD_802_11_GET_STAT): |
10078321 | 600 | ret = lbs_ret_802_11_stat(priv, resp); |
876c9d3a MT |
601 | break; |
602 | ||
6b63cd0f | 603 | case CMD_RET(CMD_802_11_SNMP_MIB): |
10078321 | 604 | ret = lbs_ret_802_11_snmp_mib(priv, resp); |
876c9d3a MT |
605 | break; |
606 | ||
6b63cd0f | 607 | case CMD_RET(CMD_802_11_RF_TX_POWER): |
10078321 | 608 | ret = lbs_ret_802_11_rf_tx_power(priv, resp); |
876c9d3a MT |
609 | break; |
610 | ||
6b63cd0f HS |
611 | case CMD_RET(CMD_802_11_SET_AFC): |
612 | case CMD_RET(CMD_802_11_GET_AFC): | |
aa21c004 DW |
613 | spin_lock_irqsave(&priv->driver_lock, flags); |
614 | memmove(priv->cur_cmd->pdata_buf, &resp->params.afc, | |
876c9d3a | 615 | sizeof(struct cmd_ds_802_11_afc)); |
aa21c004 | 616 | spin_unlock_irqrestore(&priv->driver_lock, flags); |
876c9d3a MT |
617 | |
618 | break; | |
876c9d3a | 619 | |
6b63cd0f HS |
620 | case CMD_RET(CMD_MAC_MULTICAST_ADR): |
621 | case CMD_RET(CMD_MAC_CONTROL): | |
622 | case CMD_RET(CMD_802_11_SET_WEP): | |
623 | case CMD_RET(CMD_802_11_RESET): | |
624 | case CMD_RET(CMD_802_11_AUTHENTICATE): | |
625 | case CMD_RET(CMD_802_11_RADIO_CONTROL): | |
626 | case CMD_RET(CMD_802_11_BEACON_STOP): | |
18c96c34 DW |
627 | break; |
628 | ||
6b63cd0f | 629 | case CMD_RET(CMD_802_11_ENABLE_RSN): |
10078321 | 630 | ret = lbs_ret_802_11_enable_rsn(priv, resp); |
876c9d3a MT |
631 | break; |
632 | ||
6b63cd0f | 633 | case CMD_RET(CMD_802_11_DATA_RATE): |
10078321 | 634 | ret = lbs_ret_802_11_data_rate(priv, resp); |
876c9d3a | 635 | break; |
6b63cd0f | 636 | case CMD_RET(CMD_802_11_RATE_ADAPT_RATESET): |
10078321 | 637 | ret = lbs_ret_802_11_rate_adapt_rateset(priv, resp); |
876c9d3a | 638 | break; |
6b63cd0f | 639 | case CMD_RET(CMD_802_11_RF_CHANNEL): |
10078321 | 640 | ret = lbs_ret_802_11_rf_channel(priv, resp); |
876c9d3a MT |
641 | break; |
642 | ||
6b63cd0f | 643 | case CMD_RET(CMD_802_11_RSSI): |
10078321 | 644 | ret = lbs_ret_802_11_rssi(priv, resp); |
876c9d3a MT |
645 | break; |
646 | ||
6b63cd0f | 647 | case CMD_RET(CMD_802_11_MAC_ADDRESS): |
10078321 | 648 | ret = lbs_ret_802_11_mac_address(priv, resp); |
876c9d3a MT |
649 | break; |
650 | ||
6b63cd0f | 651 | case CMD_RET(CMD_802_11_AD_HOC_STOP): |
10078321 | 652 | ret = lbs_ret_80211_ad_hoc_stop(priv, resp); |
876c9d3a MT |
653 | break; |
654 | ||
6b63cd0f | 655 | case CMD_RET(CMD_802_11_KEY_MATERIAL): |
10078321 | 656 | ret = lbs_ret_802_11_key_material(priv, resp); |
876c9d3a MT |
657 | break; |
658 | ||
6b63cd0f | 659 | case CMD_RET(CMD_802_11_EEPROM_ACCESS): |
10078321 | 660 | ret = lbs_ret_802_11_eeprom_access(priv, resp); |
876c9d3a MT |
661 | break; |
662 | ||
6b63cd0f | 663 | case CMD_RET(CMD_802_11D_DOMAIN_INFO): |
10078321 | 664 | ret = lbs_ret_802_11d_domain_info(priv, resp); |
876c9d3a MT |
665 | break; |
666 | ||
6b63cd0f | 667 | case CMD_RET(CMD_802_11_SLEEP_PARAMS): |
10078321 | 668 | ret = lbs_ret_802_11_sleep_params(priv, resp); |
876c9d3a | 669 | break; |
6b63cd0f | 670 | case CMD_RET(CMD_802_11_INACTIVITY_TIMEOUT): |
aa21c004 DW |
671 | spin_lock_irqsave(&priv->driver_lock, flags); |
672 | *((u16 *) priv->cur_cmd->pdata_buf) = | |
876c9d3a | 673 | le16_to_cpu(resp->params.inactivity_timeout.timeout); |
aa21c004 | 674 | spin_unlock_irqrestore(&priv->driver_lock, flags); |
876c9d3a MT |
675 | break; |
676 | ||
6b63cd0f | 677 | case CMD_RET(CMD_802_11_TPC_CFG): |
aa21c004 DW |
678 | spin_lock_irqsave(&priv->driver_lock, flags); |
679 | memmove(priv->cur_cmd->pdata_buf, &resp->params.tpccfg, | |
876c9d3a | 680 | sizeof(struct cmd_ds_802_11_tpc_cfg)); |
aa21c004 | 681 | spin_unlock_irqrestore(&priv->driver_lock, flags); |
876c9d3a | 682 | break; |
6b63cd0f | 683 | case CMD_RET(CMD_802_11_LED_GPIO_CTRL): |
aa21c004 DW |
684 | spin_lock_irqsave(&priv->driver_lock, flags); |
685 | memmove(priv->cur_cmd->pdata_buf, &resp->params.ledgpio, | |
876c9d3a | 686 | sizeof(struct cmd_ds_802_11_led_ctrl)); |
aa21c004 | 687 | spin_unlock_irqrestore(&priv->driver_lock, flags); |
876c9d3a | 688 | break; |
3a188649 HS |
689 | case CMD_RET(CMD_802_11_SUBSCRIBE_EVENT): |
690 | ret = lbs_ret_802_11_subscribe_event(priv, resp); | |
691 | break; | |
692 | ||
6b63cd0f | 693 | case CMD_RET(CMD_802_11_PWR_CFG): |
aa21c004 DW |
694 | spin_lock_irqsave(&priv->driver_lock, flags); |
695 | memmove(priv->cur_cmd->pdata_buf, &resp->params.pwrcfg, | |
876c9d3a | 696 | sizeof(struct cmd_ds_802_11_pwr_cfg)); |
aa21c004 | 697 | spin_unlock_irqrestore(&priv->driver_lock, flags); |
876c9d3a MT |
698 | |
699 | break; | |
700 | ||
6b63cd0f | 701 | case CMD_RET(CMD_GET_TSF): |
aa21c004 DW |
702 | spin_lock_irqsave(&priv->driver_lock, flags); |
703 | memcpy(priv->cur_cmd->pdata_buf, | |
876c9d3a | 704 | &resp->params.gettsf.tsfvalue, sizeof(u64)); |
aa21c004 | 705 | spin_unlock_irqrestore(&priv->driver_lock, flags); |
876c9d3a | 706 | break; |
6b63cd0f | 707 | case CMD_RET(CMD_BT_ACCESS): |
aa21c004 DW |
708 | spin_lock_irqsave(&priv->driver_lock, flags); |
709 | if (priv->cur_cmd->pdata_buf) | |
710 | memcpy(priv->cur_cmd->pdata_buf, | |
876c9d3a | 711 | &resp->params.bt.addr1, 2 * ETH_ALEN); |
aa21c004 | 712 | spin_unlock_irqrestore(&priv->driver_lock, flags); |
876c9d3a | 713 | break; |
6b63cd0f | 714 | case CMD_RET(CMD_FWT_ACCESS): |
aa21c004 DW |
715 | spin_lock_irqsave(&priv->driver_lock, flags); |
716 | if (priv->cur_cmd->pdata_buf) | |
717 | memcpy(priv->cur_cmd->pdata_buf, &resp->params.fwt, | |
981f187b | 718 | sizeof(resp->params.fwt)); |
aa21c004 | 719 | spin_unlock_irqrestore(&priv->driver_lock, flags); |
876c9d3a | 720 | break; |
6b63cd0f | 721 | case CMD_RET(CMD_MESH_ACCESS): |
aa21c004 DW |
722 | if (priv->cur_cmd->pdata_buf) |
723 | memcpy(priv->cur_cmd->pdata_buf, &resp->params.mesh, | |
876c9d3a MT |
724 | sizeof(resp->params.mesh)); |
725 | break; | |
96287ac4 BD |
726 | case CMD_RET(CMD_802_11_BEACON_CTRL): |
727 | ret = lbs_ret_802_11_bcn_ctrl(priv, resp); | |
728 | break; | |
729 | ||
876c9d3a | 730 | default: |
a6c8700f | 731 | lbs_deb_host("CMD_RESP: unknown cmd response 0x%04x\n", |
981f187b | 732 | resp->command); |
876c9d3a MT |
733 | break; |
734 | } | |
a6c8700f | 735 | lbs_deb_leave(LBS_DEB_HOST); |
876c9d3a MT |
736 | return ret; |
737 | } | |
738 | ||
69f9032d | 739 | int lbs_process_rx_command(struct lbs_private *priv) |
876c9d3a MT |
740 | { |
741 | u16 respcmd; | |
742 | struct cmd_ds_command *resp; | |
876c9d3a MT |
743 | int ret = 0; |
744 | ulong flags; | |
745 | u16 result; | |
746 | ||
a6c8700f | 747 | lbs_deb_enter(LBS_DEB_HOST); |
876c9d3a MT |
748 | |
749 | /* Now we got response from FW, cancel the command timer */ | |
aa21c004 | 750 | del_timer(&priv->command_timer); |
876c9d3a | 751 | |
aa21c004 DW |
752 | mutex_lock(&priv->lock); |
753 | spin_lock_irqsave(&priv->driver_lock, flags); | |
876c9d3a | 754 | |
aa21c004 | 755 | if (!priv->cur_cmd) { |
a6c8700f | 756 | lbs_deb_host("CMD_RESP: cur_cmd is NULL\n"); |
876c9d3a | 757 | ret = -1; |
aa21c004 | 758 | spin_unlock_irqrestore(&priv->driver_lock, flags); |
876c9d3a MT |
759 | goto done; |
760 | } | |
aa21c004 | 761 | resp = (struct cmd_ds_command *)(priv->cur_cmd->bufvirtualaddr); |
876c9d3a | 762 | |
876c9d3a | 763 | respcmd = le16_to_cpu(resp->command); |
876c9d3a MT |
764 | result = le16_to_cpu(resp->result); |
765 | ||
a6c8700f HS |
766 | lbs_deb_host("CMD_RESP: response 0x%04x, size %d, jiffies %lu\n", |
767 | respcmd, priv->upld_len, jiffies); | |
aa21c004 | 768 | lbs_deb_hex(LBS_DEB_HOST, "CMD_RESP", priv->cur_cmd->bufvirtualaddr, |
a6c8700f | 769 | priv->upld_len); |
876c9d3a MT |
770 | |
771 | if (!(respcmd & 0x8000)) { | |
a6c8700f | 772 | lbs_deb_host("invalid response!\n"); |
aa21c004 DW |
773 | priv->cur_cmd_retcode = -1; |
774 | __lbs_cleanup_and_insert_cmd(priv, priv->cur_cmd); | |
775 | priv->cur_cmd = NULL; | |
776 | spin_unlock_irqrestore(&priv->driver_lock, flags); | |
876c9d3a MT |
777 | ret = -1; |
778 | goto done; | |
779 | } | |
780 | ||
781 | /* Store the response code to cur_cmd_retcode. */ | |
aa21c004 | 782 | priv->cur_cmd_retcode = result; |
876c9d3a | 783 | |
6b63cd0f | 784 | if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) { |
981f187b DW |
785 | struct cmd_ds_802_11_ps_mode *psmode = &resp->params.psmode; |
786 | u16 action = le16_to_cpu(psmode->action); | |
876c9d3a | 787 | |
a6c8700f HS |
788 | lbs_deb_host( |
789 | "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n", | |
981f187b | 790 | result, action); |
876c9d3a MT |
791 | |
792 | if (result) { | |
a6c8700f | 793 | lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n", |
981f187b DW |
794 | result); |
795 | /* | |
796 | * We should not re-try enter-ps command in | |
797 | * ad-hoc mode. It takes place in | |
10078321 | 798 | * lbs_execute_next_command(). |
981f187b | 799 | */ |
aa21c004 | 800 | if (priv->mode == IW_MODE_ADHOC && |
0aef64d7 | 801 | action == CMD_SUBCMD_ENTER_PS) |
aa21c004 | 802 | priv->psmode = LBS802_11POWERMODECAM; |
0aef64d7 | 803 | } else if (action == CMD_SUBCMD_ENTER_PS) { |
aa21c004 DW |
804 | priv->needtowakeup = 0; |
805 | priv->psstate = PS_STATE_AWAKE; | |
876c9d3a | 806 | |
a6c8700f | 807 | lbs_deb_host("CMD_RESP: ENTER_PS command response\n"); |
aa21c004 | 808 | if (priv->connect_status != LBS_CONNECTED) { |
876c9d3a MT |
809 | /* |
810 | * When Deauth Event received before Enter_PS command | |
811 | * response, We need to wake up the firmware. | |
812 | */ | |
a6c8700f | 813 | lbs_deb_host( |
10078321 | 814 | "disconnected, invoking lbs_ps_wakeup\n"); |
876c9d3a | 815 | |
aa21c004 DW |
816 | spin_unlock_irqrestore(&priv->driver_lock, flags); |
817 | mutex_unlock(&priv->lock); | |
10078321 | 818 | lbs_ps_wakeup(priv, 0); |
aa21c004 DW |
819 | mutex_lock(&priv->lock); |
820 | spin_lock_irqsave(&priv->driver_lock, flags); | |
876c9d3a | 821 | } |
0aef64d7 | 822 | } else if (action == CMD_SUBCMD_EXIT_PS) { |
aa21c004 DW |
823 | priv->needtowakeup = 0; |
824 | priv->psstate = PS_STATE_FULL_POWER; | |
a6c8700f | 825 | lbs_deb_host("CMD_RESP: EXIT_PS command response\n"); |
876c9d3a | 826 | } else { |
a6c8700f | 827 | lbs_deb_host("CMD_RESP: PS action 0x%X\n", action); |
876c9d3a MT |
828 | } |
829 | ||
aa21c004 DW |
830 | __lbs_cleanup_and_insert_cmd(priv, priv->cur_cmd); |
831 | priv->cur_cmd = NULL; | |
832 | spin_unlock_irqrestore(&priv->driver_lock, flags); | |
876c9d3a MT |
833 | |
834 | ret = 0; | |
835 | goto done; | |
836 | } | |
837 | ||
876c9d3a MT |
838 | /* If the command is not successful, cleanup and return failure */ |
839 | if ((result != 0 || !(respcmd & 0x8000))) { | |
a6c8700f HS |
840 | lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n", |
841 | result, respcmd); | |
876c9d3a MT |
842 | /* |
843 | * Handling errors here | |
844 | */ | |
845 | switch (respcmd) { | |
6b63cd0f HS |
846 | case CMD_RET(CMD_GET_HW_SPEC): |
847 | case CMD_RET(CMD_802_11_RESET): | |
a6c8700f | 848 | lbs_deb_host("CMD_RESP: reset failed\n"); |
876c9d3a MT |
849 | break; |
850 | ||
851 | } | |
852 | ||
aa21c004 DW |
853 | __lbs_cleanup_and_insert_cmd(priv, priv->cur_cmd); |
854 | priv->cur_cmd = NULL; | |
855 | spin_unlock_irqrestore(&priv->driver_lock, flags); | |
876c9d3a MT |
856 | |
857 | ret = -1; | |
858 | goto done; | |
859 | } | |
860 | ||
aa21c004 | 861 | spin_unlock_irqrestore(&priv->driver_lock, flags); |
1723047d | 862 | |
aa21c004 DW |
863 | if (priv->cur_cmd && priv->cur_cmd->callback) |
864 | ret = priv->cur_cmd->callback(respcmd, resp, priv); | |
1723047d | 865 | else |
675787e2 | 866 | ret = handle_cmd_response(respcmd, resp, priv); |
1723047d | 867 | |
aa21c004 | 868 | spin_lock_irqsave(&priv->driver_lock, flags); |
1723047d | 869 | |
aa21c004 | 870 | if (priv->cur_cmd) { |
876c9d3a | 871 | /* Clean up and Put current command back to cmdfreeq */ |
aa21c004 DW |
872 | __lbs_cleanup_and_insert_cmd(priv, priv->cur_cmd); |
873 | priv->cur_cmd = NULL; | |
876c9d3a | 874 | } |
aa21c004 | 875 | spin_unlock_irqrestore(&priv->driver_lock, flags); |
876c9d3a MT |
876 | |
877 | done: | |
aa21c004 | 878 | mutex_unlock(&priv->lock); |
a6c8700f | 879 | lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); |
876c9d3a MT |
880 | return ret; |
881 | } | |
882 | ||
69f9032d | 883 | int lbs_process_event(struct lbs_private *priv) |
876c9d3a MT |
884 | { |
885 | int ret = 0; | |
876c9d3a MT |
886 | u32 eventcause; |
887 | ||
9556d212 HS |
888 | lbs_deb_enter(LBS_DEB_CMD); |
889 | ||
aa21c004 DW |
890 | spin_lock_irq(&priv->driver_lock); |
891 | eventcause = priv->eventcause >> SBI_EVENT_CAUSE_SHIFT; | |
892 | spin_unlock_irq(&priv->driver_lock); | |
876c9d3a | 893 | |
0b3c07ff | 894 | lbs_deb_cmd("event cause %d\n", eventcause); |
876c9d3a | 895 | |
0b3c07ff | 896 | switch (eventcause) { |
876c9d3a | 897 | case MACREG_INT_CODE_LINK_SENSED: |
9012b28a | 898 | lbs_deb_cmd("EVENT: MACREG_INT_CODE_LINK_SENSED\n"); |
876c9d3a MT |
899 | break; |
900 | ||
901 | case MACREG_INT_CODE_DEAUTHENTICATED: | |
a6c8700f | 902 | lbs_deb_cmd("EVENT: deauthenticated\n"); |
10078321 | 903 | lbs_mac_event_disconnected(priv); |
876c9d3a MT |
904 | break; |
905 | ||
906 | case MACREG_INT_CODE_DISASSOCIATED: | |
a6c8700f | 907 | lbs_deb_cmd("EVENT: disassociated\n"); |
10078321 | 908 | lbs_mac_event_disconnected(priv); |
876c9d3a MT |
909 | break; |
910 | ||
0b3c07ff | 911 | case MACREG_INT_CODE_LINK_LOST_NO_SCAN: |
a6c8700f | 912 | lbs_deb_cmd("EVENT: link lost\n"); |
10078321 | 913 | lbs_mac_event_disconnected(priv); |
876c9d3a MT |
914 | break; |
915 | ||
916 | case MACREG_INT_CODE_PS_SLEEP: | |
a6c8700f | 917 | lbs_deb_cmd("EVENT: sleep\n"); |
876c9d3a MT |
918 | |
919 | /* handle unexpected PS SLEEP event */ | |
aa21c004 | 920 | if (priv->psstate == PS_STATE_FULL_POWER) { |
9012b28a | 921 | lbs_deb_cmd( |
a6c8700f | 922 | "EVENT: in FULL POWER mode, ignoreing PS_SLEEP\n"); |
876c9d3a MT |
923 | break; |
924 | } | |
aa21c004 | 925 | priv->psstate = PS_STATE_PRE_SLEEP; |
876c9d3a | 926 | |
aa21c004 | 927 | lbs_ps_confirm_sleep(priv, (u16) priv->psmode); |
876c9d3a MT |
928 | |
929 | break; | |
930 | ||
931 | case MACREG_INT_CODE_PS_AWAKE: | |
a6c8700f | 932 | lbs_deb_cmd("EVENT: awake\n"); |
876c9d3a MT |
933 | |
934 | /* handle unexpected PS AWAKE event */ | |
aa21c004 | 935 | if (priv->psstate == PS_STATE_FULL_POWER) { |
9012b28a | 936 | lbs_deb_cmd( |
876c9d3a MT |
937 | "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); |
938 | break; | |
939 | } | |
940 | ||
aa21c004 | 941 | priv->psstate = PS_STATE_AWAKE; |
876c9d3a | 942 | |
aa21c004 | 943 | if (priv->needtowakeup) { |
876c9d3a MT |
944 | /* |
945 | * wait for the command processing to finish | |
946 | * before resuming sending | |
aa21c004 | 947 | * priv->needtowakeup will be set to FALSE |
10078321 | 948 | * in lbs_ps_wakeup() |
876c9d3a | 949 | */ |
a6c8700f | 950 | lbs_deb_cmd("waking up ...\n"); |
10078321 | 951 | lbs_ps_wakeup(priv, 0); |
876c9d3a MT |
952 | } |
953 | break; | |
954 | ||
955 | case MACREG_INT_CODE_MIC_ERR_UNICAST: | |
9012b28a | 956 | lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n"); |
876c9d3a MT |
957 | handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST); |
958 | break; | |
959 | ||
960 | case MACREG_INT_CODE_MIC_ERR_MULTICAST: | |
9012b28a | 961 | lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n"); |
876c9d3a MT |
962 | handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_MULTICAST); |
963 | break; | |
964 | case MACREG_INT_CODE_MIB_CHANGED: | |
965 | case MACREG_INT_CODE_INIT_DONE: | |
966 | break; | |
967 | ||
968 | case MACREG_INT_CODE_ADHOC_BCN_LOST: | |
a6c8700f | 969 | lbs_deb_cmd("EVENT: ADHOC beacon lost\n"); |
876c9d3a MT |
970 | break; |
971 | ||
972 | case MACREG_INT_CODE_RSSI_LOW: | |
a6c8700f | 973 | lbs_pr_alert("EVENT: rssi low\n"); |
876c9d3a MT |
974 | break; |
975 | case MACREG_INT_CODE_SNR_LOW: | |
a6c8700f | 976 | lbs_pr_alert("EVENT: snr low\n"); |
876c9d3a MT |
977 | break; |
978 | case MACREG_INT_CODE_MAX_FAIL: | |
a6c8700f | 979 | lbs_pr_alert("EVENT: max fail\n"); |
876c9d3a MT |
980 | break; |
981 | case MACREG_INT_CODE_RSSI_HIGH: | |
a6c8700f | 982 | lbs_pr_alert("EVENT: rssi high\n"); |
876c9d3a MT |
983 | break; |
984 | case MACREG_INT_CODE_SNR_HIGH: | |
a6c8700f | 985 | lbs_pr_alert("EVENT: snr high\n"); |
876c9d3a MT |
986 | break; |
987 | ||
7d8d28b3 | 988 | case MACREG_INT_CODE_MESH_AUTO_STARTED: |
5612c014 DW |
989 | /* Ignore spurious autostart events if autostart is disabled */ |
990 | if (!priv->mesh_autostart_enabled) { | |
991 | lbs_pr_info("EVENT: MESH_AUTO_STARTED (ignoring)\n"); | |
992 | break; | |
993 | } | |
9cdc6d29 | 994 | lbs_pr_info("EVENT: MESH_AUTO_STARTED\n"); |
aa21c004 | 995 | priv->mesh_connect_status = LBS_CONNECTED; |
7d8d28b3 | 996 | if (priv->mesh_open == 1) { |
9cdc6d29 LCC |
997 | netif_wake_queue(priv->mesh_dev); |
998 | netif_carrier_on(priv->mesh_dev); | |
7d8d28b3 | 999 | } |
aa21c004 | 1000 | priv->mode = IW_MODE_ADHOC; |
b8bedefd | 1001 | schedule_work(&priv->sync_channel); |
7d8d28b3 LCCR |
1002 | break; |
1003 | ||
876c9d3a | 1004 | default: |
0b3c07ff | 1005 | lbs_pr_alert("EVENT: unknown event id %d\n", eventcause); |
876c9d3a MT |
1006 | break; |
1007 | } | |
1008 | ||
aa21c004 DW |
1009 | spin_lock_irq(&priv->driver_lock); |
1010 | priv->eventcause = 0; | |
1011 | spin_unlock_irq(&priv->driver_lock); | |
9012b28a | 1012 | |
9556d212 | 1013 | lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); |
876c9d3a MT |
1014 | return ret; |
1015 | } |