commands.c 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. /*
  2. * Intel Wireless Multicomm 3200 WiFi driver
  3. *
  4. * Copyright (C) 2009 Intel Corporation. All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * * Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * * Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in
  14. * the documentation and/or other materials provided with the
  15. * distribution.
  16. * * Neither the name of Intel Corporation nor the names of its
  17. * contributors may be used to endorse or promote products derived
  18. * from this software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. *
  32. *
  33. * Intel Corporation <ilw@linux.intel.com>
  34. * Samuel Ortiz <samuel.ortiz@intel.com>
  35. * Zhu Yi <yi.zhu@intel.com>
  36. *
  37. */
  38. #include <linux/kernel.h>
  39. #include <linux/wireless.h>
  40. #include <linux/etherdevice.h>
  41. #include <linux/ieee80211.h>
  42. #include <linux/sched.h>
  43. #include <linux/slab.h>
  44. #include <linux/moduleparam.h>
  45. #include "iwm.h"
  46. #include "bus.h"
  47. #include "hal.h"
  48. #include "umac.h"
  49. #include "commands.h"
  50. #include "debug.h"
  51. static int iwm_send_lmac_ptrough_cmd(struct iwm_priv *iwm,
  52. u8 lmac_cmd_id,
  53. const void *lmac_payload,
  54. u16 lmac_payload_size,
  55. u8 resp)
  56. {
  57. struct iwm_udma_wifi_cmd udma_cmd = UDMA_LMAC_INIT;
  58. struct iwm_umac_cmd umac_cmd;
  59. struct iwm_lmac_cmd lmac_cmd;
  60. lmac_cmd.id = lmac_cmd_id;
  61. umac_cmd.id = UMAC_CMD_OPCODE_WIFI_PASS_THROUGH;
  62. umac_cmd.resp = resp;
  63. return iwm_hal_send_host_cmd(iwm, &udma_cmd, &umac_cmd, &lmac_cmd,
  64. lmac_payload, lmac_payload_size);
  65. }
  66. int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size,
  67. bool resp)
  68. {
  69. struct iwm_umac_wifi_if *hdr = (struct iwm_umac_wifi_if *)payload;
  70. struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
  71. struct iwm_umac_cmd umac_cmd;
  72. int ret;
  73. u8 oid = hdr->oid;
  74. if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
  75. IWM_ERR(iwm, "Interface is not ready yet");
  76. return -EAGAIN;
  77. }
  78. umac_cmd.id = UMAC_CMD_OPCODE_WIFI_IF_WRAPPER;
  79. umac_cmd.resp = resp;
  80. ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd,
  81. payload, payload_size);
  82. if (resp) {
  83. ret = wait_event_interruptible_timeout(iwm->wifi_ntfy_queue,
  84. test_and_clear_bit(oid, &iwm->wifi_ntfy[0]),
  85. 3 * HZ);
  86. return ret ? 0 : -EBUSY;
  87. }
  88. return ret;
  89. }
  90. static int modparam_wiwi = COEX_MODE_CM;
  91. module_param_named(wiwi, modparam_wiwi, int, 0644);
  92. MODULE_PARM_DESC(wiwi, "Wifi-WiMAX coexistence: 1=SA, 2=XOR, 3=CM (default)");
  93. static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] =
  94. {
  95. {4, 3, 0, COEX_UNASSOC_IDLE_FLAGS},
  96. {4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
  97. {4, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
  98. {4, 3, 0, COEX_CALIBRATION_FLAGS},
  99. {4, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS},
  100. {4, 3, 0, COEX_CONNECTION_ESTAB_FLAGS},
  101. {4, 3, 0, COEX_ASSOCIATED_IDLE_FLAGS},
  102. {4, 3, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
  103. {4, 3, 0, COEX_ASSOC_AUTO_SCAN_FLAGS},
  104. {4, 3, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS},
  105. {6, 3, 0, COEX_XOR_RF_ON_FLAGS},
  106. {4, 3, 0, COEX_RF_OFF_FLAGS},
  107. {6, 6, 0, COEX_STAND_ALONE_DEBUG_FLAGS},
  108. {4, 3, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS},
  109. {4, 3, 0, COEX_RSRVD1_FLAGS},
  110. {4, 3, 0, COEX_RSRVD2_FLAGS}
  111. };
  112. static struct coex_event iwm_sta_cm_prio_tbl[COEX_EVENTS_NUM] =
  113. {
  114. {1, 1, 0, COEX_UNASSOC_IDLE_FLAGS},
  115. {4, 4, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
  116. {3, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
  117. {6, 6, 0, COEX_CALIBRATION_FLAGS},
  118. {3, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS},
  119. {6, 5, 0, COEX_CONNECTION_ESTAB_FLAGS},
  120. {4, 4, 0, COEX_ASSOCIATED_IDLE_FLAGS},
  121. {4, 4, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
  122. {4, 4, 0, COEX_ASSOC_AUTO_SCAN_FLAGS},
  123. {4, 4, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS},
  124. {1, 1, 0, COEX_RF_ON_FLAGS},
  125. {1, 1, 0, COEX_RF_OFF_FLAGS},
  126. {7, 7, 0, COEX_STAND_ALONE_DEBUG_FLAGS},
  127. {5, 4, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS},
  128. {1, 1, 0, COEX_RSRVD1_FLAGS},
  129. {1, 1, 0, COEX_RSRVD2_FLAGS}
  130. };
  131. int iwm_send_prio_table(struct iwm_priv *iwm)
  132. {
  133. struct iwm_coex_prio_table_cmd coex_table_cmd;
  134. u32 coex_enabled, mode_enabled;
  135. memset(&coex_table_cmd, 0, sizeof(struct iwm_coex_prio_table_cmd));
  136. coex_table_cmd.flags = COEX_FLAGS_STA_TABLE_VALID_MSK;
  137. switch (modparam_wiwi) {
  138. case COEX_MODE_XOR:
  139. case COEX_MODE_CM:
  140. coex_enabled = 1;
  141. break;
  142. default:
  143. coex_enabled = 0;
  144. break;
  145. }
  146. switch (iwm->conf.mode) {
  147. case UMAC_MODE_BSS:
  148. case UMAC_MODE_IBSS:
  149. mode_enabled = 1;
  150. break;
  151. default:
  152. mode_enabled = 0;
  153. break;
  154. }
  155. if (coex_enabled && mode_enabled) {
  156. coex_table_cmd.flags |= COEX_FLAGS_COEX_ENABLE_MSK |
  157. COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK |
  158. COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK;
  159. switch (modparam_wiwi) {
  160. case COEX_MODE_XOR:
  161. memcpy(coex_table_cmd.sta_prio, iwm_sta_xor_prio_tbl,
  162. sizeof(iwm_sta_xor_prio_tbl));
  163. break;
  164. case COEX_MODE_CM:
  165. memcpy(coex_table_cmd.sta_prio, iwm_sta_cm_prio_tbl,
  166. sizeof(iwm_sta_cm_prio_tbl));
  167. break;
  168. default:
  169. IWM_ERR(iwm, "Invalid coex_mode 0x%x\n",
  170. modparam_wiwi);
  171. break;
  172. }
  173. } else
  174. IWM_WARN(iwm, "coexistense disabled\n");
  175. return iwm_send_lmac_ptrough_cmd(iwm, COEX_PRIORITY_TABLE_CMD,
  176. &coex_table_cmd,
  177. sizeof(struct iwm_coex_prio_table_cmd), 0);
  178. }
  179. int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested)
  180. {
  181. struct iwm_lmac_cal_cfg_cmd cal_cfg_cmd;
  182. memset(&cal_cfg_cmd, 0, sizeof(struct iwm_lmac_cal_cfg_cmd));
  183. cal_cfg_cmd.ucode_cfg.init.enable = cpu_to_le32(calib_requested);
  184. cal_cfg_cmd.ucode_cfg.init.start = cpu_to_le32(calib_requested);
  185. cal_cfg_cmd.ucode_cfg.init.send_res = cpu_to_le32(calib_requested);
  186. cal_cfg_cmd.ucode_cfg.flags =
  187. cpu_to_le32(CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK);
  188. return iwm_send_lmac_ptrough_cmd(iwm, CALIBRATION_CFG_CMD, &cal_cfg_cmd,
  189. sizeof(struct iwm_lmac_cal_cfg_cmd), 1);
  190. }
  191. int iwm_send_periodic_calib_cfg(struct iwm_priv *iwm, u8 calib_requested)
  192. {
  193. struct iwm_lmac_cal_cfg_cmd cal_cfg_cmd;
  194. memset(&cal_cfg_cmd, 0, sizeof(struct iwm_lmac_cal_cfg_cmd));
  195. cal_cfg_cmd.ucode_cfg.periodic.enable = cpu_to_le32(calib_requested);
  196. cal_cfg_cmd.ucode_cfg.periodic.start = cpu_to_le32(calib_requested);
  197. return iwm_send_lmac_ptrough_cmd(iwm, CALIBRATION_CFG_CMD, &cal_cfg_cmd,
  198. sizeof(struct iwm_lmac_cal_cfg_cmd), 0);
  199. }
  200. int iwm_store_rxiq_calib_result(struct iwm_priv *iwm)
  201. {
  202. struct iwm_calib_rxiq *rxiq;
  203. u8 *eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ);
  204. int grplen = sizeof(struct iwm_calib_rxiq_group);
  205. rxiq = kzalloc(sizeof(struct iwm_calib_rxiq), GFP_KERNEL);
  206. if (!rxiq) {
  207. IWM_ERR(iwm, "Couldn't alloc memory for RX IQ\n");
  208. return -ENOMEM;
  209. }
  210. eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ);
  211. if (IS_ERR(eeprom_rxiq)) {
  212. IWM_ERR(iwm, "Couldn't access EEPROM RX IQ entry\n");
  213. kfree(rxiq);
  214. return PTR_ERR(eeprom_rxiq);
  215. }
  216. iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].buf = (u8 *)rxiq;
  217. iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].size = sizeof(*rxiq);
  218. rxiq->hdr.opcode = SHILOH_PHY_CALIBRATE_RX_IQ_CMD;
  219. rxiq->hdr.first_grp = 0;
  220. rxiq->hdr.grp_num = 1;
  221. rxiq->hdr.all_data_valid = 1;
  222. memcpy(&rxiq->group[0], eeprom_rxiq, 4 * grplen);
  223. memcpy(&rxiq->group[4], eeprom_rxiq + 6 * grplen, grplen);
  224. return 0;
  225. }
  226. int iwm_send_calib_results(struct iwm_priv *iwm)
  227. {
  228. int i, ret = 0;
  229. for (i = PHY_CALIBRATE_OPCODES_NUM; i < CALIBRATION_CMD_NUM; i++) {
  230. if (test_bit(i - PHY_CALIBRATE_OPCODES_NUM,
  231. &iwm->calib_done_map)) {
  232. IWM_DBG_CMD(iwm, DBG,
  233. "Send calibration %d result\n", i);
  234. ret |= iwm_send_lmac_ptrough_cmd(iwm,
  235. REPLY_PHY_CALIBRATION_CMD,
  236. iwm->calib_res[i].buf,
  237. iwm->calib_res[i].size, 0);
  238. kfree(iwm->calib_res[i].buf);
  239. iwm->calib_res[i].buf = NULL;
  240. iwm->calib_res[i].size = 0;
  241. }
  242. }
  243. return ret;
  244. }
  245. int iwm_send_ct_kill_cfg(struct iwm_priv *iwm, u8 entry, u8 exit)
  246. {
  247. struct iwm_ct_kill_cfg_cmd cmd;
  248. cmd.entry_threshold = entry;
  249. cmd.exit_threshold = exit;
  250. return iwm_send_lmac_ptrough_cmd(iwm, REPLY_CT_KILL_CONFIG_CMD, &cmd,
  251. sizeof(struct iwm_ct_kill_cfg_cmd), 0);
  252. }
  253. int iwm_send_umac_reset(struct iwm_priv *iwm, __le32 reset_flags, bool resp)
  254. {
  255. struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
  256. struct iwm_umac_cmd umac_cmd;
  257. struct iwm_umac_cmd_reset reset;
  258. reset.flags = reset_flags;
  259. umac_cmd.id = UMAC_CMD_OPCODE_RESET;
  260. umac_cmd.resp = resp;
  261. return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &reset,
  262. sizeof(struct iwm_umac_cmd_reset));
  263. }
  264. int iwm_umac_set_config_fix(struct iwm_priv *iwm, u16 tbl, u16 key, u32 value)
  265. {
  266. struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
  267. struct iwm_umac_cmd umac_cmd;
  268. struct iwm_umac_cmd_set_param_fix param;
  269. if ((tbl != UMAC_PARAM_TBL_CFG_FIX) &&
  270. (tbl != UMAC_PARAM_TBL_FA_CFG_FIX))
  271. return -EINVAL;
  272. umac_cmd.id = UMAC_CMD_OPCODE_SET_PARAM_FIX;
  273. umac_cmd.resp = 0;
  274. param.tbl = cpu_to_le16(tbl);
  275. param.key = cpu_to_le16(key);
  276. param.value = cpu_to_le32(value);
  277. return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &param,
  278. sizeof(struct iwm_umac_cmd_set_param_fix));
  279. }
  280. int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key,
  281. void *payload, u16 payload_size)
  282. {
  283. struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
  284. struct iwm_umac_cmd umac_cmd;
  285. struct iwm_umac_cmd_set_param_var *param_hdr;
  286. u8 *param;
  287. int ret;
  288. param = kzalloc(payload_size +
  289. sizeof(struct iwm_umac_cmd_set_param_var), GFP_KERNEL);
  290. if (!param) {
  291. IWM_ERR(iwm, "Couldn't allocate param\n");
  292. return -ENOMEM;
  293. }
  294. param_hdr = (struct iwm_umac_cmd_set_param_var *)param;
  295. umac_cmd.id = UMAC_CMD_OPCODE_SET_PARAM_VAR;
  296. umac_cmd.resp = 0;
  297. param_hdr->tbl = cpu_to_le16(UMAC_PARAM_TBL_CFG_VAR);
  298. param_hdr->key = cpu_to_le16(key);
  299. param_hdr->len = cpu_to_le16(payload_size);
  300. memcpy(param + sizeof(struct iwm_umac_cmd_set_param_var),
  301. payload, payload_size);
  302. ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, param,
  303. sizeof(struct iwm_umac_cmd_set_param_var) +
  304. payload_size);
  305. kfree(param);
  306. return ret;
  307. }
  308. int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags)
  309. {
  310. int ret;
  311. /* Use UMAC default values */
  312. ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
  313. CFG_POWER_INDEX, iwm->conf.power_index);
  314. if (ret < 0)
  315. return ret;
  316. ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX,
  317. CFG_FRAG_THRESHOLD,
  318. iwm->conf.frag_threshold);
  319. if (ret < 0)
  320. return ret;
  321. ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
  322. CFG_RTS_THRESHOLD,
  323. iwm->conf.rts_threshold);
  324. if (ret < 0)
  325. return ret;
  326. ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
  327. CFG_CTS_TO_SELF, iwm->conf.cts_to_self);
  328. if (ret < 0)
  329. return ret;
  330. ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
  331. CFG_WIRELESS_MODE,
  332. iwm->conf.wireless_mode);
  333. if (ret < 0)
  334. return ret;
  335. ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
  336. CFG_COEX_MODE, modparam_wiwi);
  337. if (ret < 0)
  338. return ret;
  339. /*
  340. ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
  341. CFG_ASSOCIATION_TIMEOUT,
  342. iwm->conf.assoc_timeout);
  343. if (ret < 0)
  344. return ret;
  345. ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
  346. CFG_ROAM_TIMEOUT,
  347. iwm->conf.roam_timeout);
  348. if (ret < 0)
  349. return ret;
  350. ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
  351. CFG_WIRELESS_MODE,
  352. WIRELESS_MODE_11A | WIRELESS_MODE_11G);
  353. if (ret < 0)
  354. return ret;
  355. */
  356. ret = iwm_umac_set_config_var(iwm, CFG_NET_ADDR,
  357. iwm_to_ndev(iwm)->dev_addr, ETH_ALEN);
  358. if (ret < 0)
  359. return ret;
  360. /* UMAC PM static configurations */
  361. ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
  362. CFG_PM_LEGACY_RX_TIMEOUT, 0x12C);
  363. if (ret < 0)
  364. return ret;
  365. ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
  366. CFG_PM_LEGACY_TX_TIMEOUT, 0x15E);
  367. if (ret < 0)
  368. return ret;
  369. ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
  370. CFG_PM_CTRL_FLAGS, 0x1);
  371. if (ret < 0)
  372. return ret;
  373. ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
  374. CFG_PM_KEEP_ALIVE_IN_BEACONS, 0x80);
  375. if (ret < 0)
  376. return ret;
  377. /* reset UMAC */
  378. ret = iwm_send_umac_reset(iwm, reset_flags, 1);
  379. if (ret < 0)
  380. return ret;
  381. ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_RESET, IWM_SRC_UMAC,
  382. WAIT_NOTIF_TIMEOUT);
  383. if (ret) {
  384. IWM_ERR(iwm, "Wait for UMAC RESET timeout\n");
  385. return ret;
  386. }
  387. return ret;
  388. }
  389. int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id)
  390. {
  391. struct iwm_udma_wifi_cmd udma_cmd;
  392. struct iwm_umac_cmd umac_cmd;
  393. struct iwm_tx_info *tx_info = skb_to_tx_info(skb);
  394. udma_cmd.eop = 1; /* always set eop for non-concatenated Tx */
  395. udma_cmd.credit_group = pool_id;
  396. udma_cmd.ra_tid = tx_info->sta << 4 | tx_info->tid;
  397. udma_cmd.lmac_offset = 0;
  398. umac_cmd.id = REPLY_TX;
  399. umac_cmd.color = tx_info->color;
  400. umac_cmd.resp = 0;
  401. return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd,
  402. skb->data, skb->len);
  403. }
  404. static int iwm_target_read(struct iwm_priv *iwm, __le32 address,
  405. u8 *response, u32 resp_size)
  406. {
  407. struct iwm_udma_nonwifi_cmd target_cmd;
  408. struct iwm_nonwifi_cmd *cmd;
  409. u16 seq_num;
  410. int ret = 0;
  411. target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ;
  412. target_cmd.addr = address;
  413. target_cmd.op1_sz = cpu_to_le32(resp_size);
  414. target_cmd.op2 = 0;
  415. target_cmd.handle_by_hw = 0;
  416. target_cmd.resp = 1;
  417. target_cmd.eop = 1;
  418. ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
  419. if (ret < 0) {
  420. IWM_ERR(iwm, "Couldn't send READ command\n");
  421. return ret;
  422. }
  423. /* When succeeding, the send_target routine returns the seq number */
  424. seq_num = ret;
  425. ret = wait_event_interruptible_timeout(iwm->nonwifi_queue,
  426. (cmd = iwm_get_pending_nonwifi_cmd(iwm, seq_num,
  427. UMAC_HDI_OUT_OPCODE_READ)) != NULL,
  428. 2 * HZ);
  429. if (!ret) {
  430. IWM_ERR(iwm, "Didn't receive a target READ answer\n");
  431. return ret;
  432. }
  433. memcpy(response, cmd->buf.hdr + sizeof(struct iwm_udma_in_hdr),
  434. resp_size);
  435. kfree(cmd);
  436. return 0;
  437. }
  438. int iwm_read_mac(struct iwm_priv *iwm, u8 *mac)
  439. {
  440. int ret;
  441. u8 mac_align[ALIGN(ETH_ALEN, 8)];
  442. ret = iwm_target_read(iwm, cpu_to_le32(WICO_MAC_ADDRESS_ADDR),
  443. mac_align, sizeof(mac_align));
  444. if (ret)
  445. return ret;
  446. if (is_valid_ether_addr(mac_align))
  447. memcpy(mac, mac_align, ETH_ALEN);
  448. else {
  449. IWM_ERR(iwm, "Invalid EEPROM MAC\n");
  450. memcpy(mac, iwm->conf.mac_addr, ETH_ALEN);
  451. get_random_bytes(&mac[3], 3);
  452. }
  453. return 0;
  454. }
  455. static int iwm_check_profile(struct iwm_priv *iwm)
  456. {
  457. if (!iwm->umac_profile_active)
  458. return -EAGAIN;
  459. if (iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_40 &&
  460. iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_104 &&
  461. iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_TKIP &&
  462. iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_CCMP) {
  463. IWM_ERR(iwm, "Wrong unicast cipher: 0x%x\n",
  464. iwm->umac_profile->sec.ucast_cipher);
  465. return -EAGAIN;
  466. }
  467. if (iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_40 &&
  468. iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_104 &&
  469. iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_TKIP &&
  470. iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_CCMP) {
  471. IWM_ERR(iwm, "Wrong multicast cipher: 0x%x\n",
  472. iwm->umac_profile->sec.mcast_cipher);
  473. return -EAGAIN;
  474. }
  475. if ((iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_40 ||
  476. iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_104) &&
  477. (iwm->umac_profile->sec.ucast_cipher !=
  478. iwm->umac_profile->sec.mcast_cipher)) {
  479. IWM_ERR(iwm, "Unicast and multicast ciphers differ for WEP\n");
  480. }
  481. return 0;
  482. }
  483. int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx)
  484. {
  485. struct iwm_umac_tx_key_id tx_key_id;
  486. int ret;
  487. ret = iwm_check_profile(iwm);
  488. if (ret < 0)
  489. return ret;
  490. /* UMAC only allows to set default key for WEP and auth type is
  491. * NOT 802.1X or RSNA. */
  492. if ((iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_40 &&
  493. iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_104) ||
  494. iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_8021X ||
  495. iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_RSNA_PSK)
  496. return 0;
  497. tx_key_id.hdr.oid = UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID;
  498. tx_key_id.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_tx_key_id) -
  499. sizeof(struct iwm_umac_wifi_if));
  500. tx_key_id.key_idx = key_idx;
  501. return iwm_send_wifi_if_cmd(iwm, &tx_key_id, sizeof(tx_key_id), 1);
  502. }
  503. int iwm_set_key(struct iwm_priv *iwm, bool remove, struct iwm_key *key)
  504. {
  505. int ret = 0;
  506. u8 cmd[64], *sta_addr, *key_data, key_len;
  507. s8 key_idx;
  508. u16 cmd_size = 0;
  509. struct iwm_umac_key_hdr *key_hdr = &key->hdr;
  510. struct iwm_umac_key_wep40 *wep40 = (struct iwm_umac_key_wep40 *)cmd;
  511. struct iwm_umac_key_wep104 *wep104 = (struct iwm_umac_key_wep104 *)cmd;
  512. struct iwm_umac_key_tkip *tkip = (struct iwm_umac_key_tkip *)cmd;
  513. struct iwm_umac_key_ccmp *ccmp = (struct iwm_umac_key_ccmp *)cmd;
  514. if (!remove) {
  515. ret = iwm_check_profile(iwm);
  516. if (ret < 0)
  517. return ret;
  518. }
  519. sta_addr = key->hdr.mac;
  520. key_data = key->key;
  521. key_len = key->key_len;
  522. key_idx = key->hdr.key_idx;
  523. if (!remove) {
  524. u8 auth_type = iwm->umac_profile->sec.auth_type;
  525. IWM_DBG_WEXT(iwm, DBG, "key_idx:%d\n", key_idx);
  526. IWM_DBG_WEXT(iwm, DBG, "key_len:%d\n", key_len);
  527. IWM_DBG_WEXT(iwm, DBG, "MAC:%pM, idx:%d, multicast:%d\n",
  528. key_hdr->mac, key_hdr->key_idx, key_hdr->multicast);
  529. IWM_DBG_WEXT(iwm, DBG, "profile: mcast:0x%x, ucast:0x%x\n",
  530. iwm->umac_profile->sec.mcast_cipher,
  531. iwm->umac_profile->sec.ucast_cipher);
  532. IWM_DBG_WEXT(iwm, DBG, "profile: auth_type:0x%x, flags:0x%x\n",
  533. iwm->umac_profile->sec.auth_type,
  534. iwm->umac_profile->sec.flags);
  535. switch (key->cipher) {
  536. case WLAN_CIPHER_SUITE_WEP40:
  537. wep40->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP40_KEY;
  538. wep40->hdr.buf_size =
  539. cpu_to_le16(sizeof(struct iwm_umac_key_wep40) -
  540. sizeof(struct iwm_umac_wifi_if));
  541. memcpy(&wep40->key_hdr, key_hdr,
  542. sizeof(struct iwm_umac_key_hdr));
  543. memcpy(wep40->key, key_data, key_len);
  544. wep40->static_key =
  545. !!((auth_type != UMAC_AUTH_TYPE_8021X) &&
  546. (auth_type != UMAC_AUTH_TYPE_RSNA_PSK));
  547. cmd_size = sizeof(struct iwm_umac_key_wep40);
  548. break;
  549. case WLAN_CIPHER_SUITE_WEP104:
  550. wep104->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP104_KEY;
  551. wep104->hdr.buf_size =
  552. cpu_to_le16(sizeof(struct iwm_umac_key_wep104) -
  553. sizeof(struct iwm_umac_wifi_if));
  554. memcpy(&wep104->key_hdr, key_hdr,
  555. sizeof(struct iwm_umac_key_hdr));
  556. memcpy(wep104->key, key_data, key_len);
  557. wep104->static_key =
  558. !!((auth_type != UMAC_AUTH_TYPE_8021X) &&
  559. (auth_type != UMAC_AUTH_TYPE_RSNA_PSK));
  560. cmd_size = sizeof(struct iwm_umac_key_wep104);
  561. break;
  562. case WLAN_CIPHER_SUITE_CCMP:
  563. key_hdr->key_idx++;
  564. ccmp->hdr.oid = UMAC_WIFI_IF_CMD_ADD_CCMP_KEY;
  565. ccmp->hdr.buf_size =
  566. cpu_to_le16(sizeof(struct iwm_umac_key_ccmp) -
  567. sizeof(struct iwm_umac_wifi_if));
  568. memcpy(&ccmp->key_hdr, key_hdr,
  569. sizeof(struct iwm_umac_key_hdr));
  570. memcpy(ccmp->key, key_data, key_len);
  571. if (key->seq_len)
  572. memcpy(ccmp->iv_count, key->seq, key->seq_len);
  573. cmd_size = sizeof(struct iwm_umac_key_ccmp);
  574. break;
  575. case WLAN_CIPHER_SUITE_TKIP:
  576. key_hdr->key_idx++;
  577. tkip->hdr.oid = UMAC_WIFI_IF_CMD_ADD_TKIP_KEY;
  578. tkip->hdr.buf_size =
  579. cpu_to_le16(sizeof(struct iwm_umac_key_tkip) -
  580. sizeof(struct iwm_umac_wifi_if));
  581. memcpy(&tkip->key_hdr, key_hdr,
  582. sizeof(struct iwm_umac_key_hdr));
  583. memcpy(tkip->tkip_key, key_data, IWM_TKIP_KEY_SIZE);
  584. memcpy(tkip->mic_tx_key, key_data + IWM_TKIP_KEY_SIZE,
  585. IWM_TKIP_MIC_SIZE);
  586. memcpy(tkip->mic_rx_key,
  587. key_data + IWM_TKIP_KEY_SIZE + IWM_TKIP_MIC_SIZE,
  588. IWM_TKIP_MIC_SIZE);
  589. if (key->seq_len)
  590. memcpy(ccmp->iv_count, key->seq, key->seq_len);
  591. cmd_size = sizeof(struct iwm_umac_key_tkip);
  592. break;
  593. default:
  594. return -ENOTSUPP;
  595. }
  596. if ((key->cipher == WLAN_CIPHER_SUITE_TKIP) ||
  597. (key->cipher == WLAN_CIPHER_SUITE_CCMP))
  598. /*
  599. * UGLY_UGLY_UGLY
  600. * Copied HACK from the MWG driver.
  601. * Without it, the key is set before the second
  602. * EAPOL frame is sent, and the latter is thus
  603. * encrypted.
  604. */
  605. schedule_timeout_interruptible(usecs_to_jiffies(300));
  606. ret = iwm_send_wifi_if_cmd(iwm, cmd, cmd_size, 1);
  607. } else {
  608. struct iwm_umac_key_remove key_remove;
  609. IWM_DBG_WEXT(iwm, ERR, "Removing key_idx:%d\n", key_idx);
  610. key_remove.hdr.oid = UMAC_WIFI_IF_CMD_REMOVE_KEY;
  611. key_remove.hdr.buf_size =
  612. cpu_to_le16(sizeof(struct iwm_umac_key_remove) -
  613. sizeof(struct iwm_umac_wifi_if));
  614. memcpy(&key_remove.key_hdr, key_hdr,
  615. sizeof(struct iwm_umac_key_hdr));
  616. ret = iwm_send_wifi_if_cmd(iwm, &key_remove,
  617. sizeof(struct iwm_umac_key_remove),
  618. 1);
  619. if (ret)
  620. return ret;
  621. iwm->keys[key_idx].key_len = 0;
  622. }
  623. return ret;
  624. }
  625. int iwm_send_mlme_profile(struct iwm_priv *iwm)
  626. {
  627. int ret;
  628. struct iwm_umac_profile profile;
  629. memcpy(&profile, iwm->umac_profile, sizeof(profile));
  630. profile.hdr.oid = UMAC_WIFI_IF_CMD_SET_PROFILE;
  631. profile.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_profile) -
  632. sizeof(struct iwm_umac_wifi_if));
  633. ret = iwm_send_wifi_if_cmd(iwm, &profile, sizeof(profile), 1);
  634. if (ret) {
  635. IWM_ERR(iwm, "Send profile command failed\n");
  636. return ret;
  637. }
  638. set_bit(IWM_STATUS_SME_CONNECTING, &iwm->status);
  639. return 0;
  640. }
  641. int __iwm_invalidate_mlme_profile(struct iwm_priv *iwm)
  642. {
  643. struct iwm_umac_invalidate_profile invalid;
  644. invalid.hdr.oid = UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE;
  645. invalid.hdr.buf_size =
  646. cpu_to_le16(sizeof(struct iwm_umac_invalidate_profile) -
  647. sizeof(struct iwm_umac_wifi_if));
  648. invalid.reason = WLAN_REASON_UNSPECIFIED;
  649. return iwm_send_wifi_if_cmd(iwm, &invalid, sizeof(invalid), 1);
  650. }
  651. int iwm_invalidate_mlme_profile(struct iwm_priv *iwm)
  652. {
  653. int ret;
  654. ret = __iwm_invalidate_mlme_profile(iwm);
  655. if (ret)
  656. return ret;
  657. ret = wait_event_interruptible_timeout(iwm->mlme_queue,
  658. (iwm->umac_profile_active == 0), 5 * HZ);
  659. return ret ? 0 : -EBUSY;
  660. }
  661. int iwm_tx_power_trigger(struct iwm_priv *iwm)
  662. {
  663. struct iwm_umac_pwr_trigger pwr_trigger;
  664. pwr_trigger.hdr.oid = UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER;
  665. pwr_trigger.hdr.buf_size =
  666. cpu_to_le16(sizeof(struct iwm_umac_pwr_trigger) -
  667. sizeof(struct iwm_umac_wifi_if));
  668. return iwm_send_wifi_if_cmd(iwm, &pwr_trigger, sizeof(pwr_trigger), 1);
  669. }
  670. int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags)
  671. {
  672. struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
  673. struct iwm_umac_cmd umac_cmd;
  674. struct iwm_umac_cmd_stats_req stats_req;
  675. stats_req.flags = cpu_to_le32(flags);
  676. umac_cmd.id = UMAC_CMD_OPCODE_STATISTIC_REQUEST;
  677. umac_cmd.resp = 0;
  678. return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stats_req,
  679. sizeof(struct iwm_umac_cmd_stats_req));
  680. }
  681. int iwm_send_umac_channel_list(struct iwm_priv *iwm)
  682. {
  683. struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
  684. struct iwm_umac_cmd umac_cmd;
  685. struct iwm_umac_cmd_get_channel_list *ch_list;
  686. int size = sizeof(struct iwm_umac_cmd_get_channel_list) +
  687. sizeof(struct iwm_umac_channel_info) * 4;
  688. int ret;
  689. ch_list = kzalloc(size, GFP_KERNEL);
  690. if (!ch_list) {
  691. IWM_ERR(iwm, "Couldn't allocate channel list cmd\n");
  692. return -ENOMEM;
  693. }
  694. ch_list->ch[0].band = UMAC_BAND_2GHZ;
  695. ch_list->ch[0].type = UMAC_CHANNEL_WIDTH_20MHZ;
  696. ch_list->ch[0].flags = UMAC_CHANNEL_FLAG_VALID;
  697. ch_list->ch[1].band = UMAC_BAND_5GHZ;
  698. ch_list->ch[1].type = UMAC_CHANNEL_WIDTH_20MHZ;
  699. ch_list->ch[1].flags = UMAC_CHANNEL_FLAG_VALID;
  700. ch_list->ch[2].band = UMAC_BAND_2GHZ;
  701. ch_list->ch[2].type = UMAC_CHANNEL_WIDTH_20MHZ;
  702. ch_list->ch[2].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS;
  703. ch_list->ch[3].band = UMAC_BAND_5GHZ;
  704. ch_list->ch[3].type = UMAC_CHANNEL_WIDTH_20MHZ;
  705. ch_list->ch[3].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS;
  706. ch_list->count = cpu_to_le16(4);
  707. umac_cmd.id = UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST;
  708. umac_cmd.resp = 1;
  709. ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, ch_list, size);
  710. kfree(ch_list);
  711. return ret;
  712. }
  713. int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
  714. int ssid_num)
  715. {
  716. struct iwm_umac_cmd_scan_request req;
  717. int i, ret;
  718. memset(&req, 0, sizeof(struct iwm_umac_cmd_scan_request));
  719. req.hdr.oid = UMAC_WIFI_IF_CMD_SCAN_REQUEST;
  720. req.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_cmd_scan_request)
  721. - sizeof(struct iwm_umac_wifi_if));
  722. req.type = UMAC_WIFI_IF_SCAN_TYPE_USER;
  723. req.timeout = 2;
  724. req.seq_num = iwm->scan_id;
  725. req.ssid_num = min(ssid_num, UMAC_WIFI_IF_PROBE_OPTION_MAX);
  726. for (i = 0; i < req.ssid_num; i++) {
  727. memcpy(req.ssids[i].ssid, ssids[i].ssid, ssids[i].ssid_len);
  728. req.ssids[i].ssid_len = ssids[i].ssid_len;
  729. }
  730. ret = iwm_send_wifi_if_cmd(iwm, &req, sizeof(req), 0);
  731. if (ret) {
  732. IWM_ERR(iwm, "Couldn't send scan request\n");
  733. return ret;
  734. }
  735. iwm->scan_id = (iwm->scan_id + 1) % IWM_SCAN_ID_MAX;
  736. return 0;
  737. }
  738. int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len)
  739. {
  740. struct cfg80211_ssid one_ssid;
  741. if (test_and_set_bit(IWM_STATUS_SCANNING, &iwm->status))
  742. return 0;
  743. one_ssid.ssid_len = min(ssid_len, IEEE80211_MAX_SSID_LEN);
  744. memcpy(&one_ssid.ssid, ssid, one_ssid.ssid_len);
  745. return iwm_scan_ssids(iwm, &one_ssid, 1);
  746. }
  747. int iwm_target_reset(struct iwm_priv *iwm)
  748. {
  749. struct iwm_udma_nonwifi_cmd target_cmd;
  750. target_cmd.opcode = UMAC_HDI_OUT_OPCODE_REBOOT;
  751. target_cmd.addr = 0;
  752. target_cmd.op1_sz = 0;
  753. target_cmd.op2 = 0;
  754. target_cmd.handle_by_hw = 0;
  755. target_cmd.resp = 0;
  756. target_cmd.eop = 1;
  757. return iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
  758. }
  759. int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm,
  760. struct iwm_umac_notif_stop_resume_tx *ntf)
  761. {
  762. struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
  763. struct iwm_umac_cmd umac_cmd;
  764. struct iwm_umac_cmd_stop_resume_tx stp_res_cmd;
  765. struct iwm_sta_info *sta_info;
  766. u8 sta_id = STA_ID_N_COLOR_ID(ntf->sta_id);
  767. int i;
  768. sta_info = &iwm->sta_table[sta_id];
  769. if (!sta_info->valid) {
  770. IWM_ERR(iwm, "Invalid STA: %d\n", sta_id);
  771. return -EINVAL;
  772. }
  773. umac_cmd.id = UMAC_CMD_OPCODE_STOP_RESUME_STA_TX;
  774. umac_cmd.resp = 0;
  775. stp_res_cmd.flags = ntf->flags;
  776. stp_res_cmd.sta_id = ntf->sta_id;
  777. stp_res_cmd.stop_resume_tid_msk = ntf->stop_resume_tid_msk;
  778. for (i = 0; i < IWM_UMAC_TID_NR; i++)
  779. stp_res_cmd.last_seq_num[i] =
  780. sta_info->tid_info[i].last_seq_num;
  781. return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stp_res_cmd,
  782. sizeof(struct iwm_umac_cmd_stop_resume_tx));
  783. }
  784. int iwm_send_pmkid_update(struct iwm_priv *iwm,
  785. struct cfg80211_pmksa *pmksa, u32 command)
  786. {
  787. struct iwm_umac_pmkid_update update;
  788. int ret;
  789. memset(&update, 0, sizeof(struct iwm_umac_pmkid_update));
  790. update.hdr.oid = UMAC_WIFI_IF_CMD_PMKID_UPDATE;
  791. update.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_pmkid_update) -
  792. sizeof(struct iwm_umac_wifi_if));
  793. update.command = cpu_to_le32(command);
  794. if (pmksa->bssid)
  795. memcpy(&update.bssid, pmksa->bssid, ETH_ALEN);
  796. if (pmksa->pmkid)
  797. memcpy(&update.pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
  798. ret = iwm_send_wifi_if_cmd(iwm, &update,
  799. sizeof(struct iwm_umac_pmkid_update), 0);
  800. if (ret) {
  801. IWM_ERR(iwm, "PMKID update command failed\n");
  802. return ret;
  803. }
  804. return 0;
  805. }