diff options
Diffstat (limited to 'drivers/net/wireless/libertas/cmd.c')
-rw-r--r-- | drivers/net/wireless/libertas/cmd.c | 1958 |
1 files changed, 1958 insertions, 0 deletions
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c new file mode 100644 index 000000000000..bfdac58b5c06 --- /dev/null +++ b/drivers/net/wireless/libertas/cmd.c | |||
@@ -0,0 +1,1958 @@ | |||
1 | /** | ||
2 | * This file contains the handling of command. | ||
3 | * It prepares command and sends it to firmware when it is ready. | ||
4 | */ | ||
5 | |||
6 | #include <net/iw_handler.h> | ||
7 | #include "host.h" | ||
8 | #include "hostcmd.h" | ||
9 | #include "sbi.h" | ||
10 | #include "decl.h" | ||
11 | #include "defs.h" | ||
12 | #include "dev.h" | ||
13 | #include "join.h" | ||
14 | #include "wext.h" | ||
15 | |||
16 | static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode); | ||
17 | |||
18 | static u16 commands_allowed_in_ps[] = { | ||
19 | cmd_802_11_rssi, | ||
20 | }; | ||
21 | |||
22 | /** | ||
23 | * @brief This function checks if the commans is allowed | ||
24 | * in PS mode not. | ||
25 | * | ||
26 | * @param command the command ID | ||
27 | * @return TRUE or FALSE | ||
28 | */ | ||
29 | static u8 is_command_allowed_in_ps(u16 command) | ||
30 | { | ||
31 | int count = sizeof(commands_allowed_in_ps) | ||
32 | / sizeof(commands_allowed_in_ps[0]); | ||
33 | int i; | ||
34 | |||
35 | for (i = 0; i < count; i++) { | ||
36 | if (command == cpu_to_le16(commands_allowed_in_ps[i])) | ||
37 | return 1; | ||
38 | } | ||
39 | |||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static int wlan_cmd_hw_spec(wlan_private * priv, struct cmd_ds_command *cmd) | ||
44 | { | ||
45 | struct cmd_ds_get_hw_spec *hwspec = &cmd->params.hwspec; | ||
46 | |||
47 | ENTER(); | ||
48 | |||
49 | cmd->command = cpu_to_le16(cmd_get_hw_spec); | ||
50 | cmd->size = | ||
51 | cpu_to_le16(sizeof(struct cmd_ds_get_hw_spec) + S_DS_GEN); | ||
52 | memcpy(hwspec->permanentaddr, priv->adapter->current_addr, ETH_ALEN); | ||
53 | |||
54 | LEAVE(); | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static int wlan_cmd_802_11_ps_mode(wlan_private * priv, | ||
59 | struct cmd_ds_command *cmd, | ||
60 | u16 cmd_action) | ||
61 | { | ||
62 | struct cmd_ds_802_11_ps_mode *psm = &cmd->params.psmode; | ||
63 | u16 action = cmd_action; | ||
64 | wlan_adapter *adapter = priv->adapter; | ||
65 | |||
66 | ENTER(); | ||
67 | |||
68 | cmd->command = cpu_to_le16(cmd_802_11_ps_mode); | ||
69 | cmd->size = | ||
70 | cpu_to_le16(sizeof(struct cmd_ds_802_11_ps_mode) + | ||
71 | S_DS_GEN); | ||
72 | psm->action = cpu_to_le16(cmd_action); | ||
73 | psm->multipledtim = 0; | ||
74 | switch (action) { | ||
75 | case cmd_subcmd_enter_ps: | ||
76 | lbs_pr_debug(1, "PS command:" "SubCode- Enter PS\n"); | ||
77 | lbs_pr_debug(1, "locallisteninterval = %d\n", | ||
78 | adapter->locallisteninterval); | ||
79 | |||
80 | psm->locallisteninterval = | ||
81 | cpu_to_le16(adapter->locallisteninterval); | ||
82 | psm->nullpktinterval = | ||
83 | cpu_to_le16(adapter->nullpktinterval); | ||
84 | psm->multipledtim = | ||
85 | cpu_to_le16(priv->adapter->multipledtim); | ||
86 | break; | ||
87 | |||
88 | case cmd_subcmd_exit_ps: | ||
89 | lbs_pr_debug(1, "PS command:" "SubCode- Exit PS\n"); | ||
90 | break; | ||
91 | |||
92 | case cmd_subcmd_sleep_confirmed: | ||
93 | lbs_pr_debug(1, "PS command: SubCode- sleep confirm\n"); | ||
94 | break; | ||
95 | |||
96 | default: | ||
97 | break; | ||
98 | } | ||
99 | |||
100 | LEAVE(); | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int wlan_cmd_802_11_inactivity_timeout(wlan_private * priv, | ||
105 | struct cmd_ds_command *cmd, | ||
106 | u16 cmd_action, void *pdata_buf) | ||
107 | { | ||
108 | u16 *timeout = pdata_buf; | ||
109 | |||
110 | cmd->command = cpu_to_le16(cmd_802_11_inactivity_timeout); | ||
111 | cmd->size = | ||
112 | cpu_to_le16(sizeof(struct cmd_ds_802_11_inactivity_timeout) | ||
113 | + S_DS_GEN); | ||
114 | |||
115 | cmd->params.inactivity_timeout.action = cpu_to_le16(cmd_action); | ||
116 | |||
117 | if (cmd_action) | ||
118 | cmd->params.inactivity_timeout.timeout = | ||
119 | cpu_to_le16(*timeout); | ||
120 | else | ||
121 | cmd->params.inactivity_timeout.timeout = 0; | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static int wlan_cmd_802_11_sleep_params(wlan_private * priv, | ||
127 | struct cmd_ds_command *cmd, | ||
128 | u16 cmd_action) | ||
129 | { | ||
130 | wlan_adapter *adapter = priv->adapter; | ||
131 | struct cmd_ds_802_11_sleep_params *sp = &cmd->params.sleep_params; | ||
132 | |||
133 | ENTER(); | ||
134 | |||
135 | cmd->size = | ||
136 | cpu_to_le16((sizeof(struct cmd_ds_802_11_sleep_params)) + | ||
137 | S_DS_GEN); | ||
138 | cmd->command = cpu_to_le16(cmd_802_11_sleep_params); | ||
139 | |||
140 | if (cmd_action == cmd_act_get) { | ||
141 | memset(&adapter->sp, 0, sizeof(struct sleep_params)); | ||
142 | memset(sp, 0, sizeof(struct cmd_ds_802_11_sleep_params)); | ||
143 | sp->action = cpu_to_le16(cmd_action); | ||
144 | } else if (cmd_action == cmd_act_set) { | ||
145 | sp->action = cpu_to_le16(cmd_action); | ||
146 | sp->error = cpu_to_le16(adapter->sp.sp_error); | ||
147 | sp->offset = cpu_to_le16(adapter->sp.sp_offset); | ||
148 | sp->stabletime = cpu_to_le16(adapter->sp.sp_stabletime); | ||
149 | sp->calcontrol = (u8) adapter->sp.sp_calcontrol; | ||
150 | sp->externalsleepclk = (u8) adapter->sp.sp_extsleepclk; | ||
151 | sp->reserved = cpu_to_le16(adapter->sp.sp_reserved); | ||
152 | } | ||
153 | |||
154 | LEAVE(); | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static int wlan_cmd_802_11_set_wep(wlan_private * priv, | ||
159 | struct cmd_ds_command *cmd, | ||
160 | u32 cmd_act, | ||
161 | void * pdata_buf) | ||
162 | { | ||
163 | struct cmd_ds_802_11_set_wep *wep = &cmd->params.wep; | ||
164 | wlan_adapter *adapter = priv->adapter; | ||
165 | int ret = 0; | ||
166 | struct assoc_request * assoc_req = pdata_buf; | ||
167 | |||
168 | ENTER(); | ||
169 | |||
170 | cmd->command = cpu_to_le16(cmd_802_11_set_wep); | ||
171 | cmd->size = cpu_to_le16((sizeof(struct cmd_ds_802_11_set_wep)) | ||
172 | + S_DS_GEN); | ||
173 | |||
174 | if (cmd_act == cmd_act_add) { | ||
175 | int i; | ||
176 | |||
177 | if (!assoc_req) { | ||
178 | lbs_pr_debug(1, "Invalid association request!"); | ||
179 | ret = -1; | ||
180 | goto done; | ||
181 | } | ||
182 | |||
183 | wep->action = cpu_to_le16(cmd_act_add); | ||
184 | |||
185 | /* default tx key index */ | ||
186 | wep->keyindex = cpu_to_le16((u16) | ||
187 | (assoc_req->wep_tx_keyidx & | ||
188 | (u32)cmd_WEP_KEY_INDEX_MASK)); | ||
189 | |||
190 | lbs_pr_debug(1, "Tx key Index: %u\n", wep->keyindex); | ||
191 | |||
192 | /* Copy key types and material to host command structure */ | ||
193 | for (i = 0; i < 4; i++) { | ||
194 | struct WLAN_802_11_KEY * pkey = &assoc_req->wep_keys[i]; | ||
195 | |||
196 | switch (pkey->len) { | ||
197 | case KEY_LEN_WEP_40: | ||
198 | wep->keytype[i] = cmd_type_wep_40_bit; | ||
199 | memmove(&wep->keymaterial[i], pkey->key, | ||
200 | pkey->len); | ||
201 | break; | ||
202 | case KEY_LEN_WEP_104: | ||
203 | wep->keytype[i] = cmd_type_wep_104_bit; | ||
204 | memmove(&wep->keymaterial[i], pkey->key, | ||
205 | pkey->len); | ||
206 | break; | ||
207 | case 0: | ||
208 | break; | ||
209 | default: | ||
210 | lbs_pr_debug(1, "Invalid WEP key %d length of %d\n", | ||
211 | i, pkey->len); | ||
212 | ret = -1; | ||
213 | goto done; | ||
214 | break; | ||
215 | } | ||
216 | } | ||
217 | } else if (cmd_act == cmd_act_remove) { | ||
218 | /* ACT_REMOVE clears _all_ WEP keys */ | ||
219 | wep->action = cpu_to_le16(cmd_act_remove); | ||
220 | |||
221 | /* default tx key index */ | ||
222 | wep->keyindex = cpu_to_le16((u16) | ||
223 | (adapter->wep_tx_keyidx & | ||
224 | (u32)cmd_WEP_KEY_INDEX_MASK)); | ||
225 | } | ||
226 | |||
227 | ret = 0; | ||
228 | |||
229 | done: | ||
230 | LEAVE(); | ||
231 | return ret; | ||
232 | } | ||
233 | |||
234 | static int wlan_cmd_802_11_enable_rsn(wlan_private * priv, | ||
235 | struct cmd_ds_command *cmd, | ||
236 | u16 cmd_action) | ||
237 | { | ||
238 | struct cmd_ds_802_11_enable_rsn *penableRSN = &cmd->params.enbrsn; | ||
239 | wlan_adapter *adapter = priv->adapter; | ||
240 | |||
241 | cmd->command = cpu_to_le16(cmd_802_11_enable_rsn); | ||
242 | cmd->size = | ||
243 | cpu_to_le16(sizeof(struct cmd_ds_802_11_enable_rsn) + | ||
244 | S_DS_GEN); | ||
245 | penableRSN->action = cpu_to_le16(cmd_action); | ||
246 | if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { | ||
247 | penableRSN->enable = cpu_to_le16(cmd_enable_rsn); | ||
248 | } else { | ||
249 | penableRSN->enable = cpu_to_le16(cmd_disable_rsn); | ||
250 | } | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | |||
256 | static void set_one_wpa_key(struct MrvlIEtype_keyParamSet * pkeyparamset, | ||
257 | struct WLAN_802_11_KEY * pkey) | ||
258 | { | ||
259 | pkeyparamset->keytypeid = cpu_to_le16(pkey->type); | ||
260 | |||
261 | if (pkey->flags & KEY_INFO_WPA_ENABLED) { | ||
262 | pkeyparamset->keyinfo = cpu_to_le16(KEY_INFO_WPA_ENABLED); | ||
263 | } else { | ||
264 | pkeyparamset->keyinfo = cpu_to_le16(!KEY_INFO_WPA_ENABLED); | ||
265 | } | ||
266 | |||
267 | if (pkey->flags & KEY_INFO_WPA_UNICAST) { | ||
268 | pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST); | ||
269 | } else if (pkey->flags & KEY_INFO_WPA_MCAST) { | ||
270 | pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST); | ||
271 | } | ||
272 | |||
273 | pkeyparamset->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); | ||
274 | pkeyparamset->keylen = cpu_to_le16(pkey->len); | ||
275 | memcpy(pkeyparamset->key, pkey->key, pkey->len); | ||
276 | pkeyparamset->length = cpu_to_le16( sizeof(pkeyparamset->keytypeid) | ||
277 | + sizeof(pkeyparamset->keyinfo) | ||
278 | + sizeof(pkeyparamset->keylen) | ||
279 | + sizeof(pkeyparamset->key)); | ||
280 | } | ||
281 | |||
282 | static int wlan_cmd_802_11_key_material(wlan_private * priv, | ||
283 | struct cmd_ds_command *cmd, | ||
284 | u16 cmd_action, | ||
285 | u32 cmd_oid, void *pdata_buf) | ||
286 | { | ||
287 | wlan_adapter *adapter = priv->adapter; | ||
288 | struct cmd_ds_802_11_key_material *pkeymaterial = | ||
289 | &cmd->params.keymaterial; | ||
290 | int ret = 0; | ||
291 | int index = 0; | ||
292 | |||
293 | ENTER(); | ||
294 | |||
295 | cmd->command = cpu_to_le16(cmd_802_11_key_material); | ||
296 | pkeymaterial->action = cpu_to_le16(cmd_action); | ||
297 | |||
298 | if (cmd_action == cmd_act_get) { | ||
299 | cmd->size = cpu_to_le16( S_DS_GEN | ||
300 | + sizeof (pkeymaterial->action)); | ||
301 | ret = 0; | ||
302 | goto done; | ||
303 | } | ||
304 | |||
305 | memset(&pkeymaterial->keyParamSet, 0, sizeof(pkeymaterial->keyParamSet)); | ||
306 | |||
307 | if (adapter->wpa_unicast_key.len) { | ||
308 | set_one_wpa_key(&pkeymaterial->keyParamSet[index], | ||
309 | &adapter->wpa_unicast_key); | ||
310 | index++; | ||
311 | } | ||
312 | |||
313 | if (adapter->wpa_mcast_key.len) { | ||
314 | set_one_wpa_key(&pkeymaterial->keyParamSet[index], | ||
315 | &adapter->wpa_mcast_key); | ||
316 | index++; | ||
317 | } | ||
318 | |||
319 | cmd->size = cpu_to_le16( S_DS_GEN | ||
320 | + sizeof (pkeymaterial->action) | ||
321 | + index * sizeof(struct MrvlIEtype_keyParamSet)); | ||
322 | |||
323 | ret = 0; | ||
324 | |||
325 | done: | ||
326 | LEAVE(); | ||
327 | return ret; | ||
328 | } | ||
329 | |||
330 | static int wlan_cmd_802_11_reset(wlan_private * priv, | ||
331 | struct cmd_ds_command *cmd, int cmd_action) | ||
332 | { | ||
333 | struct cmd_ds_802_11_reset *reset = &cmd->params.reset; | ||
334 | |||
335 | cmd->command = cpu_to_le16(cmd_802_11_reset); | ||
336 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset) + S_DS_GEN); | ||
337 | reset->action = cpu_to_le16(cmd_action); | ||
338 | |||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | static int wlan_cmd_802_11_get_log(wlan_private * priv, | ||
343 | struct cmd_ds_command *cmd) | ||
344 | { | ||
345 | cmd->command = cpu_to_le16(cmd_802_11_get_log); | ||
346 | cmd->size = | ||
347 | cpu_to_le16(sizeof(struct cmd_ds_802_11_get_log) + S_DS_GEN); | ||
348 | |||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | static int wlan_cmd_802_11_get_stat(wlan_private * priv, | ||
353 | struct cmd_ds_command *cmd) | ||
354 | { | ||
355 | cmd->command = cpu_to_le16(cmd_802_11_get_stat); | ||
356 | cmd->size = | ||
357 | cpu_to_le16(sizeof(struct cmd_ds_802_11_get_stat) + | ||
358 | S_DS_GEN); | ||
359 | |||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | static int wlan_cmd_802_11_snmp_mib(wlan_private * priv, | ||
364 | struct cmd_ds_command *cmd, | ||
365 | int cmd_action, | ||
366 | int cmd_oid, void *pdata_buf) | ||
367 | { | ||
368 | struct cmd_ds_802_11_snmp_mib *pSNMPMIB = &cmd->params.smib; | ||
369 | wlan_adapter *adapter = priv->adapter; | ||
370 | u8 ucTemp; | ||
371 | |||
372 | ENTER(); | ||
373 | |||
374 | lbs_pr_debug(1, "SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); | ||
375 | |||
376 | cmd->command = cpu_to_le16(cmd_802_11_snmp_mib); | ||
377 | cmd->size = | ||
378 | cpu_to_le16(sizeof(struct cmd_ds_802_11_snmp_mib) + | ||
379 | S_DS_GEN); | ||
380 | |||
381 | switch (cmd_oid) { | ||
382 | case OID_802_11_INFRASTRUCTURE_MODE: | ||
383 | { | ||
384 | enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode = | ||
385 | (enum WLAN_802_11_NETWORK_INFRASTRUCTURE) pdata_buf; | ||
386 | pSNMPMIB->querytype = cpu_to_le16(cmd_act_set); | ||
387 | pSNMPMIB->oid = cpu_to_le16((u16) desired_bsstype_i); | ||
388 | pSNMPMIB->bufsize = sizeof(u8); | ||
389 | if (mode == wlan802_11infrastructure) | ||
390 | ucTemp = SNMP_MIB_VALUE_INFRA; | ||
391 | else | ||
392 | ucTemp = SNMP_MIB_VALUE_ADHOC; | ||
393 | |||
394 | memmove(pSNMPMIB->value, &ucTemp, sizeof(u8)); | ||
395 | |||
396 | break; | ||
397 | } | ||
398 | |||
399 | case OID_802_11D_ENABLE: | ||
400 | { | ||
401 | u32 ulTemp; | ||
402 | |||
403 | pSNMPMIB->oid = cpu_to_le16((u16) dot11d_i); | ||
404 | |||
405 | if (cmd_action == cmd_act_set) { | ||
406 | pSNMPMIB->querytype = cmd_act_set; | ||
407 | pSNMPMIB->bufsize = sizeof(u16); | ||
408 | ulTemp = *(u32 *)pdata_buf; | ||
409 | *((unsigned short *)(pSNMPMIB->value)) = | ||
410 | cpu_to_le16((u16) ulTemp); | ||
411 | } | ||
412 | break; | ||
413 | } | ||
414 | |||
415 | case OID_802_11_FRAGMENTATION_THRESHOLD: | ||
416 | { | ||
417 | u32 ulTemp; | ||
418 | |||
419 | pSNMPMIB->oid = cpu_to_le16((u16) fragthresh_i); | ||
420 | |||
421 | if (cmd_action == cmd_act_get) { | ||
422 | pSNMPMIB->querytype = | ||
423 | cpu_to_le16(cmd_act_get); | ||
424 | } else if (cmd_action == cmd_act_set) { | ||
425 | pSNMPMIB->querytype = | ||
426 | cpu_to_le16(cmd_act_set); | ||
427 | pSNMPMIB->bufsize = | ||
428 | cpu_to_le16(sizeof(u16)); | ||
429 | ulTemp = *((u32 *) pdata_buf); | ||
430 | *((unsigned short *)(pSNMPMIB->value)) = | ||
431 | cpu_to_le16((u16) ulTemp); | ||
432 | |||
433 | } | ||
434 | |||
435 | break; | ||
436 | } | ||
437 | |||
438 | case OID_802_11_RTS_THRESHOLD: | ||
439 | { | ||
440 | |||
441 | u32 ulTemp; | ||
442 | pSNMPMIB->oid = le16_to_cpu((u16) rtsthresh_i); | ||
443 | |||
444 | if (cmd_action == cmd_act_get) { | ||
445 | pSNMPMIB->querytype = | ||
446 | cpu_to_le16(cmd_act_get); | ||
447 | } else if (cmd_action == cmd_act_set) { | ||
448 | pSNMPMIB->querytype = | ||
449 | cpu_to_le16(cmd_act_set); | ||
450 | pSNMPMIB->bufsize = | ||
451 | cpu_to_le16(sizeof(u16)); | ||
452 | ulTemp = *((u32 *) | ||
453 | pdata_buf); | ||
454 | *(unsigned short *)(pSNMPMIB->value) = | ||
455 | cpu_to_le16((u16) ulTemp); | ||
456 | |||
457 | } | ||
458 | break; | ||
459 | } | ||
460 | case OID_802_11_TX_RETRYCOUNT: | ||
461 | pSNMPMIB->oid = cpu_to_le16((u16) short_retrylim_i); | ||
462 | |||
463 | if (cmd_action == cmd_act_get) { | ||
464 | pSNMPMIB->querytype = | ||
465 | cpu_to_le16(cmd_act_get); | ||
466 | } else if (cmd_action == cmd_act_set) { | ||
467 | pSNMPMIB->querytype = | ||
468 | cpu_to_le16(cmd_act_set); | ||
469 | pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16)); | ||
470 | *((unsigned short *)(pSNMPMIB->value)) = | ||
471 | cpu_to_le16((u16) adapter->txretrycount); | ||
472 | } | ||
473 | |||
474 | break; | ||
475 | default: | ||
476 | break; | ||
477 | } | ||
478 | |||
479 | lbs_pr_debug(1, | ||
480 | "SNMP_CMD: command=0x%x, size=0x%x, seqnum=0x%x, result=0x%x\n", | ||
481 | cmd->command, cmd->size, cmd->seqnum, cmd->result); | ||
482 | |||
483 | lbs_pr_debug(1, | ||
484 | "SNMP_CMD: action=0x%x, oid=0x%x, oidsize=0x%x, value=0x%x\n", | ||
485 | pSNMPMIB->querytype, pSNMPMIB->oid, pSNMPMIB->bufsize, | ||
486 | *(u16 *) pSNMPMIB->value); | ||
487 | |||
488 | LEAVE(); | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | static int wlan_cmd_802_11_radio_control(wlan_private * priv, | ||
493 | struct cmd_ds_command *cmd, | ||
494 | int cmd_action) | ||
495 | { | ||
496 | wlan_adapter *adapter = priv->adapter; | ||
497 | struct cmd_ds_802_11_radio_control *pradiocontrol = | ||
498 | &cmd->params.radio; | ||
499 | |||
500 | ENTER(); | ||
501 | |||
502 | cmd->size = | ||
503 | cpu_to_le16((sizeof(struct cmd_ds_802_11_radio_control)) + | ||
504 | S_DS_GEN); | ||
505 | cmd->command = cpu_to_le16(cmd_802_11_radio_control); | ||
506 | |||
507 | pradiocontrol->action = cpu_to_le16(cmd_action); | ||
508 | |||
509 | switch (adapter->preamble) { | ||
510 | case cmd_type_short_preamble: | ||
511 | pradiocontrol->control = cpu_to_le16(SET_SHORT_PREAMBLE); | ||
512 | break; | ||
513 | |||
514 | case cmd_type_long_preamble: | ||
515 | pradiocontrol->control = cpu_to_le16(SET_LONG_PREAMBLE); | ||
516 | break; | ||
517 | |||
518 | case cmd_type_auto_preamble: | ||
519 | default: | ||
520 | pradiocontrol->control = cpu_to_le16(SET_AUTO_PREAMBLE); | ||
521 | break; | ||
522 | } | ||
523 | |||
524 | if (adapter->radioon) | ||
525 | pradiocontrol->control |= cpu_to_le16(TURN_ON_RF); | ||
526 | else | ||
527 | pradiocontrol->control &= cpu_to_le16(~TURN_ON_RF); | ||
528 | |||
529 | LEAVE(); | ||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | static int wlan_cmd_802_11_rf_tx_power(wlan_private * priv, | ||
534 | struct cmd_ds_command *cmd, | ||
535 | u16 cmd_action, void *pdata_buf) | ||
536 | { | ||
537 | |||
538 | struct cmd_ds_802_11_rf_tx_power *prtp = &cmd->params.txp; | ||
539 | |||
540 | ENTER(); | ||
541 | |||
542 | cmd->size = | ||
543 | cpu_to_le16((sizeof(struct cmd_ds_802_11_rf_tx_power)) + | ||
544 | S_DS_GEN); | ||
545 | cmd->command = cpu_to_le16(cmd_802_11_rf_tx_power); | ||
546 | prtp->action = cmd_action; | ||
547 | |||
548 | lbs_pr_debug(1, "RF_TX_POWER_CMD: size:%d cmd:0x%x Act:%d\n", cmd->size, | ||
549 | cmd->command, prtp->action); | ||
550 | |||
551 | switch (cmd_action) { | ||
552 | case cmd_act_tx_power_opt_get: | ||
553 | prtp->action = cpu_to_le16(cmd_act_get); | ||
554 | prtp->currentlevel = 0; | ||
555 | break; | ||
556 | |||
557 | case cmd_act_tx_power_opt_set_high: | ||
558 | prtp->action = cpu_to_le16(cmd_act_set); | ||
559 | prtp->currentlevel = | ||
560 | cpu_to_le16(cmd_act_tx_power_index_high); | ||
561 | break; | ||
562 | |||
563 | case cmd_act_tx_power_opt_set_mid: | ||
564 | prtp->action = cpu_to_le16(cmd_act_set); | ||
565 | prtp->currentlevel = | ||
566 | cpu_to_le16(cmd_act_tx_power_index_mid); | ||
567 | break; | ||
568 | |||
569 | case cmd_act_tx_power_opt_set_low: | ||
570 | prtp->action = cpu_to_le16(cmd_act_set); | ||
571 | prtp->currentlevel = cpu_to_le16(*((u16 *) pdata_buf)); | ||
572 | break; | ||
573 | } | ||
574 | LEAVE(); | ||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | static int wlan_cmd_802_11_rf_antenna(wlan_private * priv, | ||
579 | struct cmd_ds_command *cmd, | ||
580 | u16 cmd_action, void *pdata_buf) | ||
581 | { | ||
582 | struct cmd_ds_802_11_rf_antenna *rant = &cmd->params.rant; | ||
583 | |||
584 | cmd->command = cpu_to_le16(cmd_802_11_rf_antenna); | ||
585 | cmd->size = | ||
586 | cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_antenna) + | ||
587 | S_DS_GEN); | ||
588 | |||
589 | rant->action = cpu_to_le16(cmd_action); | ||
590 | if ((cmd_action == cmd_act_set_rx) || | ||
591 | (cmd_action == cmd_act_set_tx)) { | ||
592 | rant->antennamode = | ||
593 | cpu_to_le16((u16) (*(u32 *) pdata_buf)); | ||
594 | } | ||
595 | |||
596 | return 0; | ||
597 | } | ||
598 | |||
599 | static int wlan_cmd_802_11_rate_adapt_rateset(wlan_private * priv, | ||
600 | struct cmd_ds_command *cmd, | ||
601 | u16 cmd_action) | ||
602 | { | ||
603 | struct cmd_ds_802_11_rate_adapt_rateset | ||
604 | *rateadapt = &cmd->params.rateset; | ||
605 | wlan_adapter *adapter = priv->adapter; | ||
606 | |||
607 | cmd->size = | ||
608 | cpu_to_le16(sizeof(struct cmd_ds_802_11_rate_adapt_rateset) | ||
609 | + S_DS_GEN); | ||
610 | cmd->command = cpu_to_le16(cmd_802_11_rate_adapt_rateset); | ||
611 | |||
612 | ENTER(); | ||
613 | |||
614 | rateadapt->action = cmd_action; | ||
615 | rateadapt->enablehwauto = adapter->enablehwauto; | ||
616 | rateadapt->bitmap = adapter->ratebitmap; | ||
617 | |||
618 | LEAVE(); | ||
619 | return 0; | ||
620 | } | ||
621 | |||
622 | static int wlan_cmd_802_11_data_rate(wlan_private * priv, | ||
623 | struct cmd_ds_command *cmd, | ||
624 | u16 cmd_action) | ||
625 | { | ||
626 | struct cmd_ds_802_11_data_rate *pdatarate = &cmd->params.drate; | ||
627 | wlan_adapter *adapter = priv->adapter; | ||
628 | u16 action = cmd_action; | ||
629 | |||
630 | ENTER(); | ||
631 | |||
632 | cmd->size = | ||
633 | cpu_to_le16(sizeof(struct cmd_ds_802_11_data_rate) + | ||
634 | S_DS_GEN); | ||
635 | |||
636 | cmd->command = cpu_to_le16(cmd_802_11_data_rate); | ||
637 | |||
638 | memset(pdatarate, 0, sizeof(struct cmd_ds_802_11_data_rate)); | ||
639 | |||
640 | pdatarate->action = cpu_to_le16(cmd_action); | ||
641 | |||
642 | if (action == cmd_act_set_tx_fix_rate) { | ||
643 | pdatarate->datarate[0] = libertas_data_rate_to_index(adapter->datarate); | ||
644 | lbs_pr_debug(1, "Setting FW for fixed rate 0x%02X\n", | ||
645 | adapter->datarate); | ||
646 | } else if (action == cmd_act_set_tx_auto) { | ||
647 | lbs_pr_debug(1, "Setting FW for AUTO rate\n"); | ||
648 | } | ||
649 | |||
650 | LEAVE(); | ||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | static int wlan_cmd_mac_multicast_adr(wlan_private * priv, | ||
655 | struct cmd_ds_command *cmd, | ||
656 | u16 cmd_action) | ||
657 | { | ||
658 | struct cmd_ds_mac_multicast_adr *pMCastAdr = &cmd->params.madr; | ||
659 | wlan_adapter *adapter = priv->adapter; | ||
660 | |||
661 | cmd->size = | ||
662 | cpu_to_le16(sizeof(struct cmd_ds_mac_multicast_adr) + | ||
663 | S_DS_GEN); | ||
664 | cmd->command = cpu_to_le16(cmd_mac_multicast_adr); | ||
665 | |||
666 | pMCastAdr->action = cpu_to_le16(cmd_action); | ||
667 | pMCastAdr->nr_of_adrs = | ||
668 | cpu_to_le16((u16) adapter->nr_of_multicastmacaddr); | ||
669 | memcpy(pMCastAdr->maclist, adapter->multicastlist, | ||
670 | adapter->nr_of_multicastmacaddr * ETH_ALEN); | ||
671 | |||
672 | return 0; | ||
673 | } | ||
674 | |||
675 | static int wlan_cmd_802_11_rf_channel(wlan_private * priv, | ||
676 | struct cmd_ds_command *cmd, | ||
677 | int option, void *pdata_buf) | ||
678 | { | ||
679 | struct cmd_ds_802_11_rf_channel *rfchan = &cmd->params.rfchannel; | ||
680 | |||
681 | cmd->command = cpu_to_le16(cmd_802_11_rf_channel); | ||
682 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_channel) | ||
683 | + S_DS_GEN); | ||
684 | |||
685 | if (option == cmd_opt_802_11_rf_channel_set) { | ||
686 | rfchan->currentchannel = cpu_to_le16(*((u16 *) pdata_buf)); | ||
687 | } | ||
688 | |||
689 | rfchan->action = cpu_to_le16(option); | ||
690 | |||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | static int wlan_cmd_802_11_rssi(wlan_private * priv, | ||
695 | struct cmd_ds_command *cmd) | ||
696 | { | ||
697 | wlan_adapter *adapter = priv->adapter; | ||
698 | |||
699 | cmd->command = cpu_to_le16(cmd_802_11_rssi); | ||
700 | cmd->size = | ||
701 | cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + S_DS_GEN); | ||
702 | cmd->params.rssi.N = priv->adapter->bcn_avg_factor; | ||
703 | |||
704 | /* reset Beacon SNR/NF/RSSI values */ | ||
705 | adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = 0; | ||
706 | adapter->SNR[TYPE_BEACON][TYPE_AVG] = 0; | ||
707 | adapter->NF[TYPE_BEACON][TYPE_NOAVG] = 0; | ||
708 | adapter->NF[TYPE_BEACON][TYPE_AVG] = 0; | ||
709 | adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0; | ||
710 | adapter->RSSI[TYPE_BEACON][TYPE_AVG] = 0; | ||
711 | |||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | static int wlan_cmd_reg_access(wlan_private * priv, | ||
716 | struct cmd_ds_command *cmdptr, | ||
717 | u8 cmd_action, void *pdata_buf) | ||
718 | { | ||
719 | struct wlan_offset_value *offval; | ||
720 | |||
721 | ENTER(); | ||
722 | |||
723 | offval = (struct wlan_offset_value *)pdata_buf; | ||
724 | |||
725 | switch (cmdptr->command) { | ||
726 | case cmd_mac_reg_access: | ||
727 | { | ||
728 | struct cmd_ds_mac_reg_access *macreg; | ||
729 | |||
730 | cmdptr->size = | ||
731 | cpu_to_le16(sizeof | ||
732 | (struct cmd_ds_mac_reg_access) | ||
733 | + S_DS_GEN); | ||
734 | macreg = | ||
735 | (struct cmd_ds_mac_reg_access *)&cmdptr->params. | ||
736 | macreg; | ||
737 | |||
738 | macreg->action = cpu_to_le16(cmd_action); | ||
739 | macreg->offset = cpu_to_le16((u16) offval->offset); | ||
740 | macreg->value = cpu_to_le32(offval->value); | ||
741 | |||
742 | break; | ||
743 | } | ||
744 | |||
745 | case cmd_bbp_reg_access: | ||
746 | { | ||
747 | struct cmd_ds_bbp_reg_access *bbpreg; | ||
748 | |||
749 | cmdptr->size = | ||
750 | cpu_to_le16(sizeof | ||
751 | (struct cmd_ds_bbp_reg_access) | ||
752 | + S_DS_GEN); | ||
753 | bbpreg = | ||
754 | (struct cmd_ds_bbp_reg_access *)&cmdptr->params. | ||
755 | bbpreg; | ||
756 | |||
757 | bbpreg->action = cpu_to_le16(cmd_action); | ||
758 | bbpreg->offset = cpu_to_le16((u16) offval->offset); | ||
759 | bbpreg->value = (u8) offval->value; | ||
760 | |||
761 | break; | ||
762 | } | ||
763 | |||
764 | case cmd_rf_reg_access: | ||
765 | { | ||
766 | struct cmd_ds_rf_reg_access *rfreg; | ||
767 | |||
768 | cmdptr->size = | ||
769 | cpu_to_le16(sizeof | ||
770 | (struct cmd_ds_rf_reg_access) + | ||
771 | S_DS_GEN); | ||
772 | rfreg = | ||
773 | (struct cmd_ds_rf_reg_access *)&cmdptr->params. | ||
774 | rfreg; | ||
775 | |||
776 | rfreg->action = cpu_to_le16(cmd_action); | ||
777 | rfreg->offset = cpu_to_le16((u16) offval->offset); | ||
778 | rfreg->value = (u8) offval->value; | ||
779 | |||
780 | break; | ||
781 | } | ||
782 | |||
783 | default: | ||
784 | break; | ||
785 | } | ||
786 | |||
787 | LEAVE(); | ||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | static int wlan_cmd_802_11_mac_address(wlan_private * priv, | ||
792 | struct cmd_ds_command *cmd, | ||
793 | u16 cmd_action) | ||
794 | { | ||
795 | wlan_adapter *adapter = priv->adapter; | ||
796 | |||
797 | cmd->command = cpu_to_le16(cmd_802_11_mac_address); | ||
798 | cmd->size = | ||
799 | cpu_to_le16(sizeof(struct cmd_ds_802_11_mac_address) + | ||
800 | S_DS_GEN); | ||
801 | cmd->result = 0; | ||
802 | |||
803 | cmd->params.macadd.action = cpu_to_le16(cmd_action); | ||
804 | |||
805 | if (cmd_action == cmd_act_set) { | ||
806 | memcpy(cmd->params.macadd.macadd, | ||
807 | adapter->current_addr, ETH_ALEN); | ||
808 | lbs_dbg_hex("SET_CMD: MAC ADDRESS-", adapter->current_addr, 6); | ||
809 | } | ||
810 | |||
811 | return 0; | ||
812 | } | ||
813 | |||
814 | static int wlan_cmd_802_11_eeprom_access(wlan_private * priv, | ||
815 | struct cmd_ds_command *cmd, | ||
816 | int cmd_action, void *pdata_buf) | ||
817 | { | ||
818 | struct wlan_ioctl_regrdwr *ea = pdata_buf; | ||
819 | |||
820 | ENTER(); | ||
821 | |||
822 | cmd->command = cpu_to_le16(cmd_802_11_eeprom_access); | ||
823 | cmd->size = | ||
824 | cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) + | ||
825 | S_DS_GEN); | ||
826 | cmd->result = 0; | ||
827 | |||
828 | cmd->params.rdeeprom.action = cpu_to_le16(ea->action); | ||
829 | cmd->params.rdeeprom.offset = cpu_to_le16(ea->offset); | ||
830 | cmd->params.rdeeprom.bytecount = cpu_to_le16(ea->NOB); | ||
831 | cmd->params.rdeeprom.value = 0; | ||
832 | |||
833 | return 0; | ||
834 | } | ||
835 | |||
836 | static int wlan_cmd_bt_access(wlan_private * priv, | ||
837 | struct cmd_ds_command *cmd, | ||
838 | u16 cmd_action, void *pdata_buf) | ||
839 | { | ||
840 | struct cmd_ds_bt_access *bt_access = &cmd->params.bt; | ||
841 | lbs_pr_debug(1, "BT CMD(%d)\n", cmd_action); | ||
842 | |||
843 | cmd->command = cpu_to_le16(cmd_bt_access); | ||
844 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_bt_access) | ||
845 | + S_DS_GEN); | ||
846 | cmd->result = 0; | ||
847 | bt_access->action = cpu_to_le16(cmd_action); | ||
848 | |||
849 | switch (cmd_action) { | ||
850 | case cmd_act_bt_access_add: | ||
851 | memcpy(bt_access->addr1, pdata_buf, 2 * ETH_ALEN); | ||
852 | lbs_dbg_hex("BT_ADD: blinded mac address-", bt_access->addr1, 6); | ||
853 | break; | ||
854 | case cmd_act_bt_access_del: | ||
855 | memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN); | ||
856 | lbs_dbg_hex("BT_DEL: blinded mac address-", bt_access->addr1, 6); | ||
857 | break; | ||
858 | case cmd_act_bt_access_list: | ||
859 | bt_access->id = cpu_to_le32(*(u32 *) pdata_buf); | ||
860 | break; | ||
861 | case cmd_act_bt_access_reset: | ||
862 | break; | ||
863 | default: | ||
864 | break; | ||
865 | } | ||
866 | return 0; | ||
867 | } | ||
868 | |||
869 | static int wlan_cmd_fwt_access(wlan_private * priv, | ||
870 | struct cmd_ds_command *cmd, | ||
871 | u16 cmd_action, void *pdata_buf) | ||
872 | { | ||
873 | struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt; | ||
874 | lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action); | ||
875 | |||
876 | cmd->command = cpu_to_le16(cmd_fwt_access); | ||
877 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access) | ||
878 | + S_DS_GEN); | ||
879 | cmd->result = 0; | ||
880 | |||
881 | if (pdata_buf) | ||
882 | memcpy(fwt_access, pdata_buf, sizeof(*fwt_access)); | ||
883 | else | ||
884 | memset(fwt_access, 0, sizeof(*fwt_access)); | ||
885 | |||
886 | fwt_access->action = cpu_to_le16(cmd_action); | ||
887 | |||
888 | return 0; | ||
889 | } | ||
890 | |||
891 | static int wlan_cmd_mesh_access(wlan_private * priv, | ||
892 | struct cmd_ds_command *cmd, | ||
893 | u16 cmd_action, void *pdata_buf) | ||
894 | { | ||
895 | struct cmd_ds_mesh_access *mesh_access = &cmd->params.mesh; | ||
896 | lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action); | ||
897 | |||
898 | cmd->command = cpu_to_le16(cmd_mesh_access); | ||
899 | cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mesh_access) | ||
900 | + S_DS_GEN); | ||
901 | cmd->result = 0; | ||
902 | |||
903 | if (pdata_buf) | ||
904 | memcpy(mesh_access, pdata_buf, sizeof(*mesh_access)); | ||
905 | else | ||
906 | memset(mesh_access, 0, sizeof(*mesh_access)); | ||
907 | |||
908 | mesh_access->action = cpu_to_le16(cmd_action); | ||
909 | |||
910 | return 0; | ||
911 | } | ||
912 | |||
913 | void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail) | ||
914 | { | ||
915 | unsigned long flags; | ||
916 | struct cmd_ds_command *cmdptr; | ||
917 | |||
918 | ENTER(); | ||
919 | |||
920 | if (!cmdnode) { | ||
921 | lbs_pr_debug(1, "QUEUE_CMD: cmdnode is NULL\n"); | ||
922 | goto done; | ||
923 | } | ||
924 | |||
925 | cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; | ||
926 | if (!cmdptr) { | ||
927 | lbs_pr_debug(1, "QUEUE_CMD: cmdptr is NULL\n"); | ||
928 | goto done; | ||
929 | } | ||
930 | |||
931 | /* Exit_PS command needs to be queued in the header always. */ | ||
932 | if (cmdptr->command == cmd_802_11_ps_mode) { | ||
933 | struct cmd_ds_802_11_ps_mode *psm = &cmdptr->params.psmode; | ||
934 | if (psm->action == cmd_subcmd_exit_ps) { | ||
935 | if (adapter->psstate != PS_STATE_FULL_POWER) | ||
936 | addtail = 0; | ||
937 | } | ||
938 | } | ||
939 | |||
940 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
941 | |||
942 | if (addtail) | ||
943 | list_add_tail((struct list_head *)cmdnode, | ||
944 | &adapter->cmdpendingq); | ||
945 | else | ||
946 | list_add((struct list_head *)cmdnode, &adapter->cmdpendingq); | ||
947 | |||
948 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
949 | |||
950 | lbs_pr_debug(1, "QUEUE_CMD: Inserted node=0x%x, cmd=0x%x in cmdpendingq\n", | ||
951 | (u32) cmdnode, | ||
952 | ((struct cmd_ds_gen*)cmdnode->bufvirtualaddr)->command); | ||
953 | |||
954 | done: | ||
955 | LEAVE(); | ||
956 | return; | ||
957 | } | ||
958 | |||
959 | /* | ||
960 | * TODO: Fix the issue when DownloadcommandToStation is being called the | ||
961 | * second time when the command timesout. All the cmdptr->xxx are in little | ||
962 | * endian and therefore all the comparissions will fail. | ||
963 | * For now - we are not performing the endian conversion the second time - but | ||
964 | * for PS and DEEP_SLEEP we need to worry | ||
965 | */ | ||
966 | static int DownloadcommandToStation(wlan_private * priv, | ||
967 | struct cmd_ctrl_node *cmdnode) | ||
968 | { | ||
969 | unsigned long flags; | ||
970 | struct cmd_ds_command *cmdptr; | ||
971 | wlan_adapter *adapter = priv->adapter; | ||
972 | int ret = 0; | ||
973 | u16 cmdsize; | ||
974 | u16 command; | ||
975 | |||
976 | ENTER(); | ||
977 | |||
978 | if (!adapter || !cmdnode) { | ||
979 | lbs_pr_debug(1, "DNLD_CMD: adapter = %#x, cmdnode = %#x\n", | ||
980 | (int)adapter, (int)cmdnode); | ||
981 | if (cmdnode) { | ||
982 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
983 | __libertas_cleanup_and_insert_cmd(priv, cmdnode); | ||
984 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
985 | } | ||
986 | ret = -1; | ||
987 | goto done; | ||
988 | } | ||
989 | |||
990 | cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; | ||
991 | |||
992 | |||
993 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
994 | if (!cmdptr || !cmdptr->size) { | ||
995 | lbs_pr_debug(1, "DNLD_CMD: cmdptr is Null or cmd size is Zero, " | ||
996 | "Not sending\n"); | ||
997 | __libertas_cleanup_and_insert_cmd(priv, cmdnode); | ||
998 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
999 | ret = -1; | ||
1000 | goto done; | ||
1001 | } | ||
1002 | |||
1003 | adapter->cur_cmd = cmdnode; | ||
1004 | adapter->cur_cmd_retcode = 0; | ||
1005 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1006 | lbs_pr_debug(1, "DNLD_CMD:: Before download, size of cmd = %d\n", | ||
1007 | cmdptr->size); | ||
1008 | |||
1009 | cmdsize = cmdptr->size; | ||
1010 | |||
1011 | command = cpu_to_le16(cmdptr->command); | ||
1012 | |||
1013 | cmdnode->cmdwaitqwoken = 0; | ||
1014 | cmdsize = cpu_to_le16(cmdsize); | ||
1015 | |||
1016 | ret = libertas_sbi_host_to_card(priv, MVMS_CMD, (u8 *) cmdptr, cmdsize); | ||
1017 | |||
1018 | if (ret != 0) { | ||
1019 | lbs_pr_debug(1, "DNLD_CMD: Host to Card failed\n"); | ||
1020 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1021 | __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); | ||
1022 | adapter->cur_cmd = NULL; | ||
1023 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1024 | ret = -1; | ||
1025 | goto done; | ||
1026 | } | ||
1027 | |||
1028 | lbs_pr_debug(1, "DNLD_CMD: Sent command 0x%x @ %lu\n", command, jiffies); | ||
1029 | lbs_dbg_hex("DNLD_CMD: command", cmdnode->bufvirtualaddr, cmdsize); | ||
1030 | |||
1031 | /* Setup the timer after transmit command */ | ||
1032 | if (command == cmd_802_11_scan | ||
1033 | || command == cmd_802_11_authenticate | ||
1034 | || command == cmd_802_11_associate) | ||
1035 | mod_timer(&adapter->command_timer, jiffies + (10*HZ)); | ||
1036 | else | ||
1037 | mod_timer(&adapter->command_timer, jiffies + (5*HZ)); | ||
1038 | |||
1039 | ret = 0; | ||
1040 | |||
1041 | done: | ||
1042 | LEAVE(); | ||
1043 | return ret; | ||
1044 | } | ||
1045 | |||
1046 | static int wlan_cmd_mac_control(wlan_private * priv, | ||
1047 | struct cmd_ds_command *cmd) | ||
1048 | { | ||
1049 | struct cmd_ds_mac_control *mac = &cmd->params.macctrl; | ||
1050 | |||
1051 | ENTER(); | ||
1052 | |||
1053 | cmd->command = cpu_to_le16(cmd_mac_control); | ||
1054 | cmd->size = | ||
1055 | cpu_to_le16(sizeof(struct cmd_ds_mac_control) + S_DS_GEN); | ||
1056 | mac->action = cpu_to_le16(priv->adapter->currentpacketfilter); | ||
1057 | |||
1058 | lbs_pr_debug(1, "wlan_cmd_mac_control(): action=0x%X size=%d\n", | ||
1059 | mac->action, cmd->size); | ||
1060 | |||
1061 | LEAVE(); | ||
1062 | return 0; | ||
1063 | } | ||
1064 | |||
1065 | /** | ||
1066 | * This function inserts command node to cmdfreeq | ||
1067 | * after cleans it. Requires adapter->driver_lock held. | ||
1068 | */ | ||
1069 | void __libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd) | ||
1070 | { | ||
1071 | wlan_adapter *adapter = priv->adapter; | ||
1072 | |||
1073 | if (!ptempcmd) | ||
1074 | goto done; | ||
1075 | |||
1076 | cleanup_cmdnode(ptempcmd); | ||
1077 | list_add_tail((struct list_head *)ptempcmd, &adapter->cmdfreeq); | ||
1078 | done: | ||
1079 | return; | ||
1080 | } | ||
1081 | |||
1082 | void libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd) | ||
1083 | { | ||
1084 | unsigned long flags; | ||
1085 | |||
1086 | spin_lock_irqsave(&priv->adapter->driver_lock, flags); | ||
1087 | __libertas_cleanup_and_insert_cmd(priv, ptempcmd); | ||
1088 | spin_unlock_irqrestore(&priv->adapter->driver_lock, flags); | ||
1089 | } | ||
1090 | |||
1091 | int libertas_set_radio_control(wlan_private * priv) | ||
1092 | { | ||
1093 | int ret = 0; | ||
1094 | |||
1095 | ENTER(); | ||
1096 | |||
1097 | ret = libertas_prepare_and_send_command(priv, | ||
1098 | cmd_802_11_radio_control, | ||
1099 | cmd_act_set, | ||
1100 | cmd_option_waitforrsp, 0, NULL); | ||
1101 | |||
1102 | lbs_pr_debug(1, "RADIO_SET: on or off: 0x%X, preamble = 0x%X\n", | ||
1103 | priv->adapter->radioon, priv->adapter->preamble); | ||
1104 | |||
1105 | LEAVE(); | ||
1106 | return ret; | ||
1107 | } | ||
1108 | |||
1109 | int libertas_set_mac_packet_filter(wlan_private * priv) | ||
1110 | { | ||
1111 | int ret = 0; | ||
1112 | |||
1113 | ENTER(); | ||
1114 | |||
1115 | lbs_pr_debug(1, "libertas_set_mac_packet_filter value = %x\n", | ||
1116 | priv->adapter->currentpacketfilter); | ||
1117 | |||
1118 | /* Send MAC control command to station */ | ||
1119 | ret = libertas_prepare_and_send_command(priv, | ||
1120 | cmd_mac_control, 0, 0, 0, NULL); | ||
1121 | |||
1122 | LEAVE(); | ||
1123 | return ret; | ||
1124 | } | ||
1125 | |||
1126 | /** | ||
1127 | * @brief This function prepare the command before send to firmware. | ||
1128 | * | ||
1129 | * @param priv A pointer to wlan_private structure | ||
1130 | * @param cmd_no command number | ||
1131 | * @param cmd_action command action: GET or SET | ||
1132 | * @param wait_option wait option: wait response or not | ||
1133 | * @param cmd_oid cmd oid: treated as sub command | ||
1134 | * @param pdata_buf A pointer to informaion buffer | ||
1135 | * @return 0 or -1 | ||
1136 | */ | ||
1137 | int libertas_prepare_and_send_command(wlan_private * priv, | ||
1138 | u16 cmd_no, | ||
1139 | u16 cmd_action, | ||
1140 | u16 wait_option, u32 cmd_oid, void *pdata_buf) | ||
1141 | { | ||
1142 | int ret = 0; | ||
1143 | wlan_adapter *adapter = priv->adapter; | ||
1144 | struct cmd_ctrl_node *cmdnode; | ||
1145 | struct cmd_ds_command *cmdptr; | ||
1146 | unsigned long flags; | ||
1147 | |||
1148 | ENTER(); | ||
1149 | |||
1150 | if (!adapter) { | ||
1151 | lbs_pr_debug(1, "PREP_CMD: adapter is Null\n"); | ||
1152 | ret = -1; | ||
1153 | goto done; | ||
1154 | } | ||
1155 | |||
1156 | if (adapter->surpriseremoved) { | ||
1157 | lbs_pr_debug(1, "PREP_CMD: Card is Removed\n"); | ||
1158 | ret = -1; | ||
1159 | goto done; | ||
1160 | } | ||
1161 | |||
1162 | cmdnode = libertas_get_free_cmd_ctrl_node(priv); | ||
1163 | |||
1164 | if (cmdnode == NULL) { | ||
1165 | lbs_pr_debug(1, "PREP_CMD: No free cmdnode\n"); | ||
1166 | |||
1167 | /* Wake up main thread to execute next command */ | ||
1168 | wake_up_interruptible(&priv->mainthread.waitq); | ||
1169 | ret = -1; | ||
1170 | goto done; | ||
1171 | } | ||
1172 | |||
1173 | libertas_set_cmd_ctrl_node(priv, cmdnode, cmd_oid, wait_option, pdata_buf); | ||
1174 | |||
1175 | cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; | ||
1176 | |||
1177 | lbs_pr_debug(1, "PREP_CMD: Val of cmd ptr =0x%x, command=0x%X\n", | ||
1178 | (u32) cmdptr, cmd_no); | ||
1179 | |||
1180 | if (!cmdptr) { | ||
1181 | lbs_pr_debug(1, "PREP_CMD: bufvirtualaddr of cmdnode is NULL\n"); | ||
1182 | libertas_cleanup_and_insert_cmd(priv, cmdnode); | ||
1183 | ret = -1; | ||
1184 | goto done; | ||
1185 | } | ||
1186 | |||
1187 | /* Set sequence number, command and INT option */ | ||
1188 | adapter->seqnum++; | ||
1189 | cmdptr->seqnum = cpu_to_le16(adapter->seqnum); | ||
1190 | |||
1191 | cmdptr->command = cmd_no; | ||
1192 | cmdptr->result = 0; | ||
1193 | |||
1194 | switch (cmd_no) { | ||
1195 | case cmd_get_hw_spec: | ||
1196 | ret = wlan_cmd_hw_spec(priv, cmdptr); | ||
1197 | break; | ||
1198 | case cmd_802_11_ps_mode: | ||
1199 | ret = wlan_cmd_802_11_ps_mode(priv, cmdptr, cmd_action); | ||
1200 | break; | ||
1201 | |||
1202 | case cmd_802_11_scan: | ||
1203 | ret = libertas_cmd_80211_scan(priv, cmdptr, pdata_buf); | ||
1204 | break; | ||
1205 | |||
1206 | case cmd_mac_control: | ||
1207 | ret = wlan_cmd_mac_control(priv, cmdptr); | ||
1208 | break; | ||
1209 | |||
1210 | case cmd_802_11_associate: | ||
1211 | case cmd_802_11_reassociate: | ||
1212 | ret = libertas_cmd_80211_associate(priv, cmdptr, pdata_buf); | ||
1213 | break; | ||
1214 | |||
1215 | case cmd_802_11_deauthenticate: | ||
1216 | ret = libertas_cmd_80211_deauthenticate(priv, cmdptr); | ||
1217 | break; | ||
1218 | |||
1219 | case cmd_802_11_set_wep: | ||
1220 | ret = wlan_cmd_802_11_set_wep(priv, cmdptr, cmd_action, pdata_buf); | ||
1221 | break; | ||
1222 | |||
1223 | case cmd_802_11_ad_hoc_start: | ||
1224 | ret = libertas_cmd_80211_ad_hoc_start(priv, cmdptr, pdata_buf); | ||
1225 | break; | ||
1226 | case cmd_code_dnld: | ||
1227 | break; | ||
1228 | |||
1229 | case cmd_802_11_reset: | ||
1230 | ret = wlan_cmd_802_11_reset(priv, cmdptr, cmd_action); | ||
1231 | break; | ||
1232 | |||
1233 | case cmd_802_11_get_log: | ||
1234 | ret = wlan_cmd_802_11_get_log(priv, cmdptr); | ||
1235 | break; | ||
1236 | |||
1237 | case cmd_802_11_authenticate: | ||
1238 | ret = libertas_cmd_80211_authenticate(priv, cmdptr, pdata_buf); | ||
1239 | break; | ||
1240 | |||
1241 | case cmd_802_11_get_stat: | ||
1242 | ret = wlan_cmd_802_11_get_stat(priv, cmdptr); | ||
1243 | break; | ||
1244 | |||
1245 | case cmd_802_11_snmp_mib: | ||
1246 | ret = wlan_cmd_802_11_snmp_mib(priv, cmdptr, | ||
1247 | cmd_action, cmd_oid, pdata_buf); | ||
1248 | break; | ||
1249 | |||
1250 | case cmd_mac_reg_access: | ||
1251 | case cmd_bbp_reg_access: | ||
1252 | case cmd_rf_reg_access: | ||
1253 | ret = wlan_cmd_reg_access(priv, cmdptr, cmd_action, pdata_buf); | ||
1254 | break; | ||
1255 | |||
1256 | case cmd_802_11_rf_channel: | ||
1257 | ret = wlan_cmd_802_11_rf_channel(priv, cmdptr, | ||
1258 | cmd_action, pdata_buf); | ||
1259 | break; | ||
1260 | |||
1261 | case cmd_802_11_rf_tx_power: | ||
1262 | ret = wlan_cmd_802_11_rf_tx_power(priv, cmdptr, | ||
1263 | cmd_action, pdata_buf); | ||
1264 | break; | ||
1265 | |||
1266 | case cmd_802_11_radio_control: | ||
1267 | ret = wlan_cmd_802_11_radio_control(priv, cmdptr, cmd_action); | ||
1268 | break; | ||
1269 | |||
1270 | case cmd_802_11_rf_antenna: | ||
1271 | ret = wlan_cmd_802_11_rf_antenna(priv, cmdptr, | ||
1272 | cmd_action, pdata_buf); | ||
1273 | break; | ||
1274 | |||
1275 | case cmd_802_11_data_rate: | ||
1276 | ret = wlan_cmd_802_11_data_rate(priv, cmdptr, cmd_action); | ||
1277 | break; | ||
1278 | case cmd_802_11_rate_adapt_rateset: | ||
1279 | ret = wlan_cmd_802_11_rate_adapt_rateset(priv, | ||
1280 | cmdptr, cmd_action); | ||
1281 | break; | ||
1282 | |||
1283 | case cmd_mac_multicast_adr: | ||
1284 | ret = wlan_cmd_mac_multicast_adr(priv, cmdptr, cmd_action); | ||
1285 | break; | ||
1286 | |||
1287 | case cmd_802_11_ad_hoc_join: | ||
1288 | ret = libertas_cmd_80211_ad_hoc_join(priv, cmdptr, pdata_buf); | ||
1289 | break; | ||
1290 | |||
1291 | case cmd_802_11_rssi: | ||
1292 | ret = wlan_cmd_802_11_rssi(priv, cmdptr); | ||
1293 | break; | ||
1294 | |||
1295 | case cmd_802_11_ad_hoc_stop: | ||
1296 | ret = libertas_cmd_80211_ad_hoc_stop(priv, cmdptr); | ||
1297 | break; | ||
1298 | |||
1299 | case cmd_802_11_enable_rsn: | ||
1300 | ret = wlan_cmd_802_11_enable_rsn(priv, cmdptr, cmd_action); | ||
1301 | break; | ||
1302 | |||
1303 | case cmd_802_11_key_material: | ||
1304 | ret = wlan_cmd_802_11_key_material(priv, cmdptr, | ||
1305 | cmd_action, cmd_oid, | ||
1306 | pdata_buf); | ||
1307 | break; | ||
1308 | |||
1309 | case cmd_802_11_pairwise_tsc: | ||
1310 | break; | ||
1311 | case cmd_802_11_group_tsc: | ||
1312 | break; | ||
1313 | |||
1314 | case cmd_802_11_mac_address: | ||
1315 | ret = wlan_cmd_802_11_mac_address(priv, cmdptr, cmd_action); | ||
1316 | break; | ||
1317 | |||
1318 | case cmd_802_11_eeprom_access: | ||
1319 | ret = wlan_cmd_802_11_eeprom_access(priv, cmdptr, | ||
1320 | cmd_action, pdata_buf); | ||
1321 | break; | ||
1322 | |||
1323 | case cmd_802_11_set_afc: | ||
1324 | case cmd_802_11_get_afc: | ||
1325 | |||
1326 | cmdptr->command = cpu_to_le16(cmd_no); | ||
1327 | cmdptr->size = | ||
1328 | cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) + | ||
1329 | S_DS_GEN); | ||
1330 | |||
1331 | memmove(&cmdptr->params.afc, | ||
1332 | pdata_buf, sizeof(struct cmd_ds_802_11_afc)); | ||
1333 | |||
1334 | ret = 0; | ||
1335 | goto done; | ||
1336 | |||
1337 | case cmd_802_11d_domain_info: | ||
1338 | ret = libertas_cmd_802_11d_domain_info(priv, cmdptr, | ||
1339 | cmd_no, cmd_action); | ||
1340 | break; | ||
1341 | |||
1342 | case cmd_802_11_sleep_params: | ||
1343 | ret = wlan_cmd_802_11_sleep_params(priv, cmdptr, cmd_action); | ||
1344 | break; | ||
1345 | case cmd_802_11_inactivity_timeout: | ||
1346 | ret = wlan_cmd_802_11_inactivity_timeout(priv, cmdptr, | ||
1347 | cmd_action, pdata_buf); | ||
1348 | libertas_set_cmd_ctrl_node(priv, cmdnode, 0, 0, pdata_buf); | ||
1349 | break; | ||
1350 | |||
1351 | case cmd_802_11_tpc_cfg: | ||
1352 | cmdptr->command = cpu_to_le16(cmd_802_11_tpc_cfg); | ||
1353 | cmdptr->size = | ||
1354 | cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) + | ||
1355 | S_DS_GEN); | ||
1356 | |||
1357 | memmove(&cmdptr->params.tpccfg, | ||
1358 | pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg)); | ||
1359 | |||
1360 | ret = 0; | ||
1361 | break; | ||
1362 | case cmd_802_11_led_gpio_ctrl: | ||
1363 | { | ||
1364 | struct mrvlietypes_ledgpio *gpio = | ||
1365 | (struct mrvlietypes_ledgpio*) | ||
1366 | cmdptr->params.ledgpio.data; | ||
1367 | |||
1368 | memmove(&cmdptr->params.ledgpio, | ||
1369 | pdata_buf, | ||
1370 | sizeof(struct cmd_ds_802_11_led_ctrl)); | ||
1371 | |||
1372 | cmdptr->command = | ||
1373 | cpu_to_le16(cmd_802_11_led_gpio_ctrl); | ||
1374 | |||
1375 | #define ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN 8 | ||
1376 | cmdptr->size = | ||
1377 | cpu_to_le16(gpio->header.len + S_DS_GEN + | ||
1378 | ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN); | ||
1379 | gpio->header.len = cpu_to_le16(gpio->header.len); | ||
1380 | |||
1381 | ret = 0; | ||
1382 | break; | ||
1383 | } | ||
1384 | case cmd_802_11_pwr_cfg: | ||
1385 | cmdptr->command = cpu_to_le16(cmd_802_11_pwr_cfg); | ||
1386 | cmdptr->size = | ||
1387 | cpu_to_le16(sizeof(struct cmd_ds_802_11_pwr_cfg) + | ||
1388 | S_DS_GEN); | ||
1389 | memmove(&cmdptr->params.pwrcfg, pdata_buf, | ||
1390 | sizeof(struct cmd_ds_802_11_pwr_cfg)); | ||
1391 | |||
1392 | ret = 0; | ||
1393 | break; | ||
1394 | case cmd_bt_access: | ||
1395 | ret = wlan_cmd_bt_access(priv, cmdptr, cmd_action, pdata_buf); | ||
1396 | break; | ||
1397 | |||
1398 | case cmd_fwt_access: | ||
1399 | ret = wlan_cmd_fwt_access(priv, cmdptr, cmd_action, pdata_buf); | ||
1400 | break; | ||
1401 | |||
1402 | case cmd_mesh_access: | ||
1403 | ret = wlan_cmd_mesh_access(priv, cmdptr, cmd_action, pdata_buf); | ||
1404 | break; | ||
1405 | |||
1406 | case cmd_get_tsf: | ||
1407 | cmdptr->command = cpu_to_le16(cmd_get_tsf); | ||
1408 | cmdptr->size = | ||
1409 | cpu_to_le16(sizeof(struct cmd_ds_get_tsf) | ||
1410 | + S_DS_GEN); | ||
1411 | ret = 0; | ||
1412 | break; | ||
1413 | case cmd_802_11_tx_rate_query: | ||
1414 | cmdptr->command = | ||
1415 | cpu_to_le16(cmd_802_11_tx_rate_query); | ||
1416 | cmdptr->size = | ||
1417 | cpu_to_le16(sizeof(struct cmd_tx_rate_query) + | ||
1418 | S_DS_GEN); | ||
1419 | adapter->txrate = 0; | ||
1420 | ret = 0; | ||
1421 | break; | ||
1422 | default: | ||
1423 | lbs_pr_debug(1, "PREP_CMD: unknown command- %#x\n", cmd_no); | ||
1424 | ret = -1; | ||
1425 | break; | ||
1426 | } | ||
1427 | |||
1428 | /* return error, since the command preparation failed */ | ||
1429 | if (ret != 0) { | ||
1430 | lbs_pr_debug(1, "PREP_CMD: command preparation failed\n"); | ||
1431 | libertas_cleanup_and_insert_cmd(priv, cmdnode); | ||
1432 | ret = -1; | ||
1433 | goto done; | ||
1434 | } | ||
1435 | |||
1436 | cmdnode->cmdwaitqwoken = 0; | ||
1437 | |||
1438 | libertas_queue_cmd(adapter, cmdnode, 1); | ||
1439 | adapter->nr_cmd_pending++; | ||
1440 | wake_up_interruptible(&priv->mainthread.waitq); | ||
1441 | |||
1442 | if (wait_option & cmd_option_waitforrsp) { | ||
1443 | lbs_pr_debug(1, "PREP_CMD: Wait for CMD response\n"); | ||
1444 | might_sleep(); | ||
1445 | wait_event_interruptible(cmdnode->cmdwait_q, | ||
1446 | cmdnode->cmdwaitqwoken); | ||
1447 | } | ||
1448 | |||
1449 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1450 | if (adapter->cur_cmd_retcode) { | ||
1451 | lbs_pr_debug(1, "PREP_CMD: command failed with return code=%d\n", | ||
1452 | adapter->cur_cmd_retcode); | ||
1453 | adapter->cur_cmd_retcode = 0; | ||
1454 | ret = -1; | ||
1455 | } | ||
1456 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1457 | |||
1458 | done: | ||
1459 | LEAVE(); | ||
1460 | return ret; | ||
1461 | } | ||
1462 | |||
1463 | /** | ||
1464 | * @brief This function allocates the command buffer and link | ||
1465 | * it to command free queue. | ||
1466 | * | ||
1467 | * @param priv A pointer to wlan_private structure | ||
1468 | * @return 0 or -1 | ||
1469 | */ | ||
1470 | int libertas_allocate_cmd_buffer(wlan_private * priv) | ||
1471 | { | ||
1472 | int ret = 0; | ||
1473 | u32 ulbufsize; | ||
1474 | u32 i; | ||
1475 | struct cmd_ctrl_node *tempcmd_array; | ||
1476 | u8 *ptempvirtualaddr; | ||
1477 | wlan_adapter *adapter = priv->adapter; | ||
1478 | |||
1479 | ENTER(); | ||
1480 | |||
1481 | /* Allocate and initialize cmdCtrlNode */ | ||
1482 | ulbufsize = sizeof(struct cmd_ctrl_node) * MRVDRV_NUM_OF_CMD_BUFFER; | ||
1483 | |||
1484 | if (!(tempcmd_array = kmalloc(ulbufsize, GFP_KERNEL))) { | ||
1485 | lbs_pr_debug(1, | ||
1486 | "ALLOC_CMD_BUF: failed to allocate tempcmd_array\n"); | ||
1487 | ret = -1; | ||
1488 | goto done; | ||
1489 | } | ||
1490 | |||
1491 | adapter->cmd_array = tempcmd_array; | ||
1492 | memset(adapter->cmd_array, 0, ulbufsize); | ||
1493 | |||
1494 | /* Allocate and initialize command buffers */ | ||
1495 | ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER; | ||
1496 | for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { | ||
1497 | if (!(ptempvirtualaddr = kmalloc(ulbufsize, GFP_KERNEL))) { | ||
1498 | lbs_pr_debug(1, | ||
1499 | "ALLOC_CMD_BUF: ptempvirtualaddr: out of memory\n"); | ||
1500 | ret = -1; | ||
1501 | goto done; | ||
1502 | } | ||
1503 | |||
1504 | memset(ptempvirtualaddr, 0, ulbufsize); | ||
1505 | |||
1506 | /* Update command buffer virtual */ | ||
1507 | tempcmd_array[i].bufvirtualaddr = ptempvirtualaddr; | ||
1508 | } | ||
1509 | |||
1510 | for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { | ||
1511 | init_waitqueue_head(&tempcmd_array[i].cmdwait_q); | ||
1512 | libertas_cleanup_and_insert_cmd(priv, &tempcmd_array[i]); | ||
1513 | } | ||
1514 | |||
1515 | ret = 0; | ||
1516 | done: | ||
1517 | LEAVE(); | ||
1518 | return ret; | ||
1519 | } | ||
1520 | |||
1521 | /** | ||
1522 | * @brief This function frees the command buffer. | ||
1523 | * | ||
1524 | * @param priv A pointer to wlan_private structure | ||
1525 | * @return 0 or -1 | ||
1526 | */ | ||
1527 | int libertas_free_cmd_buffer(wlan_private * priv) | ||
1528 | { | ||
1529 | u32 ulbufsize; | ||
1530 | unsigned int i; | ||
1531 | struct cmd_ctrl_node *tempcmd_array; | ||
1532 | wlan_adapter *adapter = priv->adapter; | ||
1533 | |||
1534 | ENTER(); | ||
1535 | |||
1536 | /* need to check if cmd array is allocated or not */ | ||
1537 | if (adapter->cmd_array == NULL) { | ||
1538 | lbs_pr_debug(1, "FREE_CMD_BUF: cmd_array is Null\n"); | ||
1539 | goto done; | ||
1540 | } | ||
1541 | |||
1542 | tempcmd_array = adapter->cmd_array; | ||
1543 | |||
1544 | /* Release shared memory buffers */ | ||
1545 | ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER; | ||
1546 | for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { | ||
1547 | if (tempcmd_array[i].bufvirtualaddr) { | ||
1548 | lbs_pr_debug(1, "Free all the array\n"); | ||
1549 | kfree(tempcmd_array[i].bufvirtualaddr); | ||
1550 | tempcmd_array[i].bufvirtualaddr = NULL; | ||
1551 | } | ||
1552 | } | ||
1553 | |||
1554 | /* Release cmd_ctrl_node */ | ||
1555 | if (adapter->cmd_array) { | ||
1556 | lbs_pr_debug(1, "Free cmd_array\n"); | ||
1557 | kfree(adapter->cmd_array); | ||
1558 | adapter->cmd_array = NULL; | ||
1559 | } | ||
1560 | |||
1561 | done: | ||
1562 | LEAVE(); | ||
1563 | return 0; | ||
1564 | } | ||
1565 | |||
1566 | /** | ||
1567 | * @brief This function gets a free command node if available in | ||
1568 | * command free queue. | ||
1569 | * | ||
1570 | * @param priv A pointer to wlan_private structure | ||
1571 | * @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL | ||
1572 | */ | ||
1573 | struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv) | ||
1574 | { | ||
1575 | struct cmd_ctrl_node *tempnode; | ||
1576 | wlan_adapter *adapter = priv->adapter; | ||
1577 | unsigned long flags; | ||
1578 | |||
1579 | if (!adapter) | ||
1580 | return NULL; | ||
1581 | |||
1582 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1583 | |||
1584 | if (!list_empty(&adapter->cmdfreeq)) { | ||
1585 | tempnode = (struct cmd_ctrl_node *)adapter->cmdfreeq.next; | ||
1586 | list_del((struct list_head *)tempnode); | ||
1587 | } else { | ||
1588 | lbs_pr_debug(1, "GET_CMD_NODE: cmd_ctrl_node is not available\n"); | ||
1589 | tempnode = NULL; | ||
1590 | } | ||
1591 | |||
1592 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1593 | |||
1594 | if (tempnode) { | ||
1595 | lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode available\n"); | ||
1596 | lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode Address = %p\n", | ||
1597 | tempnode); | ||
1598 | cleanup_cmdnode(tempnode); | ||
1599 | } | ||
1600 | |||
1601 | return tempnode; | ||
1602 | } | ||
1603 | |||
1604 | /** | ||
1605 | * @brief This function cleans command node. | ||
1606 | * | ||
1607 | * @param ptempnode A pointer to cmdCtrlNode structure | ||
1608 | * @return n/a | ||
1609 | */ | ||
1610 | static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode) | ||
1611 | { | ||
1612 | if (!ptempnode) | ||
1613 | return; | ||
1614 | ptempnode->cmdwaitqwoken = 1; | ||
1615 | wake_up_interruptible(&ptempnode->cmdwait_q); | ||
1616 | ptempnode->status = 0; | ||
1617 | ptempnode->cmd_oid = (u32) 0; | ||
1618 | ptempnode->wait_option = 0; | ||
1619 | ptempnode->pdata_buf = NULL; | ||
1620 | |||
1621 | if (ptempnode->bufvirtualaddr != NULL) | ||
1622 | memset(ptempnode->bufvirtualaddr, 0, MRVDRV_SIZE_OF_CMD_BUFFER); | ||
1623 | return; | ||
1624 | } | ||
1625 | |||
1626 | /** | ||
1627 | * @brief This function initializes the command node. | ||
1628 | * | ||
1629 | * @param priv A pointer to wlan_private structure | ||
1630 | * @param ptempnode A pointer to cmd_ctrl_node structure | ||
1631 | * @param cmd_oid cmd oid: treated as sub command | ||
1632 | * @param wait_option wait option: wait response or not | ||
1633 | * @param pdata_buf A pointer to informaion buffer | ||
1634 | * @return 0 or -1 | ||
1635 | */ | ||
1636 | void libertas_set_cmd_ctrl_node(wlan_private * priv, | ||
1637 | struct cmd_ctrl_node *ptempnode, | ||
1638 | u32 cmd_oid, u16 wait_option, void *pdata_buf) | ||
1639 | { | ||
1640 | ENTER(); | ||
1641 | |||
1642 | if (!ptempnode) | ||
1643 | return; | ||
1644 | |||
1645 | ptempnode->cmd_oid = cmd_oid; | ||
1646 | ptempnode->wait_option = wait_option; | ||
1647 | ptempnode->pdata_buf = pdata_buf; | ||
1648 | |||
1649 | LEAVE(); | ||
1650 | } | ||
1651 | |||
1652 | /** | ||
1653 | * @brief This function executes next command in command | ||
1654 | * pending queue. It will put fimware back to PS mode | ||
1655 | * if applicable. | ||
1656 | * | ||
1657 | * @param priv A pointer to wlan_private structure | ||
1658 | * @return 0 or -1 | ||
1659 | */ | ||
1660 | int libertas_execute_next_command(wlan_private * priv) | ||
1661 | { | ||
1662 | wlan_adapter *adapter = priv->adapter; | ||
1663 | struct cmd_ctrl_node *cmdnode = NULL; | ||
1664 | struct cmd_ds_command *cmdptr; | ||
1665 | unsigned long flags; | ||
1666 | int ret = 0; | ||
1667 | |||
1668 | lbs_pr_debug(1, "libertas_execute_next_command\n"); | ||
1669 | |||
1670 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1671 | |||
1672 | if (adapter->cur_cmd) { | ||
1673 | lbs_pr_alert( "EXEC_NEXT_CMD: there is command in processing!\n"); | ||
1674 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1675 | ret = -1; | ||
1676 | goto done; | ||
1677 | } | ||
1678 | |||
1679 | if (!list_empty(&adapter->cmdpendingq)) { | ||
1680 | cmdnode = (struct cmd_ctrl_node *) | ||
1681 | adapter->cmdpendingq.next; | ||
1682 | } | ||
1683 | |||
1684 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1685 | |||
1686 | if (cmdnode) { | ||
1687 | lbs_pr_debug(1, | ||
1688 | "EXEC_NEXT_CMD: Got next command from cmdpendingq\n"); | ||
1689 | cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; | ||
1690 | |||
1691 | if (is_command_allowed_in_ps(cmdptr->command)) { | ||
1692 | if ((adapter->psstate == PS_STATE_SLEEP) | ||
1693 | || (adapter->psstate == PS_STATE_PRE_SLEEP) | ||
1694 | ) { | ||
1695 | lbs_pr_debug(1, | ||
1696 | "EXEC_NEXT_CMD: Cannot send cmd 0x%x in psstate %d\n", | ||
1697 | cmdptr->command, adapter->psstate); | ||
1698 | ret = -1; | ||
1699 | goto done; | ||
1700 | } | ||
1701 | lbs_pr_debug(1, "EXEC_NEXT_CMD: OK to send command " | ||
1702 | "0x%x in psstate %d\n", | ||
1703 | cmdptr->command, adapter->psstate); | ||
1704 | } else if (adapter->psstate != PS_STATE_FULL_POWER) { | ||
1705 | /* | ||
1706 | * 1. Non-PS command: | ||
1707 | * Queue it. set needtowakeup to TRUE if current state | ||
1708 | * is SLEEP, otherwise call libertas_ps_wakeup to send Exit_PS. | ||
1709 | * 2. PS command but not Exit_PS: | ||
1710 | * Ignore it. | ||
1711 | * 3. PS command Exit_PS: | ||
1712 | * Set needtowakeup to TRUE if current state is SLEEP, | ||
1713 | * otherwise send this command down to firmware | ||
1714 | * immediately. | ||
1715 | */ | ||
1716 | if (cmdptr->command != | ||
1717 | cpu_to_le16(cmd_802_11_ps_mode)) { | ||
1718 | /* Prepare to send Exit PS, | ||
1719 | * this non PS command will be sent later */ | ||
1720 | if ((adapter->psstate == PS_STATE_SLEEP) | ||
1721 | || (adapter->psstate == PS_STATE_PRE_SLEEP) | ||
1722 | ) { | ||
1723 | /* w/ new scheme, it will not reach here. | ||
1724 | since it is blocked in main_thread. */ | ||
1725 | adapter->needtowakeup = 1; | ||
1726 | } else | ||
1727 | libertas_ps_wakeup(priv, 0); | ||
1728 | |||
1729 | ret = 0; | ||
1730 | goto done; | ||
1731 | } else { | ||
1732 | /* | ||
1733 | * PS command. Ignore it if it is not Exit_PS. | ||
1734 | * otherwise send it down immediately. | ||
1735 | */ | ||
1736 | struct cmd_ds_802_11_ps_mode *psm = | ||
1737 | &cmdptr->params.psmode; | ||
1738 | |||
1739 | lbs_pr_debug(1, | ||
1740 | "EXEC_NEXT_CMD: PS cmd- action=0x%x\n", | ||
1741 | psm->action); | ||
1742 | if (psm->action != | ||
1743 | cpu_to_le16(cmd_subcmd_exit_ps)) { | ||
1744 | lbs_pr_debug(1, | ||
1745 | "EXEC_NEXT_CMD: Ignore Enter PS cmd\n"); | ||
1746 | list_del((struct list_head *)cmdnode); | ||
1747 | libertas_cleanup_and_insert_cmd(priv, cmdnode); | ||
1748 | |||
1749 | ret = 0; | ||
1750 | goto done; | ||
1751 | } | ||
1752 | |||
1753 | if ((adapter->psstate == PS_STATE_SLEEP) | ||
1754 | || (adapter->psstate == PS_STATE_PRE_SLEEP) | ||
1755 | ) { | ||
1756 | lbs_pr_debug(1, | ||
1757 | "EXEC_NEXT_CMD: Ignore ExitPS cmd in sleep\n"); | ||
1758 | list_del((struct list_head *)cmdnode); | ||
1759 | libertas_cleanup_and_insert_cmd(priv, cmdnode); | ||
1760 | adapter->needtowakeup = 1; | ||
1761 | |||
1762 | ret = 0; | ||
1763 | goto done; | ||
1764 | } | ||
1765 | |||
1766 | lbs_pr_debug(1, | ||
1767 | "EXEC_NEXT_CMD: Sending Exit_PS down...\n"); | ||
1768 | } | ||
1769 | } | ||
1770 | list_del((struct list_head *)cmdnode); | ||
1771 | lbs_pr_debug(1, "EXEC_NEXT_CMD: Sending 0x%04X command\n", | ||
1772 | cmdptr->command); | ||
1773 | DownloadcommandToStation(priv, cmdnode); | ||
1774 | } else { | ||
1775 | /* | ||
1776 | * check if in power save mode, if yes, put the device back | ||
1777 | * to PS mode | ||
1778 | */ | ||
1779 | if ((adapter->psmode != wlan802_11powermodecam) && | ||
1780 | (adapter->psstate == PS_STATE_FULL_POWER) && | ||
1781 | (adapter->connect_status == libertas_connected)) { | ||
1782 | if (adapter->secinfo.WPAenabled | ||
1783 | || adapter->secinfo.WPA2enabled) { | ||
1784 | /* check for valid WPA group keys */ | ||
1785 | if (adapter->wpa_mcast_key.len | ||
1786 | || adapter->wpa_unicast_key.len) { | ||
1787 | lbs_pr_debug(1, | ||
1788 | "EXEC_NEXT_CMD: WPA enabled and GTK_SET" | ||
1789 | " go back to PS_SLEEP"); | ||
1790 | libertas_ps_sleep(priv, 0); | ||
1791 | } | ||
1792 | } else { | ||
1793 | lbs_pr_debug(1, | ||
1794 | "EXEC_NEXT_CMD: command PendQ is empty," | ||
1795 | " go back to PS_SLEEP"); | ||
1796 | libertas_ps_sleep(priv, 0); | ||
1797 | } | ||
1798 | } | ||
1799 | } | ||
1800 | |||
1801 | ret = 0; | ||
1802 | done: | ||
1803 | return ret; | ||
1804 | } | ||
1805 | |||
1806 | void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str) | ||
1807 | { | ||
1808 | union iwreq_data iwrq; | ||
1809 | u8 buf[50]; | ||
1810 | |||
1811 | ENTER(); | ||
1812 | |||
1813 | memset(&iwrq, 0, sizeof(union iwreq_data)); | ||
1814 | memset(buf, 0, sizeof(buf)); | ||
1815 | |||
1816 | snprintf(buf, sizeof(buf) - 1, "%s", str); | ||
1817 | |||
1818 | iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN; | ||
1819 | |||
1820 | /* Send Event to upper layer */ | ||
1821 | lbs_pr_debug(1, "Event Indication string = %s\n", | ||
1822 | (char *)buf); | ||
1823 | lbs_pr_debug(1, "Event Indication String length = %d\n", iwrq.data.length); | ||
1824 | |||
1825 | lbs_pr_debug(1, "Sending wireless event IWEVCUSTOM for %s\n", str); | ||
1826 | wireless_send_event(priv->wlan_dev.netdev, IWEVCUSTOM, &iwrq, buf); | ||
1827 | |||
1828 | LEAVE(); | ||
1829 | return; | ||
1830 | } | ||
1831 | |||
1832 | static int sendconfirmsleep(wlan_private * priv, u8 * cmdptr, u16 size) | ||
1833 | { | ||
1834 | unsigned long flags; | ||
1835 | wlan_adapter *adapter = priv->adapter; | ||
1836 | int ret = 0; | ||
1837 | |||
1838 | ENTER(); | ||
1839 | |||
1840 | lbs_pr_debug(1, "SEND_SLEEPC_CMD: Before download, size of cmd = %d\n", | ||
1841 | size); | ||
1842 | |||
1843 | lbs_dbg_hex("SEND_SLEEPC_CMD: Sleep confirm command", cmdptr, size); | ||
1844 | |||
1845 | ret = libertas_sbi_host_to_card(priv, MVMS_CMD, cmdptr, size); | ||
1846 | priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; | ||
1847 | |||
1848 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1849 | if (adapter->intcounter || adapter->currenttxskb) | ||
1850 | lbs_pr_debug(1, "SEND_SLEEPC_CMD: intcounter=%d currenttxskb=%p\n", | ||
1851 | adapter->intcounter, adapter->currenttxskb); | ||
1852 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1853 | |||
1854 | if (ret) { | ||
1855 | lbs_pr_alert( | ||
1856 | "SEND_SLEEPC_CMD: Host to Card failed for Confirm Sleep\n"); | ||
1857 | } else { | ||
1858 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1859 | if (!adapter->intcounter) { | ||
1860 | adapter->psstate = PS_STATE_SLEEP; | ||
1861 | } else { | ||
1862 | lbs_pr_debug(1, "SEND_SLEEPC_CMD: After sent,IntC=%d\n", | ||
1863 | adapter->intcounter); | ||
1864 | } | ||
1865 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1866 | |||
1867 | lbs_pr_debug(1, "SEND_SLEEPC_CMD: Sent Confirm Sleep command\n"); | ||
1868 | lbs_pr_debug(1, "+"); | ||
1869 | } | ||
1870 | |||
1871 | LEAVE(); | ||
1872 | return ret; | ||
1873 | } | ||
1874 | |||
1875 | void libertas_ps_sleep(wlan_private * priv, int wait_option) | ||
1876 | { | ||
1877 | |||
1878 | ENTER(); | ||
1879 | |||
1880 | /* | ||
1881 | * PS is currently supported only in Infrastructure mode | ||
1882 | * Remove this check if it is to be supported in IBSS mode also | ||
1883 | */ | ||
1884 | |||
1885 | libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode, | ||
1886 | cmd_subcmd_enter_ps, wait_option, 0, NULL); | ||
1887 | |||
1888 | LEAVE(); | ||
1889 | return; | ||
1890 | } | ||
1891 | |||
1892 | /** | ||
1893 | * @brief This function sends Eixt_PS command to firmware. | ||
1894 | * | ||
1895 | * @param priv A pointer to wlan_private structure | ||
1896 | * @param wait_option wait response or not | ||
1897 | * @return n/a | ||
1898 | */ | ||
1899 | void libertas_ps_wakeup(wlan_private * priv, int wait_option) | ||
1900 | { | ||
1901 | enum WLAN_802_11_POWER_MODE Localpsmode; | ||
1902 | |||
1903 | ENTER(); | ||
1904 | |||
1905 | Localpsmode = wlan802_11powermodecam; | ||
1906 | |||
1907 | lbs_pr_debug(1, "Exit_PS: Localpsmode = %d\n", Localpsmode); | ||
1908 | |||
1909 | libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode, | ||
1910 | cmd_subcmd_exit_ps, | ||
1911 | wait_option, 0, &Localpsmode); | ||
1912 | |||
1913 | LEAVE(); | ||
1914 | return; | ||
1915 | } | ||
1916 | |||
1917 | /** | ||
1918 | * @brief This function checks condition and prepares to | ||
1919 | * send sleep confirm command to firmware if ok. | ||
1920 | * | ||
1921 | * @param priv A pointer to wlan_private structure | ||
1922 | * @param psmode Power Saving mode | ||
1923 | * @return n/a | ||
1924 | */ | ||
1925 | void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode) | ||
1926 | { | ||
1927 | unsigned long flags =0; | ||
1928 | wlan_adapter *adapter = priv->adapter; | ||
1929 | u8 allowed = 1; | ||
1930 | |||
1931 | ENTER(); | ||
1932 | |||
1933 | if (priv->wlan_dev.dnld_sent) { | ||
1934 | allowed = 0; | ||
1935 | lbs_pr_debug(1, "D"); | ||
1936 | } | ||
1937 | |||
1938 | spin_lock_irqsave(&adapter->driver_lock, flags); | ||
1939 | if (adapter->cur_cmd) { | ||
1940 | allowed = 0; | ||
1941 | lbs_pr_debug(1, "C"); | ||
1942 | } | ||
1943 | if (adapter->intcounter > 0) { | ||
1944 | allowed = 0; | ||
1945 | lbs_pr_debug(1, "I%d", adapter->intcounter); | ||
1946 | } | ||
1947 | spin_unlock_irqrestore(&adapter->driver_lock, flags); | ||
1948 | |||
1949 | if (allowed) { | ||
1950 | lbs_pr_debug(1, "Sending libertas_ps_confirm_sleep\n"); | ||
1951 | sendconfirmsleep(priv, (u8 *) & adapter->libertas_ps_confirm_sleep, | ||
1952 | sizeof(struct PS_CMD_ConfirmSleep)); | ||
1953 | } else { | ||
1954 | lbs_pr_debug(1, "Sleep Confirm has been delayed\n"); | ||
1955 | } | ||
1956 | |||
1957 | LEAVE(); | ||
1958 | } | ||