1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2f988d640SKalle Valo /* 3f988d640SKalle Valo * This file contains the handling of command. 4f988d640SKalle Valo * It prepares command and sends it to firmware when it is ready. 5f988d640SKalle Valo */ 6f988d640SKalle Valo 7f988d640SKalle Valo #include <linux/hardirq.h> 8f988d640SKalle Valo #include <linux/kfifo.h> 9f988d640SKalle Valo #include <linux/sched.h> 10f988d640SKalle Valo #include <linux/slab.h> 11f988d640SKalle Valo #include <linux/if_arp.h> 12f988d640SKalle Valo #include <linux/export.h> 13f988d640SKalle Valo 14f988d640SKalle Valo #include "decl.h" 15f988d640SKalle Valo #include "cfg.h" 16f988d640SKalle Valo #include "cmd.h" 17f988d640SKalle Valo 18f988d640SKalle Valo #define CAL_NF(nf) ((s32)(-(s32)(nf))) 19f988d640SKalle Valo #define CAL_RSSI(snr, nf) ((s32)((s32)(snr) + CAL_NF(nf))) 20f988d640SKalle Valo 21f988d640SKalle Valo /** 22f988d640SKalle Valo * lbs_cmd_copyback - Simple callback that copies response back into command 23f988d640SKalle Valo * 24f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 25f988d640SKalle Valo * @extra: A pointer to the original command structure for which 26f988d640SKalle Valo * 'resp' is a response 27f988d640SKalle Valo * @resp: A pointer to the command response 28f988d640SKalle Valo * 29f988d640SKalle Valo * returns: 0 on success, error on failure 30f988d640SKalle Valo */ 31f988d640SKalle Valo int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, 32f988d640SKalle Valo struct cmd_header *resp) 33f988d640SKalle Valo { 34f988d640SKalle Valo struct cmd_header *buf = (void *)extra; 35f988d640SKalle Valo uint16_t copy_len; 36f988d640SKalle Valo 37f988d640SKalle Valo copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); 38f988d640SKalle Valo memcpy(buf, resp, copy_len); 39f988d640SKalle Valo return 0; 40f988d640SKalle Valo } 41f988d640SKalle Valo EXPORT_SYMBOL_GPL(lbs_cmd_copyback); 42f988d640SKalle Valo 43f988d640SKalle Valo /** 44f988d640SKalle Valo * lbs_cmd_async_callback - Simple callback that ignores the result. 45f988d640SKalle Valo * Use this if you just want to send a command to the hardware, but don't 46f988d640SKalle Valo * care for the result. 47f988d640SKalle Valo * 48f988d640SKalle Valo * @priv: ignored 49f988d640SKalle Valo * @extra: ignored 50f988d640SKalle Valo * @resp: ignored 51f988d640SKalle Valo * 52f988d640SKalle Valo * returns: 0 for success 53f988d640SKalle Valo */ 54f988d640SKalle Valo static int lbs_cmd_async_callback(struct lbs_private *priv, unsigned long extra, 55f988d640SKalle Valo struct cmd_header *resp) 56f988d640SKalle Valo { 57f988d640SKalle Valo return 0; 58f988d640SKalle Valo } 59f988d640SKalle Valo 60f988d640SKalle Valo 61f988d640SKalle Valo /** 62f988d640SKalle Valo * is_command_allowed_in_ps - tests if a command is allowed in Power Save mode 63f988d640SKalle Valo * 64f988d640SKalle Valo * @cmd: the command ID 65f988d640SKalle Valo * 66f988d640SKalle Valo * returns: 1 if allowed, 0 if not allowed 67f988d640SKalle Valo */ 68f988d640SKalle Valo static u8 is_command_allowed_in_ps(u16 cmd) 69f988d640SKalle Valo { 70f988d640SKalle Valo switch (cmd) { 71f988d640SKalle Valo case CMD_802_11_RSSI: 72f988d640SKalle Valo return 1; 73f988d640SKalle Valo case CMD_802_11_HOST_SLEEP_CFG: 74f988d640SKalle Valo return 1; 75f988d640SKalle Valo default: 76f988d640SKalle Valo break; 77f988d640SKalle Valo } 78f988d640SKalle Valo return 0; 79f988d640SKalle Valo } 80f988d640SKalle Valo 81f988d640SKalle Valo /** 82f988d640SKalle Valo * lbs_update_hw_spec - Updates the hardware details like MAC address 83f988d640SKalle Valo * and regulatory region 84f988d640SKalle Valo * 85f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 86f988d640SKalle Valo * 87f988d640SKalle Valo * returns: 0 on success, error on failure 88f988d640SKalle Valo */ 89f988d640SKalle Valo int lbs_update_hw_spec(struct lbs_private *priv) 90f988d640SKalle Valo { 91f988d640SKalle Valo struct cmd_ds_get_hw_spec cmd; 92f988d640SKalle Valo int ret = -1; 93f988d640SKalle Valo u32 i; 94f988d640SKalle Valo 95f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 96f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 97f988d640SKalle Valo memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); 98f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); 99f988d640SKalle Valo if (ret) 100f988d640SKalle Valo goto out; 101f988d640SKalle Valo 102f988d640SKalle Valo priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); 103f988d640SKalle Valo 104f988d640SKalle Valo /* The firmware release is in an interesting format: the patch 105f988d640SKalle Valo * level is in the most significant nibble ... so fix that: */ 106f988d640SKalle Valo priv->fwrelease = le32_to_cpu(cmd.fwrelease); 107f988d640SKalle Valo priv->fwrelease = (priv->fwrelease << 8) | 108f988d640SKalle Valo (priv->fwrelease >> 24 & 0xff); 109f988d640SKalle Valo 110f988d640SKalle Valo /* Some firmware capabilities: 111f988d640SKalle Valo * CF card firmware 5.0.16p0: cap 0x00000303 112f988d640SKalle Valo * USB dongle firmware 5.110.17p2: cap 0x00000303 113f988d640SKalle Valo */ 114f988d640SKalle Valo netdev_info(priv->dev, "%pM, fw %u.%u.%up%u, cap 0x%08x\n", 115f988d640SKalle Valo cmd.permanentaddr, 116f988d640SKalle Valo priv->fwrelease >> 24 & 0xff, 117f988d640SKalle Valo priv->fwrelease >> 16 & 0xff, 118f988d640SKalle Valo priv->fwrelease >> 8 & 0xff, 119f988d640SKalle Valo priv->fwrelease & 0xff, 120f988d640SKalle Valo priv->fwcapinfo); 121f988d640SKalle Valo lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", 122f988d640SKalle Valo cmd.hwifversion, cmd.version); 123f988d640SKalle Valo 124f988d640SKalle Valo /* Clamp region code to 8-bit since FW spec indicates that it should 125f988d640SKalle Valo * only ever be 8-bit, even though the field size is 16-bit. Some firmware 126f988d640SKalle Valo * returns non-zero high 8 bits here. 127f988d640SKalle Valo * 128f988d640SKalle Valo * Firmware version 4.0.102 used in CF8381 has region code shifted. We 129f988d640SKalle Valo * need to check for this problem and handle it properly. 130f988d640SKalle Valo */ 131f988d640SKalle Valo if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4) 132f988d640SKalle Valo priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF; 133f988d640SKalle Valo else 134f988d640SKalle Valo priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; 135f988d640SKalle Valo 136f988d640SKalle Valo for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { 137f988d640SKalle Valo /* use the region code to search for the index */ 138f988d640SKalle Valo if (priv->regioncode == lbs_region_code_to_index[i]) 139f988d640SKalle Valo break; 140f988d640SKalle Valo } 141f988d640SKalle Valo 142f988d640SKalle Valo /* if it's unidentified region code, use the default (USA) */ 143f988d640SKalle Valo if (i >= MRVDRV_MAX_REGION_CODE) { 144f988d640SKalle Valo priv->regioncode = 0x10; 145f988d640SKalle Valo netdev_info(priv->dev, 146f988d640SKalle Valo "unidentified region code; using the default (USA)\n"); 147f988d640SKalle Valo } 148f988d640SKalle Valo 149f988d640SKalle Valo if (priv->current_addr[0] == 0xff) 150f988d640SKalle Valo memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); 151f988d640SKalle Valo 152f988d640SKalle Valo if (!priv->copied_hwaddr) { 153f988d640SKalle Valo memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN); 154f988d640SKalle Valo if (priv->mesh_dev) 155f988d640SKalle Valo memcpy(priv->mesh_dev->dev_addr, 156f988d640SKalle Valo priv->current_addr, ETH_ALEN); 157f988d640SKalle Valo priv->copied_hwaddr = 1; 158f988d640SKalle Valo } 159f988d640SKalle Valo 160f988d640SKalle Valo out: 161f988d640SKalle Valo return ret; 162f988d640SKalle Valo } 163f988d640SKalle Valo 164f988d640SKalle Valo static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy, 165f988d640SKalle Valo struct cmd_header *resp) 166f988d640SKalle Valo { 167f988d640SKalle Valo if (priv->is_host_sleep_activated) { 168f988d640SKalle Valo priv->is_host_sleep_configured = 0; 169f988d640SKalle Valo if (priv->psstate == PS_STATE_FULL_POWER) { 170f988d640SKalle Valo priv->is_host_sleep_activated = 0; 171f988d640SKalle Valo wake_up_interruptible(&priv->host_sleep_q); 172f988d640SKalle Valo } 173f988d640SKalle Valo } else { 174f988d640SKalle Valo priv->is_host_sleep_configured = 1; 175f988d640SKalle Valo } 1764bc606afSKees Cook 177f988d640SKalle Valo return 0; 178f988d640SKalle Valo } 179f988d640SKalle Valo 180f988d640SKalle Valo int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria, 181f988d640SKalle Valo struct wol_config *p_wol_config) 182f988d640SKalle Valo { 183f988d640SKalle Valo struct cmd_ds_host_sleep cmd_config; 184f988d640SKalle Valo int ret; 185f988d640SKalle Valo 186f988d640SKalle Valo /* 187f988d640SKalle Valo * Certain firmware versions do not support EHS_REMOVE_WAKEUP command 188f988d640SKalle Valo * and the card will return a failure. Since we need to be 189f988d640SKalle Valo * able to reset the mask, in those cases we set a 0 mask instead. 190f988d640SKalle Valo */ 191f988d640SKalle Valo if (criteria == EHS_REMOVE_WAKEUP && !priv->ehs_remove_supported) 192f988d640SKalle Valo criteria = 0; 193f988d640SKalle Valo 194f988d640SKalle Valo cmd_config.hdr.size = cpu_to_le16(sizeof(cmd_config)); 195f988d640SKalle Valo cmd_config.criteria = cpu_to_le32(criteria); 196f988d640SKalle Valo cmd_config.gpio = priv->wol_gpio; 197f988d640SKalle Valo cmd_config.gap = priv->wol_gap; 198f988d640SKalle Valo 199f988d640SKalle Valo if (p_wol_config != NULL) 200f988d640SKalle Valo memcpy((uint8_t *)&cmd_config.wol_conf, (uint8_t *)p_wol_config, 201f988d640SKalle Valo sizeof(struct wol_config)); 202f988d640SKalle Valo else 203f988d640SKalle Valo cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE; 204f988d640SKalle Valo 205f988d640SKalle Valo ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr, 206f988d640SKalle Valo le16_to_cpu(cmd_config.hdr.size), 207f988d640SKalle Valo lbs_ret_host_sleep_cfg, 0); 208f988d640SKalle Valo if (!ret) { 209f988d640SKalle Valo if (p_wol_config) 210f988d640SKalle Valo memcpy((uint8_t *) p_wol_config, 211f988d640SKalle Valo (uint8_t *)&cmd_config.wol_conf, 212f988d640SKalle Valo sizeof(struct wol_config)); 213f988d640SKalle Valo } else { 214f988d640SKalle Valo netdev_info(priv->dev, "HOST_SLEEP_CFG failed %d\n", ret); 215f988d640SKalle Valo } 216f988d640SKalle Valo 217f988d640SKalle Valo return ret; 218f988d640SKalle Valo } 219f988d640SKalle Valo EXPORT_SYMBOL_GPL(lbs_host_sleep_cfg); 220f988d640SKalle Valo 221f988d640SKalle Valo /** 222f988d640SKalle Valo * lbs_set_ps_mode - Sets the Power Save mode 223f988d640SKalle Valo * 224f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 225f988d640SKalle Valo * @cmd_action: The Power Save operation (PS_MODE_ACTION_ENTER_PS or 226f988d640SKalle Valo * PS_MODE_ACTION_EXIT_PS) 227f988d640SKalle Valo * @block: Whether to block on a response or not 228f988d640SKalle Valo * 229f988d640SKalle Valo * returns: 0 on success, error on failure 230f988d640SKalle Valo */ 231f988d640SKalle Valo int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block) 232f988d640SKalle Valo { 233f988d640SKalle Valo struct cmd_ds_802_11_ps_mode cmd; 234f988d640SKalle Valo int ret = 0; 235f988d640SKalle Valo 236f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 237f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 238f988d640SKalle Valo cmd.action = cpu_to_le16(cmd_action); 239f988d640SKalle Valo 240f988d640SKalle Valo if (cmd_action == PS_MODE_ACTION_ENTER_PS) { 241f988d640SKalle Valo lbs_deb_cmd("PS_MODE: action ENTER_PS\n"); 242f988d640SKalle Valo cmd.multipledtim = cpu_to_le16(1); /* Default DTIM multiple */ 243f988d640SKalle Valo } else if (cmd_action == PS_MODE_ACTION_EXIT_PS) { 244f988d640SKalle Valo lbs_deb_cmd("PS_MODE: action EXIT_PS\n"); 245f988d640SKalle Valo } else { 246f988d640SKalle Valo /* We don't handle CONFIRM_SLEEP here because it needs to 247f988d640SKalle Valo * be fastpathed to the firmware. 248f988d640SKalle Valo */ 249f988d640SKalle Valo lbs_deb_cmd("PS_MODE: unknown action 0x%X\n", cmd_action); 250f988d640SKalle Valo ret = -EOPNOTSUPP; 251f988d640SKalle Valo goto out; 252f988d640SKalle Valo } 253f988d640SKalle Valo 254f988d640SKalle Valo if (block) 255f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11_PS_MODE, &cmd); 256f988d640SKalle Valo else 257f988d640SKalle Valo lbs_cmd_async(priv, CMD_802_11_PS_MODE, &cmd.hdr, sizeof (cmd)); 258f988d640SKalle Valo 259f988d640SKalle Valo out: 260f988d640SKalle Valo return ret; 261f988d640SKalle Valo } 262f988d640SKalle Valo 263f988d640SKalle Valo int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, 264f988d640SKalle Valo struct sleep_params *sp) 265f988d640SKalle Valo { 266f988d640SKalle Valo struct cmd_ds_802_11_sleep_params cmd; 267f988d640SKalle Valo int ret; 268f988d640SKalle Valo 269f988d640SKalle Valo if (cmd_action == CMD_ACT_GET) { 270f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 271f988d640SKalle Valo } else { 272f988d640SKalle Valo cmd.error = cpu_to_le16(sp->sp_error); 273f988d640SKalle Valo cmd.offset = cpu_to_le16(sp->sp_offset); 274f988d640SKalle Valo cmd.stabletime = cpu_to_le16(sp->sp_stabletime); 275f988d640SKalle Valo cmd.calcontrol = sp->sp_calcontrol; 276f988d640SKalle Valo cmd.externalsleepclk = sp->sp_extsleepclk; 277f988d640SKalle Valo cmd.reserved = cpu_to_le16(sp->sp_reserved); 278f988d640SKalle Valo } 279f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 280f988d640SKalle Valo cmd.action = cpu_to_le16(cmd_action); 281f988d640SKalle Valo 282f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11_SLEEP_PARAMS, &cmd); 283f988d640SKalle Valo 284f988d640SKalle Valo if (!ret) { 285f988d640SKalle Valo lbs_deb_cmd("error 0x%x, offset 0x%x, stabletime 0x%x, " 286f988d640SKalle Valo "calcontrol 0x%x extsleepclk 0x%x\n", 287f988d640SKalle Valo le16_to_cpu(cmd.error), le16_to_cpu(cmd.offset), 288f988d640SKalle Valo le16_to_cpu(cmd.stabletime), cmd.calcontrol, 289f988d640SKalle Valo cmd.externalsleepclk); 290f988d640SKalle Valo 291f988d640SKalle Valo sp->sp_error = le16_to_cpu(cmd.error); 292f988d640SKalle Valo sp->sp_offset = le16_to_cpu(cmd.offset); 293f988d640SKalle Valo sp->sp_stabletime = le16_to_cpu(cmd.stabletime); 294f988d640SKalle Valo sp->sp_calcontrol = cmd.calcontrol; 295f988d640SKalle Valo sp->sp_extsleepclk = cmd.externalsleepclk; 296f988d640SKalle Valo sp->sp_reserved = le16_to_cpu(cmd.reserved); 297f988d640SKalle Valo } 298f988d640SKalle Valo 299259010c5SPan Bian return ret; 300f988d640SKalle Valo } 301f988d640SKalle Valo 302f988d640SKalle Valo static int lbs_wait_for_ds_awake(struct lbs_private *priv) 303f988d640SKalle Valo { 304f988d640SKalle Valo int ret = 0; 305f988d640SKalle Valo 306f988d640SKalle Valo if (priv->is_deep_sleep) { 307f988d640SKalle Valo if (!wait_event_interruptible_timeout(priv->ds_awake_q, 308f988d640SKalle Valo !priv->is_deep_sleep, (10 * HZ))) { 309f988d640SKalle Valo netdev_err(priv->dev, "ds_awake_q: timer expired\n"); 310f988d640SKalle Valo ret = -1; 311f988d640SKalle Valo } 312f988d640SKalle Valo } 313f988d640SKalle Valo 314f988d640SKalle Valo return ret; 315f988d640SKalle Valo } 316f988d640SKalle Valo 317f988d640SKalle Valo int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep) 318f988d640SKalle Valo { 319f988d640SKalle Valo int ret = 0; 320f988d640SKalle Valo 321f988d640SKalle Valo if (deep_sleep) { 322f988d640SKalle Valo if (priv->is_deep_sleep != 1) { 323f988d640SKalle Valo lbs_deb_cmd("deep sleep: sleep\n"); 324f988d640SKalle Valo BUG_ON(!priv->enter_deep_sleep); 325f988d640SKalle Valo ret = priv->enter_deep_sleep(priv); 326f988d640SKalle Valo if (!ret) { 327f988d640SKalle Valo netif_stop_queue(priv->dev); 328f988d640SKalle Valo netif_carrier_off(priv->dev); 329f988d640SKalle Valo } 330f988d640SKalle Valo } else { 331f988d640SKalle Valo netdev_err(priv->dev, "deep sleep: already enabled\n"); 332f988d640SKalle Valo } 333f988d640SKalle Valo } else { 334f988d640SKalle Valo if (priv->is_deep_sleep) { 335f988d640SKalle Valo lbs_deb_cmd("deep sleep: wakeup\n"); 336f988d640SKalle Valo BUG_ON(!priv->exit_deep_sleep); 337f988d640SKalle Valo ret = priv->exit_deep_sleep(priv); 338f988d640SKalle Valo if (!ret) { 339f988d640SKalle Valo ret = lbs_wait_for_ds_awake(priv); 340f988d640SKalle Valo if (ret) 341f988d640SKalle Valo netdev_err(priv->dev, 342f988d640SKalle Valo "deep sleep: wakeup failed\n"); 343f988d640SKalle Valo } 344f988d640SKalle Valo } 345f988d640SKalle Valo } 346f988d640SKalle Valo 347f988d640SKalle Valo return ret; 348f988d640SKalle Valo } 349f988d640SKalle Valo 350f988d640SKalle Valo static int lbs_ret_host_sleep_activate(struct lbs_private *priv, 351f988d640SKalle Valo unsigned long dummy, 352f988d640SKalle Valo struct cmd_header *cmd) 353f988d640SKalle Valo { 354f988d640SKalle Valo priv->is_host_sleep_activated = 1; 355f988d640SKalle Valo wake_up_interruptible(&priv->host_sleep_q); 3564bc606afSKees Cook 357f988d640SKalle Valo return 0; 358f988d640SKalle Valo } 359f988d640SKalle Valo 360f988d640SKalle Valo int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep) 361f988d640SKalle Valo { 362f988d640SKalle Valo struct cmd_header cmd; 363f988d640SKalle Valo int ret = 0; 364f988d640SKalle Valo uint32_t criteria = EHS_REMOVE_WAKEUP; 365f988d640SKalle Valo 366f988d640SKalle Valo if (host_sleep) { 367f988d640SKalle Valo if (priv->is_host_sleep_activated != 1) { 368f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 369f988d640SKalle Valo ret = lbs_host_sleep_cfg(priv, priv->wol_criteria, 370f988d640SKalle Valo (struct wol_config *)NULL); 371f988d640SKalle Valo if (ret) { 372f988d640SKalle Valo netdev_info(priv->dev, 373f988d640SKalle Valo "Host sleep configuration failed: %d\n", 374f988d640SKalle Valo ret); 375f988d640SKalle Valo return ret; 376f988d640SKalle Valo } 377f988d640SKalle Valo if (priv->psstate == PS_STATE_FULL_POWER) { 378f988d640SKalle Valo ret = __lbs_cmd(priv, 379f988d640SKalle Valo CMD_802_11_HOST_SLEEP_ACTIVATE, 380f988d640SKalle Valo &cmd, 381f988d640SKalle Valo sizeof(cmd), 382f988d640SKalle Valo lbs_ret_host_sleep_activate, 0); 383f988d640SKalle Valo if (ret) 384f988d640SKalle Valo netdev_info(priv->dev, 385f988d640SKalle Valo "HOST_SLEEP_ACTIVATE failed: %d\n", 386f988d640SKalle Valo ret); 387f988d640SKalle Valo } 388f988d640SKalle Valo 389f988d640SKalle Valo if (!wait_event_interruptible_timeout( 390f988d640SKalle Valo priv->host_sleep_q, 391f988d640SKalle Valo priv->is_host_sleep_activated, 392f988d640SKalle Valo (10 * HZ))) { 393f988d640SKalle Valo netdev_err(priv->dev, 394f988d640SKalle Valo "host_sleep_q: timer expired\n"); 395f988d640SKalle Valo ret = -1; 396f988d640SKalle Valo } 397f988d640SKalle Valo } else { 398f988d640SKalle Valo netdev_err(priv->dev, "host sleep: already enabled\n"); 399f988d640SKalle Valo } 400f988d640SKalle Valo } else { 401f988d640SKalle Valo if (priv->is_host_sleep_activated) 402f988d640SKalle Valo ret = lbs_host_sleep_cfg(priv, criteria, 403f988d640SKalle Valo (struct wol_config *)NULL); 404f988d640SKalle Valo } 405f988d640SKalle Valo 406f988d640SKalle Valo return ret; 407f988d640SKalle Valo } 408f988d640SKalle Valo 409f988d640SKalle Valo /** 410f988d640SKalle Valo * lbs_set_snmp_mib - Set an SNMP MIB value 411f988d640SKalle Valo * 412f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 413f988d640SKalle Valo * @oid: The OID to set in the firmware 414f988d640SKalle Valo * @val: Value to set the OID to 415f988d640SKalle Valo * 416f988d640SKalle Valo * returns: 0 on success, error on failure 417f988d640SKalle Valo */ 418f988d640SKalle Valo int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val) 419f988d640SKalle Valo { 420f988d640SKalle Valo struct cmd_ds_802_11_snmp_mib cmd; 421f988d640SKalle Valo int ret; 422f988d640SKalle Valo 423f988d640SKalle Valo memset(&cmd, 0, sizeof (cmd)); 424f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 425f988d640SKalle Valo cmd.action = cpu_to_le16(CMD_ACT_SET); 426f988d640SKalle Valo cmd.oid = cpu_to_le16((u16) oid); 427f988d640SKalle Valo 428f988d640SKalle Valo switch (oid) { 429f988d640SKalle Valo case SNMP_MIB_OID_BSS_TYPE: 430f988d640SKalle Valo cmd.bufsize = cpu_to_le16(sizeof(u8)); 431f988d640SKalle Valo cmd.value[0] = val; 432f988d640SKalle Valo break; 433f988d640SKalle Valo case SNMP_MIB_OID_11D_ENABLE: 434f988d640SKalle Valo case SNMP_MIB_OID_FRAG_THRESHOLD: 435f988d640SKalle Valo case SNMP_MIB_OID_RTS_THRESHOLD: 436f988d640SKalle Valo case SNMP_MIB_OID_SHORT_RETRY_LIMIT: 437f988d640SKalle Valo case SNMP_MIB_OID_LONG_RETRY_LIMIT: 438f988d640SKalle Valo cmd.bufsize = cpu_to_le16(sizeof(u16)); 439f988d640SKalle Valo *((__le16 *)(&cmd.value)) = cpu_to_le16(val); 440f988d640SKalle Valo break; 441f988d640SKalle Valo default: 442f988d640SKalle Valo lbs_deb_cmd("SNMP_CMD: (set) unhandled OID 0x%x\n", oid); 443f988d640SKalle Valo ret = -EINVAL; 444f988d640SKalle Valo goto out; 445f988d640SKalle Valo } 446f988d640SKalle Valo 447f988d640SKalle Valo lbs_deb_cmd("SNMP_CMD: (set) oid 0x%x, oid size 0x%x, value 0x%x\n", 448f988d640SKalle Valo le16_to_cpu(cmd.oid), le16_to_cpu(cmd.bufsize), val); 449f988d640SKalle Valo 450f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); 451f988d640SKalle Valo 452f988d640SKalle Valo out: 453f988d640SKalle Valo return ret; 454f988d640SKalle Valo } 455f988d640SKalle Valo 456f988d640SKalle Valo /** 457f988d640SKalle Valo * lbs_get_snmp_mib - Get an SNMP MIB value 458f988d640SKalle Valo * 459f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 460f988d640SKalle Valo * @oid: The OID to retrieve from the firmware 461f988d640SKalle Valo * @out_val: Location for the returned value 462f988d640SKalle Valo * 463f988d640SKalle Valo * returns: 0 on success, error on failure 464f988d640SKalle Valo */ 465f988d640SKalle Valo int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val) 466f988d640SKalle Valo { 467f988d640SKalle Valo struct cmd_ds_802_11_snmp_mib cmd; 468f988d640SKalle Valo int ret; 469f988d640SKalle Valo 470f988d640SKalle Valo memset(&cmd, 0, sizeof (cmd)); 471f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 472f988d640SKalle Valo cmd.action = cpu_to_le16(CMD_ACT_GET); 473f988d640SKalle Valo cmd.oid = cpu_to_le16(oid); 474f988d640SKalle Valo 475f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); 476f988d640SKalle Valo if (ret) 477f988d640SKalle Valo goto out; 478f988d640SKalle Valo 479f988d640SKalle Valo switch (le16_to_cpu(cmd.bufsize)) { 480f988d640SKalle Valo case sizeof(u8): 481f988d640SKalle Valo *out_val = cmd.value[0]; 482f988d640SKalle Valo break; 483f988d640SKalle Valo case sizeof(u16): 484f988d640SKalle Valo *out_val = le16_to_cpu(*((__le16 *)(&cmd.value))); 485f988d640SKalle Valo break; 486f988d640SKalle Valo default: 487f988d640SKalle Valo lbs_deb_cmd("SNMP_CMD: (get) unhandled OID 0x%x size %d\n", 488f988d640SKalle Valo oid, le16_to_cpu(cmd.bufsize)); 489f988d640SKalle Valo break; 490f988d640SKalle Valo } 491f988d640SKalle Valo 492f988d640SKalle Valo out: 493f988d640SKalle Valo return ret; 494f988d640SKalle Valo } 495f988d640SKalle Valo 496f988d640SKalle Valo /** 497f988d640SKalle Valo * lbs_get_tx_power - Get the min, max, and current TX power 498f988d640SKalle Valo * 499f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 500f988d640SKalle Valo * @curlevel: Current power level in dBm 501f988d640SKalle Valo * @minlevel: Minimum supported power level in dBm (optional) 502f988d640SKalle Valo * @maxlevel: Maximum supported power level in dBm (optional) 503f988d640SKalle Valo * 504f988d640SKalle Valo * returns: 0 on success, error on failure 505f988d640SKalle Valo */ 506f988d640SKalle Valo int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, 507f988d640SKalle Valo s16 *maxlevel) 508f988d640SKalle Valo { 509f988d640SKalle Valo struct cmd_ds_802_11_rf_tx_power cmd; 510f988d640SKalle Valo int ret; 511f988d640SKalle Valo 512f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 513f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 514f988d640SKalle Valo cmd.action = cpu_to_le16(CMD_ACT_GET); 515f988d640SKalle Valo 516f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); 517f988d640SKalle Valo if (ret == 0) { 518f988d640SKalle Valo *curlevel = le16_to_cpu(cmd.curlevel); 519f988d640SKalle Valo if (minlevel) 520f988d640SKalle Valo *minlevel = cmd.minlevel; 521f988d640SKalle Valo if (maxlevel) 522f988d640SKalle Valo *maxlevel = cmd.maxlevel; 523f988d640SKalle Valo } 524f988d640SKalle Valo 525f988d640SKalle Valo return ret; 526f988d640SKalle Valo } 527f988d640SKalle Valo 528f988d640SKalle Valo /** 529f988d640SKalle Valo * lbs_set_tx_power - Set the TX power 530f988d640SKalle Valo * 531f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 532f988d640SKalle Valo * @dbm: The desired power level in dBm 533f988d640SKalle Valo * 534f988d640SKalle Valo * returns: 0 on success, error on failure 535f988d640SKalle Valo */ 536f988d640SKalle Valo int lbs_set_tx_power(struct lbs_private *priv, s16 dbm) 537f988d640SKalle Valo { 538f988d640SKalle Valo struct cmd_ds_802_11_rf_tx_power cmd; 539f988d640SKalle Valo int ret; 540f988d640SKalle Valo 541f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 542f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 543f988d640SKalle Valo cmd.action = cpu_to_le16(CMD_ACT_SET); 544f988d640SKalle Valo cmd.curlevel = cpu_to_le16(dbm); 545f988d640SKalle Valo 546f988d640SKalle Valo lbs_deb_cmd("SET_RF_TX_POWER: %d dBm\n", dbm); 547f988d640SKalle Valo 548f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); 549f988d640SKalle Valo 550f988d640SKalle Valo return ret; 551f988d640SKalle Valo } 552f988d640SKalle Valo 553f988d640SKalle Valo /** 554f988d640SKalle Valo * lbs_set_monitor_mode - Enable or disable monitor mode 555f988d640SKalle Valo * (only implemented on OLPC usb8388 FW) 556f988d640SKalle Valo * 557f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 558f988d640SKalle Valo * @enable: 1 to enable monitor mode, 0 to disable 559f988d640SKalle Valo * 560f988d640SKalle Valo * returns: 0 on success, error on failure 561f988d640SKalle Valo */ 562f988d640SKalle Valo int lbs_set_monitor_mode(struct lbs_private *priv, int enable) 563f988d640SKalle Valo { 564f988d640SKalle Valo struct cmd_ds_802_11_monitor_mode cmd; 565f988d640SKalle Valo int ret; 566f988d640SKalle Valo 567f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 568f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 569f988d640SKalle Valo cmd.action = cpu_to_le16(CMD_ACT_SET); 570f988d640SKalle Valo if (enable) 571f988d640SKalle Valo cmd.mode = cpu_to_le16(0x1); 572f988d640SKalle Valo 573f988d640SKalle Valo lbs_deb_cmd("SET_MONITOR_MODE: %d\n", enable); 574f988d640SKalle Valo 575f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11_MONITOR_MODE, &cmd); 576f988d640SKalle Valo if (ret == 0) { 577f988d640SKalle Valo priv->dev->type = enable ? ARPHRD_IEEE80211_RADIOTAP : 578f988d640SKalle Valo ARPHRD_ETHER; 579f988d640SKalle Valo } 580f988d640SKalle Valo 581f988d640SKalle Valo return ret; 582f988d640SKalle Valo } 583f988d640SKalle Valo 584f988d640SKalle Valo /** 585f988d640SKalle Valo * lbs_get_channel - Get the radio channel 586f988d640SKalle Valo * 587f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 588f988d640SKalle Valo * 589f988d640SKalle Valo * returns: The channel on success, error on failure 590f988d640SKalle Valo */ 591f988d640SKalle Valo static int lbs_get_channel(struct lbs_private *priv) 592f988d640SKalle Valo { 593f988d640SKalle Valo struct cmd_ds_802_11_rf_channel cmd; 594f988d640SKalle Valo int ret = 0; 595f988d640SKalle Valo 596f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 597f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 598f988d640SKalle Valo cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_GET); 599f988d640SKalle Valo 600f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); 601f988d640SKalle Valo if (ret) 602f988d640SKalle Valo goto out; 603f988d640SKalle Valo 604f988d640SKalle Valo ret = le16_to_cpu(cmd.channel); 605f988d640SKalle Valo lbs_deb_cmd("current radio channel is %d\n", ret); 606f988d640SKalle Valo 607f988d640SKalle Valo out: 608f988d640SKalle Valo return ret; 609f988d640SKalle Valo } 610f988d640SKalle Valo 611f988d640SKalle Valo int lbs_update_channel(struct lbs_private *priv) 612f988d640SKalle Valo { 613f988d640SKalle Valo int ret; 614f988d640SKalle Valo 615f988d640SKalle Valo /* the channel in f/w could be out of sync; get the current channel */ 616f988d640SKalle Valo ret = lbs_get_channel(priv); 617f988d640SKalle Valo if (ret > 0) { 618f988d640SKalle Valo priv->channel = ret; 619f988d640SKalle Valo ret = 0; 620f988d640SKalle Valo } 6214bc606afSKees Cook 622f988d640SKalle Valo return ret; 623f988d640SKalle Valo } 624f988d640SKalle Valo 625f988d640SKalle Valo /** 626f988d640SKalle Valo * lbs_set_channel - Set the radio channel 627f988d640SKalle Valo * 628f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 629f988d640SKalle Valo * @channel: The desired channel, or 0 to clear a locked channel 630f988d640SKalle Valo * 631f988d640SKalle Valo * returns: 0 on success, error on failure 632f988d640SKalle Valo */ 633f988d640SKalle Valo int lbs_set_channel(struct lbs_private *priv, u8 channel) 634f988d640SKalle Valo { 635f988d640SKalle Valo struct cmd_ds_802_11_rf_channel cmd; 636f988d640SKalle Valo #ifdef DEBUG 637f988d640SKalle Valo u8 old_channel = priv->channel; 638f988d640SKalle Valo #endif 639f988d640SKalle Valo int ret = 0; 640f988d640SKalle Valo 641f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 642f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 643f988d640SKalle Valo cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); 644f988d640SKalle Valo cmd.channel = cpu_to_le16(channel); 645f988d640SKalle Valo 646f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); 647f988d640SKalle Valo if (ret) 648f988d640SKalle Valo goto out; 649f988d640SKalle Valo 650f988d640SKalle Valo priv->channel = (uint8_t) le16_to_cpu(cmd.channel); 651f988d640SKalle Valo lbs_deb_cmd("channel switch from %d to %d\n", old_channel, 652f988d640SKalle Valo priv->channel); 653f988d640SKalle Valo 654f988d640SKalle Valo out: 655f988d640SKalle Valo return ret; 656f988d640SKalle Valo } 657f988d640SKalle Valo 658f988d640SKalle Valo /** 659f988d640SKalle Valo * lbs_get_rssi - Get current RSSI and noise floor 660f988d640SKalle Valo * 661f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 662f988d640SKalle Valo * @rssi: On successful return, signal level in mBm 663f988d640SKalle Valo * @nf: On successful return, Noise floor 664f988d640SKalle Valo * 665f988d640SKalle Valo * returns: The channel on success, error on failure 666f988d640SKalle Valo */ 667f988d640SKalle Valo int lbs_get_rssi(struct lbs_private *priv, s8 *rssi, s8 *nf) 668f988d640SKalle Valo { 669f988d640SKalle Valo struct cmd_ds_802_11_rssi cmd; 670f988d640SKalle Valo int ret = 0; 671f988d640SKalle Valo 672f988d640SKalle Valo BUG_ON(rssi == NULL); 673f988d640SKalle Valo BUG_ON(nf == NULL); 674f988d640SKalle Valo 675f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 676f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 677f988d640SKalle Valo /* Average SNR over last 8 beacons */ 678f988d640SKalle Valo cmd.n_or_snr = cpu_to_le16(8); 679f988d640SKalle Valo 680f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11_RSSI, &cmd); 681f988d640SKalle Valo if (ret == 0) { 682f988d640SKalle Valo *nf = CAL_NF(le16_to_cpu(cmd.nf)); 683f988d640SKalle Valo *rssi = CAL_RSSI(le16_to_cpu(cmd.n_or_snr), le16_to_cpu(cmd.nf)); 684f988d640SKalle Valo } 685f988d640SKalle Valo 686f988d640SKalle Valo return ret; 687f988d640SKalle Valo } 688f988d640SKalle Valo 689f988d640SKalle Valo /** 690f988d640SKalle Valo * lbs_set_11d_domain_info - Send regulatory and 802.11d domain information 691f988d640SKalle Valo * to the firmware 692f988d640SKalle Valo * 693f988d640SKalle Valo * @priv: pointer to &struct lbs_private 694f988d640SKalle Valo * 695f988d640SKalle Valo * returns: 0 on success, error code on failure 696f988d640SKalle Valo */ 697f988d640SKalle Valo int lbs_set_11d_domain_info(struct lbs_private *priv) 698f988d640SKalle Valo { 699f988d640SKalle Valo struct wiphy *wiphy = priv->wdev->wiphy; 700f988d640SKalle Valo struct ieee80211_supported_band **bands = wiphy->bands; 701f988d640SKalle Valo struct cmd_ds_802_11d_domain_info cmd; 702f988d640SKalle Valo struct mrvl_ie_domain_param_set *domain = &cmd.domain; 703f988d640SKalle Valo struct ieee80211_country_ie_triplet *t; 70457fbcce3SJohannes Berg enum nl80211_band band; 705f988d640SKalle Valo struct ieee80211_channel *ch; 706f988d640SKalle Valo u8 num_triplet = 0; 707f988d640SKalle Valo u8 num_parsed_chan = 0; 708f988d640SKalle Valo u8 first_channel = 0, next_chan = 0, max_pwr = 0; 709f988d640SKalle Valo u8 i, flag = 0; 710f988d640SKalle Valo size_t triplet_size; 711f988d640SKalle Valo int ret = 0; 712f988d640SKalle Valo 713f988d640SKalle Valo if (!priv->country_code[0]) 714f988d640SKalle Valo goto out; 715f988d640SKalle Valo 716f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 717f988d640SKalle Valo cmd.action = cpu_to_le16(CMD_ACT_SET); 718f988d640SKalle Valo 719f988d640SKalle Valo lbs_deb_11d("Setting country code '%c%c'\n", 720f988d640SKalle Valo priv->country_code[0], priv->country_code[1]); 721f988d640SKalle Valo 722f988d640SKalle Valo domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); 723f988d640SKalle Valo 724f988d640SKalle Valo /* Set country code */ 725f988d640SKalle Valo domain->country_code[0] = priv->country_code[0]; 726f988d640SKalle Valo domain->country_code[1] = priv->country_code[1]; 727f988d640SKalle Valo domain->country_code[2] = ' '; 728f988d640SKalle Valo 729f988d640SKalle Valo /* Now set up the channel triplets; firmware is somewhat picky here 730f988d640SKalle Valo * and doesn't validate channel numbers and spans; hence it would 731f988d640SKalle Valo * interpret a triplet of (36, 4, 20) as channels 36, 37, 38, 39. Since 732f988d640SKalle Valo * the last 3 aren't valid channels, the driver is responsible for 733f988d640SKalle Valo * splitting that up into 4 triplet pairs of (36, 1, 20) + (40, 1, 20) 734f988d640SKalle Valo * etc. 735f988d640SKalle Valo */ 736f988d640SKalle Valo for (band = 0; 73757fbcce3SJohannes Berg (band < NUM_NL80211_BANDS) && (num_triplet < MAX_11D_TRIPLETS); 738f988d640SKalle Valo band++) { 739f988d640SKalle Valo 740f988d640SKalle Valo if (!bands[band]) 741f988d640SKalle Valo continue; 742f988d640SKalle Valo 743f988d640SKalle Valo for (i = 0; 744f988d640SKalle Valo (i < bands[band]->n_channels) && (num_triplet < MAX_11D_TRIPLETS); 745f988d640SKalle Valo i++) { 746f988d640SKalle Valo ch = &bands[band]->channels[i]; 747f988d640SKalle Valo if (ch->flags & IEEE80211_CHAN_DISABLED) 748f988d640SKalle Valo continue; 749f988d640SKalle Valo 750f988d640SKalle Valo if (!flag) { 751f988d640SKalle Valo flag = 1; 752f988d640SKalle Valo next_chan = first_channel = (u32) ch->hw_value; 753f988d640SKalle Valo max_pwr = ch->max_power; 754f988d640SKalle Valo num_parsed_chan = 1; 755f988d640SKalle Valo continue; 756f988d640SKalle Valo } 757f988d640SKalle Valo 758f988d640SKalle Valo if ((ch->hw_value == next_chan + 1) && 759f988d640SKalle Valo (ch->max_power == max_pwr)) { 760f988d640SKalle Valo /* Consolidate adjacent channels */ 761f988d640SKalle Valo next_chan++; 762f988d640SKalle Valo num_parsed_chan++; 763f988d640SKalle Valo } else { 764f988d640SKalle Valo /* Add this triplet */ 765f988d640SKalle Valo lbs_deb_11d("11D triplet (%d, %d, %d)\n", 766f988d640SKalle Valo first_channel, num_parsed_chan, 767f988d640SKalle Valo max_pwr); 768f988d640SKalle Valo t = &domain->triplet[num_triplet]; 769f988d640SKalle Valo t->chans.first_channel = first_channel; 770f988d640SKalle Valo t->chans.num_channels = num_parsed_chan; 771f988d640SKalle Valo t->chans.max_power = max_pwr; 772f988d640SKalle Valo num_triplet++; 773f988d640SKalle Valo flag = 0; 774f988d640SKalle Valo } 775f988d640SKalle Valo } 776f988d640SKalle Valo 777f988d640SKalle Valo if (flag) { 778f988d640SKalle Valo /* Add last triplet */ 779f988d640SKalle Valo lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel, 780f988d640SKalle Valo num_parsed_chan, max_pwr); 781f988d640SKalle Valo t = &domain->triplet[num_triplet]; 782f988d640SKalle Valo t->chans.first_channel = first_channel; 783f988d640SKalle Valo t->chans.num_channels = num_parsed_chan; 784f988d640SKalle Valo t->chans.max_power = max_pwr; 785f988d640SKalle Valo num_triplet++; 786f988d640SKalle Valo } 787f988d640SKalle Valo } 788f988d640SKalle Valo 789f988d640SKalle Valo lbs_deb_11d("# triplets %d\n", num_triplet); 790f988d640SKalle Valo 791f988d640SKalle Valo /* Set command header sizes */ 792f988d640SKalle Valo triplet_size = num_triplet * sizeof(struct ieee80211_country_ie_triplet); 793f988d640SKalle Valo domain->header.len = cpu_to_le16(sizeof(domain->country_code) + 794f988d640SKalle Valo triplet_size); 795f988d640SKalle Valo 796f988d640SKalle Valo lbs_deb_hex(LBS_DEB_11D, "802.11D domain param set", 797f988d640SKalle Valo (u8 *) &cmd.domain.country_code, 798f988d640SKalle Valo le16_to_cpu(domain->header.len)); 799f988d640SKalle Valo 800f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd.hdr) + 801f988d640SKalle Valo sizeof(cmd.action) + 802f988d640SKalle Valo sizeof(cmd.domain.header) + 803f988d640SKalle Valo sizeof(cmd.domain.country_code) + 804f988d640SKalle Valo triplet_size); 805f988d640SKalle Valo 806f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd); 807f988d640SKalle Valo 808f988d640SKalle Valo out: 809f988d640SKalle Valo return ret; 810f988d640SKalle Valo } 811f988d640SKalle Valo 812f988d640SKalle Valo /** 813f988d640SKalle Valo * lbs_get_reg - Read a MAC, Baseband, or RF register 814f988d640SKalle Valo * 815f988d640SKalle Valo * @priv: pointer to &struct lbs_private 816f988d640SKalle Valo * @reg: register command, one of CMD_MAC_REG_ACCESS, 817f988d640SKalle Valo * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS 818f988d640SKalle Valo * @offset: byte offset of the register to get 819f988d640SKalle Valo * @value: on success, the value of the register at 'offset' 820f988d640SKalle Valo * 821f988d640SKalle Valo * returns: 0 on success, error code on failure 822f988d640SKalle Valo */ 823f988d640SKalle Valo int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value) 824f988d640SKalle Valo { 825f988d640SKalle Valo struct cmd_ds_reg_access cmd; 826f988d640SKalle Valo int ret = 0; 827f988d640SKalle Valo 828f988d640SKalle Valo BUG_ON(value == NULL); 829f988d640SKalle Valo 830f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 831f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 832f988d640SKalle Valo cmd.action = cpu_to_le16(CMD_ACT_GET); 833f988d640SKalle Valo cmd.offset = cpu_to_le16(offset); 834f988d640SKalle Valo 835f988d640SKalle Valo if (reg != CMD_MAC_REG_ACCESS && 836f988d640SKalle Valo reg != CMD_BBP_REG_ACCESS && 837f988d640SKalle Valo reg != CMD_RF_REG_ACCESS) { 838f988d640SKalle Valo ret = -EINVAL; 839f988d640SKalle Valo goto out; 840f988d640SKalle Valo } 841f988d640SKalle Valo 842f988d640SKalle Valo ret = lbs_cmd_with_response(priv, reg, &cmd); 843f988d640SKalle Valo if (!ret) { 844f988d640SKalle Valo if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) 845f988d640SKalle Valo *value = cmd.value.bbp_rf; 846f988d640SKalle Valo else if (reg == CMD_MAC_REG_ACCESS) 847f988d640SKalle Valo *value = le32_to_cpu(cmd.value.mac); 848f988d640SKalle Valo } 849f988d640SKalle Valo 850f988d640SKalle Valo out: 851f988d640SKalle Valo return ret; 852f988d640SKalle Valo } 853f988d640SKalle Valo 854f988d640SKalle Valo /** 855f988d640SKalle Valo * lbs_set_reg - Write a MAC, Baseband, or RF register 856f988d640SKalle Valo * 857f988d640SKalle Valo * @priv: pointer to &struct lbs_private 858f988d640SKalle Valo * @reg: register command, one of CMD_MAC_REG_ACCESS, 859f988d640SKalle Valo * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS 860f988d640SKalle Valo * @offset: byte offset of the register to set 861f988d640SKalle Valo * @value: the value to write to the register at 'offset' 862f988d640SKalle Valo * 863f988d640SKalle Valo * returns: 0 on success, error code on failure 864f988d640SKalle Valo */ 865f988d640SKalle Valo int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value) 866f988d640SKalle Valo { 867f988d640SKalle Valo struct cmd_ds_reg_access cmd; 868f988d640SKalle Valo int ret = 0; 869f988d640SKalle Valo 870f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 871f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 872f988d640SKalle Valo cmd.action = cpu_to_le16(CMD_ACT_SET); 873f988d640SKalle Valo cmd.offset = cpu_to_le16(offset); 874f988d640SKalle Valo 875f988d640SKalle Valo if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) 876f988d640SKalle Valo cmd.value.bbp_rf = (u8) (value & 0xFF); 877f988d640SKalle Valo else if (reg == CMD_MAC_REG_ACCESS) 878f988d640SKalle Valo cmd.value.mac = cpu_to_le32(value); 879f988d640SKalle Valo else { 880f988d640SKalle Valo ret = -EINVAL; 881f988d640SKalle Valo goto out; 882f988d640SKalle Valo } 883f988d640SKalle Valo 884f988d640SKalle Valo ret = lbs_cmd_with_response(priv, reg, &cmd); 885f988d640SKalle Valo 886f988d640SKalle Valo out: 887f988d640SKalle Valo return ret; 888f988d640SKalle Valo } 889f988d640SKalle Valo 890f988d640SKalle Valo static void lbs_queue_cmd(struct lbs_private *priv, 891f988d640SKalle Valo struct cmd_ctrl_node *cmdnode) 892f988d640SKalle Valo { 893f988d640SKalle Valo unsigned long flags; 894f988d640SKalle Valo int addtail = 1; 895f988d640SKalle Valo 896f988d640SKalle Valo if (!cmdnode) { 897f988d640SKalle Valo lbs_deb_host("QUEUE_CMD: cmdnode is NULL\n"); 8984bc606afSKees Cook return; 899f988d640SKalle Valo } 900f988d640SKalle Valo if (!cmdnode->cmdbuf->size) { 901f988d640SKalle Valo lbs_deb_host("DNLD_CMD: cmd size is zero\n"); 9024bc606afSKees Cook return; 903f988d640SKalle Valo } 904f988d640SKalle Valo cmdnode->result = 0; 905f988d640SKalle Valo 906f988d640SKalle Valo /* Exit_PS command needs to be queued in the header always. */ 907f988d640SKalle Valo if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) { 9080a7701b4SAndreas Kemnade struct cmd_ds_802_11_ps_mode *psm = (void *)cmdnode->cmdbuf; 909f988d640SKalle Valo 910f988d640SKalle Valo if (psm->action == cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { 911f988d640SKalle Valo if (priv->psstate != PS_STATE_FULL_POWER) 912f988d640SKalle Valo addtail = 0; 913f988d640SKalle Valo } 914f988d640SKalle Valo } 915f988d640SKalle Valo 916f988d640SKalle Valo if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_WAKEUP_CONFIRM) 917f988d640SKalle Valo addtail = 0; 918f988d640SKalle Valo 919f988d640SKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 920f988d640SKalle Valo 921f988d640SKalle Valo if (addtail) 922f988d640SKalle Valo list_add_tail(&cmdnode->list, &priv->cmdpendingq); 923f988d640SKalle Valo else 924f988d640SKalle Valo list_add(&cmdnode->list, &priv->cmdpendingq); 925f988d640SKalle Valo 926f988d640SKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 927f988d640SKalle Valo 928f988d640SKalle Valo lbs_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n", 929f988d640SKalle Valo le16_to_cpu(cmdnode->cmdbuf->command)); 930f988d640SKalle Valo } 931f988d640SKalle Valo 932f988d640SKalle Valo static void lbs_submit_command(struct lbs_private *priv, 933f988d640SKalle Valo struct cmd_ctrl_node *cmdnode) 934f988d640SKalle Valo { 935f988d640SKalle Valo unsigned long flags; 936f988d640SKalle Valo struct cmd_header *cmd; 937f988d640SKalle Valo uint16_t cmdsize; 938f988d640SKalle Valo uint16_t command; 939f988d640SKalle Valo int timeo = 3 * HZ; 940f988d640SKalle Valo int ret; 941f988d640SKalle Valo 942f988d640SKalle Valo cmd = cmdnode->cmdbuf; 943f988d640SKalle Valo 944f988d640SKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 945f988d640SKalle Valo priv->seqnum++; 946f988d640SKalle Valo cmd->seqnum = cpu_to_le16(priv->seqnum); 947f988d640SKalle Valo priv->cur_cmd = cmdnode; 948f988d640SKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 949f988d640SKalle Valo 950f988d640SKalle Valo cmdsize = le16_to_cpu(cmd->size); 951f988d640SKalle Valo command = le16_to_cpu(cmd->command); 952f988d640SKalle Valo 953f988d640SKalle Valo /* These commands take longer */ 954f988d640SKalle Valo if (command == CMD_802_11_SCAN || command == CMD_802_11_ASSOCIATE) 955f988d640SKalle Valo timeo = 5 * HZ; 956f988d640SKalle Valo 957f988d640SKalle Valo lbs_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n", 958f988d640SKalle Valo command, le16_to_cpu(cmd->seqnum), cmdsize); 959f988d640SKalle Valo lbs_deb_hex(LBS_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize); 960f988d640SKalle Valo 961f988d640SKalle Valo ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); 962f988d640SKalle Valo 963f988d640SKalle Valo if (ret) { 964f988d640SKalle Valo netdev_info(priv->dev, "DNLD_CMD: hw_host_to_card failed: %d\n", 965f988d640SKalle Valo ret); 966f988d640SKalle Valo /* Reset dnld state machine, report failure */ 967f988d640SKalle Valo priv->dnld_sent = DNLD_RES_RECEIVED; 968f988d640SKalle Valo lbs_complete_command(priv, cmdnode, ret); 969f988d640SKalle Valo } 970f988d640SKalle Valo 971f988d640SKalle Valo if (command == CMD_802_11_DEEP_SLEEP) { 972f988d640SKalle Valo if (priv->is_auto_deep_sleep_enabled) { 973f988d640SKalle Valo priv->wakeup_dev_required = 1; 974f988d640SKalle Valo priv->dnld_sent = 0; 975f988d640SKalle Valo } 976f988d640SKalle Valo priv->is_deep_sleep = 1; 977f988d640SKalle Valo lbs_complete_command(priv, cmdnode, 0); 978f988d640SKalle Valo } else { 979f988d640SKalle Valo /* Setup the timer after transmit command */ 980f988d640SKalle Valo mod_timer(&priv->command_timer, jiffies + timeo); 981f988d640SKalle Valo } 982f988d640SKalle Valo } 983f988d640SKalle Valo 984f988d640SKalle Valo /* 985f988d640SKalle Valo * This function inserts command node to cmdfreeq 986f988d640SKalle Valo * after cleans it. Requires priv->driver_lock held. 987f988d640SKalle Valo */ 988f988d640SKalle Valo static void __lbs_cleanup_and_insert_cmd(struct lbs_private *priv, 989f988d640SKalle Valo struct cmd_ctrl_node *cmdnode) 990f988d640SKalle Valo { 991f988d640SKalle Valo if (!cmdnode) 9924bc606afSKees Cook return; 993f988d640SKalle Valo 994f988d640SKalle Valo cmdnode->callback = NULL; 995f988d640SKalle Valo cmdnode->callback_arg = 0; 996f988d640SKalle Valo 997f988d640SKalle Valo memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); 998f988d640SKalle Valo 999f988d640SKalle Valo list_add_tail(&cmdnode->list, &priv->cmdfreeq); 1000f988d640SKalle Valo } 1001f988d640SKalle Valo 1002f988d640SKalle Valo static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv, 1003f988d640SKalle Valo struct cmd_ctrl_node *ptempcmd) 1004f988d640SKalle Valo { 1005f988d640SKalle Valo unsigned long flags; 1006f988d640SKalle Valo 1007f988d640SKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 1008f988d640SKalle Valo __lbs_cleanup_and_insert_cmd(priv, ptempcmd); 1009f988d640SKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 1010f988d640SKalle Valo } 1011f988d640SKalle Valo 1012f988d640SKalle Valo void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, 1013f988d640SKalle Valo int result) 1014f988d640SKalle Valo { 1015f988d640SKalle Valo /* 1016f988d640SKalle Valo * Normally, commands are removed from cmdpendingq before being 1017f988d640SKalle Valo * submitted. However, we can arrive here on alternative codepaths 1018f988d640SKalle Valo * where the command is still pending. Make sure the command really 1019f988d640SKalle Valo * isn't part of a list at this point. 1020f988d640SKalle Valo */ 1021f988d640SKalle Valo list_del_init(&cmd->list); 1022f988d640SKalle Valo 1023f988d640SKalle Valo cmd->result = result; 1024f988d640SKalle Valo cmd->cmdwaitqwoken = 1; 1025f988d640SKalle Valo wake_up(&cmd->cmdwait_q); 1026f988d640SKalle Valo 1027f988d640SKalle Valo if (!cmd->callback || cmd->callback == lbs_cmd_async_callback) 1028f988d640SKalle Valo __lbs_cleanup_and_insert_cmd(priv, cmd); 1029f988d640SKalle Valo priv->cur_cmd = NULL; 1030f988d640SKalle Valo wake_up(&priv->waitq); 1031f988d640SKalle Valo } 1032f988d640SKalle Valo 1033f988d640SKalle Valo void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, 1034f988d640SKalle Valo int result) 1035f988d640SKalle Valo { 1036f988d640SKalle Valo unsigned long flags; 1037f988d640SKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 1038f988d640SKalle Valo __lbs_complete_command(priv, cmd, result); 1039f988d640SKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 1040f988d640SKalle Valo } 1041f988d640SKalle Valo 1042f988d640SKalle Valo int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on) 1043f988d640SKalle Valo { 1044f988d640SKalle Valo struct cmd_ds_802_11_radio_control cmd; 1045f988d640SKalle Valo int ret = -EINVAL; 1046f988d640SKalle Valo 1047f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 1048f988d640SKalle Valo cmd.action = cpu_to_le16(CMD_ACT_SET); 1049f988d640SKalle Valo cmd.control = 0; 1050f988d640SKalle Valo 1051f988d640SKalle Valo /* Only v8 and below support setting the preamble */ 1052f988d640SKalle Valo if (priv->fwrelease < 0x09000000) { 1053f988d640SKalle Valo switch (preamble) { 1054f988d640SKalle Valo case RADIO_PREAMBLE_SHORT: 1055f988d640SKalle Valo case RADIO_PREAMBLE_AUTO: 1056f988d640SKalle Valo case RADIO_PREAMBLE_LONG: 1057f988d640SKalle Valo cmd.control = cpu_to_le16(preamble); 1058f988d640SKalle Valo break; 1059f988d640SKalle Valo default: 1060f988d640SKalle Valo goto out; 1061f988d640SKalle Valo } 1062f988d640SKalle Valo } 1063f988d640SKalle Valo 1064f988d640SKalle Valo if (radio_on) 1065f988d640SKalle Valo cmd.control |= cpu_to_le16(0x1); 1066f988d640SKalle Valo else { 1067f988d640SKalle Valo cmd.control &= cpu_to_le16(~0x1); 1068f988d640SKalle Valo priv->txpower_cur = 0; 1069f988d640SKalle Valo } 1070f988d640SKalle Valo 1071f988d640SKalle Valo lbs_deb_cmd("RADIO_CONTROL: radio %s, preamble %d\n", 1072f988d640SKalle Valo radio_on ? "ON" : "OFF", preamble); 1073f988d640SKalle Valo 1074f988d640SKalle Valo priv->radio_on = radio_on; 1075f988d640SKalle Valo 1076f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); 1077f988d640SKalle Valo 1078f988d640SKalle Valo out: 1079f988d640SKalle Valo return ret; 1080f988d640SKalle Valo } 1081f988d640SKalle Valo 1082f988d640SKalle Valo void lbs_set_mac_control(struct lbs_private *priv) 1083f988d640SKalle Valo { 1084f988d640SKalle Valo struct cmd_ds_mac_control cmd; 1085f988d640SKalle Valo 1086f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 1087f988d640SKalle Valo cmd.action = cpu_to_le16(priv->mac_control); 1088f988d640SKalle Valo cmd.reserved = 0; 1089f988d640SKalle Valo 1090f988d640SKalle Valo lbs_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd)); 1091f988d640SKalle Valo } 1092f988d640SKalle Valo 1093f988d640SKalle Valo int lbs_set_mac_control_sync(struct lbs_private *priv) 1094f988d640SKalle Valo { 1095f988d640SKalle Valo struct cmd_ds_mac_control cmd; 1096f988d640SKalle Valo int ret = 0; 1097f988d640SKalle Valo 1098f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 1099f988d640SKalle Valo cmd.action = cpu_to_le16(priv->mac_control); 1100f988d640SKalle Valo cmd.reserved = 0; 1101f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_MAC_CONTROL, &cmd); 1102f988d640SKalle Valo 1103f988d640SKalle Valo return ret; 1104f988d640SKalle Valo } 1105f988d640SKalle Valo 1106f988d640SKalle Valo /** 1107f988d640SKalle Valo * lbs_allocate_cmd_buffer - allocates the command buffer and links 1108f988d640SKalle Valo * it to command free queue 1109f988d640SKalle Valo * 1110f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 1111f988d640SKalle Valo * 1112f988d640SKalle Valo * returns: 0 for success or -1 on error 1113f988d640SKalle Valo */ 1114f988d640SKalle Valo int lbs_allocate_cmd_buffer(struct lbs_private *priv) 1115f988d640SKalle Valo { 1116f988d640SKalle Valo int ret = 0; 1117f988d640SKalle Valo u32 bufsize; 1118f988d640SKalle Valo u32 i; 1119f988d640SKalle Valo struct cmd_ctrl_node *cmdarray; 1120f988d640SKalle Valo 1121f988d640SKalle Valo /* Allocate and initialize the command array */ 1122f988d640SKalle Valo bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; 1123f988d640SKalle Valo if (!(cmdarray = kzalloc(bufsize, GFP_KERNEL))) { 1124f988d640SKalle Valo lbs_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n"); 1125f988d640SKalle Valo ret = -1; 1126f988d640SKalle Valo goto done; 1127f988d640SKalle Valo } 1128f988d640SKalle Valo priv->cmd_array = cmdarray; 1129f988d640SKalle Valo 1130f988d640SKalle Valo /* Allocate and initialize each command buffer in the command array */ 1131f988d640SKalle Valo for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { 1132f988d640SKalle Valo cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); 1133f988d640SKalle Valo if (!cmdarray[i].cmdbuf) { 1134f988d640SKalle Valo lbs_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n"); 1135f988d640SKalle Valo ret = -1; 1136f988d640SKalle Valo goto done; 1137f988d640SKalle Valo } 1138f988d640SKalle Valo } 1139f988d640SKalle Valo 1140f988d640SKalle Valo for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { 1141f988d640SKalle Valo init_waitqueue_head(&cmdarray[i].cmdwait_q); 1142f988d640SKalle Valo lbs_cleanup_and_insert_cmd(priv, &cmdarray[i]); 1143f988d640SKalle Valo } 1144f988d640SKalle Valo ret = 0; 1145f988d640SKalle Valo 1146f988d640SKalle Valo done: 1147f988d640SKalle Valo return ret; 1148f988d640SKalle Valo } 1149f988d640SKalle Valo 1150f988d640SKalle Valo /** 1151f988d640SKalle Valo * lbs_free_cmd_buffer - free the command buffer 1152f988d640SKalle Valo * 1153f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 1154f988d640SKalle Valo * 1155f988d640SKalle Valo * returns: 0 for success 1156f988d640SKalle Valo */ 1157f988d640SKalle Valo int lbs_free_cmd_buffer(struct lbs_private *priv) 1158f988d640SKalle Valo { 1159f988d640SKalle Valo struct cmd_ctrl_node *cmdarray; 1160f988d640SKalle Valo unsigned int i; 1161f988d640SKalle Valo 1162f988d640SKalle Valo /* need to check if cmd array is allocated or not */ 1163f988d640SKalle Valo if (priv->cmd_array == NULL) { 1164f988d640SKalle Valo lbs_deb_host("FREE_CMD_BUF: cmd_array is NULL\n"); 1165f988d640SKalle Valo goto done; 1166f988d640SKalle Valo } 1167f988d640SKalle Valo 1168f988d640SKalle Valo cmdarray = priv->cmd_array; 1169f988d640SKalle Valo 1170f988d640SKalle Valo /* Release shared memory buffers */ 1171f988d640SKalle Valo for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { 1172f988d640SKalle Valo if (cmdarray[i].cmdbuf) { 1173f988d640SKalle Valo kfree(cmdarray[i].cmdbuf); 1174f988d640SKalle Valo cmdarray[i].cmdbuf = NULL; 1175f988d640SKalle Valo } 1176f988d640SKalle Valo } 1177f988d640SKalle Valo 1178f988d640SKalle Valo /* Release cmd_ctrl_node */ 1179f988d640SKalle Valo if (priv->cmd_array) { 1180f988d640SKalle Valo kfree(priv->cmd_array); 1181f988d640SKalle Valo priv->cmd_array = NULL; 1182f988d640SKalle Valo } 1183f988d640SKalle Valo 1184f988d640SKalle Valo done: 1185f988d640SKalle Valo return 0; 1186f988d640SKalle Valo } 1187f988d640SKalle Valo 1188f988d640SKalle Valo /** 1189f988d640SKalle Valo * lbs_get_free_cmd_node - gets a free command node if available in 1190f988d640SKalle Valo * command free queue 1191f988d640SKalle Valo * 1192f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 1193f988d640SKalle Valo * 1194f988d640SKalle Valo * returns: A pointer to &cmd_ctrl_node structure on success 1195f988d640SKalle Valo * or %NULL on error 1196f988d640SKalle Valo */ 1197f988d640SKalle Valo static struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv) 1198f988d640SKalle Valo { 1199f988d640SKalle Valo struct cmd_ctrl_node *tempnode; 1200f988d640SKalle Valo unsigned long flags; 1201f988d640SKalle Valo 1202f988d640SKalle Valo if (!priv) 1203f988d640SKalle Valo return NULL; 1204f988d640SKalle Valo 1205f988d640SKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 1206f988d640SKalle Valo 1207f988d640SKalle Valo if (!list_empty(&priv->cmdfreeq)) { 1208f988d640SKalle Valo tempnode = list_first_entry(&priv->cmdfreeq, 1209f988d640SKalle Valo struct cmd_ctrl_node, list); 1210f988d640SKalle Valo list_del_init(&tempnode->list); 1211f988d640SKalle Valo } else { 1212f988d640SKalle Valo lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); 1213f988d640SKalle Valo tempnode = NULL; 1214f988d640SKalle Valo } 1215f988d640SKalle Valo 1216f988d640SKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 1217f988d640SKalle Valo 1218f988d640SKalle Valo return tempnode; 1219f988d640SKalle Valo } 1220f988d640SKalle Valo 1221f988d640SKalle Valo /** 1222f988d640SKalle Valo * lbs_execute_next_command - execute next command in command 1223f988d640SKalle Valo * pending queue. Will put firmware back to PS mode if applicable. 1224f988d640SKalle Valo * 1225f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 1226f988d640SKalle Valo * 1227f988d640SKalle Valo * returns: 0 on success or -1 on error 1228f988d640SKalle Valo */ 1229f988d640SKalle Valo int lbs_execute_next_command(struct lbs_private *priv) 1230f988d640SKalle Valo { 1231f988d640SKalle Valo struct cmd_ctrl_node *cmdnode = NULL; 1232f988d640SKalle Valo struct cmd_header *cmd; 1233f988d640SKalle Valo unsigned long flags; 1234f988d640SKalle Valo int ret = 0; 1235f988d640SKalle Valo 1236f988d640SKalle Valo /* Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the 1237f988d640SKalle Valo * only caller to us is lbs_thread() and we get even when a 1238f988d640SKalle Valo * data packet is received */ 1239f988d640SKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 1240f988d640SKalle Valo 1241f988d640SKalle Valo if (priv->cur_cmd) { 1242f988d640SKalle Valo netdev_alert(priv->dev, 1243f988d640SKalle Valo "EXEC_NEXT_CMD: already processing command!\n"); 1244f988d640SKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 1245f988d640SKalle Valo ret = -1; 1246f988d640SKalle Valo goto done; 1247f988d640SKalle Valo } 1248f988d640SKalle Valo 1249f988d640SKalle Valo if (!list_empty(&priv->cmdpendingq)) { 1250f988d640SKalle Valo cmdnode = list_first_entry(&priv->cmdpendingq, 1251f988d640SKalle Valo struct cmd_ctrl_node, list); 1252f988d640SKalle Valo } 1253f988d640SKalle Valo 1254f988d640SKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 1255f988d640SKalle Valo 1256f988d640SKalle Valo if (cmdnode) { 1257f988d640SKalle Valo cmd = cmdnode->cmdbuf; 1258f988d640SKalle Valo 1259f988d640SKalle Valo if (is_command_allowed_in_ps(le16_to_cpu(cmd->command))) { 1260f988d640SKalle Valo if ((priv->psstate == PS_STATE_SLEEP) || 1261f988d640SKalle Valo (priv->psstate == PS_STATE_PRE_SLEEP)) { 1262f988d640SKalle Valo lbs_deb_host( 1263f988d640SKalle Valo "EXEC_NEXT_CMD: cannot send cmd 0x%04x in psstate %d\n", 1264f988d640SKalle Valo le16_to_cpu(cmd->command), 1265f988d640SKalle Valo priv->psstate); 1266f988d640SKalle Valo ret = -1; 1267f988d640SKalle Valo goto done; 1268f988d640SKalle Valo } 1269f988d640SKalle Valo lbs_deb_host("EXEC_NEXT_CMD: OK to send command " 1270f988d640SKalle Valo "0x%04x in psstate %d\n", 1271f988d640SKalle Valo le16_to_cpu(cmd->command), priv->psstate); 1272f988d640SKalle Valo } else if (priv->psstate != PS_STATE_FULL_POWER) { 1273f988d640SKalle Valo /* 1274f988d640SKalle Valo * 1. Non-PS command: 1275f988d640SKalle Valo * Queue it. set needtowakeup to TRUE if current state 1276f988d640SKalle Valo * is SLEEP, otherwise call send EXIT_PS. 1277f988d640SKalle Valo * 2. PS command but not EXIT_PS: 1278f988d640SKalle Valo * Ignore it. 1279f988d640SKalle Valo * 3. PS command EXIT_PS: 1280f988d640SKalle Valo * Set needtowakeup to TRUE if current state is SLEEP, 1281f988d640SKalle Valo * otherwise send this command down to firmware 1282f988d640SKalle Valo * immediately. 1283f988d640SKalle Valo */ 1284f988d640SKalle Valo if (cmd->command != cpu_to_le16(CMD_802_11_PS_MODE)) { 1285f988d640SKalle Valo /* Prepare to send Exit PS, 1286f988d640SKalle Valo * this non PS command will be sent later */ 1287f988d640SKalle Valo if ((priv->psstate == PS_STATE_SLEEP) 1288f988d640SKalle Valo || (priv->psstate == PS_STATE_PRE_SLEEP) 1289f988d640SKalle Valo ) { 1290f988d640SKalle Valo /* w/ new scheme, it will not reach here. 1291f988d640SKalle Valo since it is blocked in main_thread. */ 1292f988d640SKalle Valo priv->needtowakeup = 1; 1293f988d640SKalle Valo } else { 1294f988d640SKalle Valo lbs_set_ps_mode(priv, 1295f988d640SKalle Valo PS_MODE_ACTION_EXIT_PS, 1296f988d640SKalle Valo false); 1297f988d640SKalle Valo } 1298f988d640SKalle Valo 1299f988d640SKalle Valo ret = 0; 1300f988d640SKalle Valo goto done; 1301f988d640SKalle Valo } else { 1302f988d640SKalle Valo /* 1303f988d640SKalle Valo * PS command. Ignore it if it is not Exit_PS. 1304f988d640SKalle Valo * otherwise send it down immediately. 1305f988d640SKalle Valo */ 13060a7701b4SAndreas Kemnade struct cmd_ds_802_11_ps_mode *psm = (void *)cmd; 1307f988d640SKalle Valo 1308f988d640SKalle Valo lbs_deb_host( 1309f988d640SKalle Valo "EXEC_NEXT_CMD: PS cmd, action 0x%02x\n", 1310f988d640SKalle Valo psm->action); 1311f988d640SKalle Valo if (psm->action != 1312f988d640SKalle Valo cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { 1313f988d640SKalle Valo lbs_deb_host( 1314f988d640SKalle Valo "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n"); 1315f988d640SKalle Valo lbs_complete_command(priv, cmdnode, 0); 1316f988d640SKalle Valo 1317f988d640SKalle Valo ret = 0; 1318f988d640SKalle Valo goto done; 1319f988d640SKalle Valo } 1320f988d640SKalle Valo 1321f988d640SKalle Valo if ((priv->psstate == PS_STATE_SLEEP) || 1322f988d640SKalle Valo (priv->psstate == PS_STATE_PRE_SLEEP)) { 1323f988d640SKalle Valo lbs_deb_host( 1324f988d640SKalle Valo "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n"); 1325f988d640SKalle Valo lbs_complete_command(priv, cmdnode, 0); 1326f988d640SKalle Valo priv->needtowakeup = 1; 1327f988d640SKalle Valo 1328f988d640SKalle Valo ret = 0; 1329f988d640SKalle Valo goto done; 1330f988d640SKalle Valo } 1331f988d640SKalle Valo 1332f988d640SKalle Valo lbs_deb_host( 1333f988d640SKalle Valo "EXEC_NEXT_CMD: sending EXIT_PS\n"); 1334f988d640SKalle Valo } 1335f988d640SKalle Valo } 1336f988d640SKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 1337f988d640SKalle Valo list_del_init(&cmdnode->list); 1338f988d640SKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 1339f988d640SKalle Valo lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", 1340f988d640SKalle Valo le16_to_cpu(cmd->command)); 1341f988d640SKalle Valo lbs_submit_command(priv, cmdnode); 1342f988d640SKalle Valo } else { 1343f988d640SKalle Valo /* 1344f988d640SKalle Valo * check if in power save mode, if yes, put the device back 1345f988d640SKalle Valo * to PS mode 1346f988d640SKalle Valo */ 1347f988d640SKalle Valo if ((priv->psmode != LBS802_11POWERMODECAM) && 1348f988d640SKalle Valo (priv->psstate == PS_STATE_FULL_POWER) && 1349fada24a5SAndreas Kemnade (priv->connect_status == LBS_CONNECTED)) { 1350f988d640SKalle Valo lbs_deb_host( 1351fada24a5SAndreas Kemnade "EXEC_NEXT_CMD: cmdpendingq empty, go back to PS_SLEEP"); 1352f988d640SKalle Valo lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS, 1353f988d640SKalle Valo false); 1354f988d640SKalle Valo } 1355f988d640SKalle Valo } 1356f988d640SKalle Valo 1357f988d640SKalle Valo ret = 0; 1358f988d640SKalle Valo done: 1359f988d640SKalle Valo return ret; 1360f988d640SKalle Valo } 1361f988d640SKalle Valo 1362f988d640SKalle Valo static void lbs_send_confirmsleep(struct lbs_private *priv) 1363f988d640SKalle Valo { 1364f988d640SKalle Valo unsigned long flags; 1365f988d640SKalle Valo int ret; 1366f988d640SKalle Valo 1367f988d640SKalle Valo lbs_deb_hex(LBS_DEB_HOST, "sleep confirm", (u8 *) &confirm_sleep, 1368f988d640SKalle Valo sizeof(confirm_sleep)); 1369f988d640SKalle Valo 1370f988d640SKalle Valo ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &confirm_sleep, 1371f988d640SKalle Valo sizeof(confirm_sleep)); 1372f988d640SKalle Valo if (ret) { 1373f988d640SKalle Valo netdev_alert(priv->dev, "confirm_sleep failed\n"); 13744bc606afSKees Cook return; 1375f988d640SKalle Valo } 1376f988d640SKalle Valo 1377f988d640SKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 1378f988d640SKalle Valo 1379f988d640SKalle Valo /* We don't get a response on the sleep-confirmation */ 1380f988d640SKalle Valo priv->dnld_sent = DNLD_RES_RECEIVED; 1381f988d640SKalle Valo 1382f988d640SKalle Valo if (priv->is_host_sleep_configured) { 1383f988d640SKalle Valo priv->is_host_sleep_activated = 1; 1384f988d640SKalle Valo wake_up_interruptible(&priv->host_sleep_q); 1385f988d640SKalle Valo } 1386f988d640SKalle Valo 1387f988d640SKalle Valo /* If nothing to do, go back to sleep (?) */ 1388f988d640SKalle Valo if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx]) 1389f988d640SKalle Valo priv->psstate = PS_STATE_SLEEP; 1390f988d640SKalle Valo 1391f988d640SKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 1392f988d640SKalle Valo } 1393f988d640SKalle Valo 1394f988d640SKalle Valo /** 1395f988d640SKalle Valo * lbs_ps_confirm_sleep - checks condition and prepares to 1396f988d640SKalle Valo * send sleep confirm command to firmware if ok 1397f988d640SKalle Valo * 1398f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 1399f988d640SKalle Valo * 1400f988d640SKalle Valo * returns: n/a 1401f988d640SKalle Valo */ 1402f988d640SKalle Valo void lbs_ps_confirm_sleep(struct lbs_private *priv) 1403f988d640SKalle Valo { 1404f988d640SKalle Valo unsigned long flags =0; 1405f988d640SKalle Valo int allowed = 1; 1406f988d640SKalle Valo 1407f988d640SKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 1408f988d640SKalle Valo if (priv->dnld_sent) { 1409f988d640SKalle Valo allowed = 0; 1410f988d640SKalle Valo lbs_deb_host("dnld_sent was set\n"); 1411f988d640SKalle Valo } 1412f988d640SKalle Valo 1413f988d640SKalle Valo /* In-progress command? */ 1414f988d640SKalle Valo if (priv->cur_cmd) { 1415f988d640SKalle Valo allowed = 0; 1416f988d640SKalle Valo lbs_deb_host("cur_cmd was set\n"); 1417f988d640SKalle Valo } 1418f988d640SKalle Valo 1419f988d640SKalle Valo /* Pending events or command responses? */ 1420f988d640SKalle Valo if (kfifo_len(&priv->event_fifo) || priv->resp_len[priv->resp_idx]) { 1421f988d640SKalle Valo allowed = 0; 1422f988d640SKalle Valo lbs_deb_host("pending events or command responses\n"); 1423f988d640SKalle Valo } 1424f988d640SKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 1425f988d640SKalle Valo 1426f988d640SKalle Valo if (allowed) { 1427f988d640SKalle Valo lbs_deb_host("sending lbs_ps_confirm_sleep\n"); 1428f988d640SKalle Valo lbs_send_confirmsleep(priv); 1429f988d640SKalle Valo } else { 1430f988d640SKalle Valo lbs_deb_host("sleep confirm has been delayed\n"); 1431f988d640SKalle Valo } 1432f988d640SKalle Valo } 1433f988d640SKalle Valo 1434f988d640SKalle Valo 1435f988d640SKalle Valo /** 1436f988d640SKalle Valo * lbs_set_tpc_cfg - Configures the transmission power control functionality 1437f988d640SKalle Valo * 1438f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 1439f988d640SKalle Valo * @enable: Transmission power control enable 1440f988d640SKalle Valo * @p0: Power level when link quality is good (dBm). 1441f988d640SKalle Valo * @p1: Power level when link quality is fair (dBm). 1442f988d640SKalle Valo * @p2: Power level when link quality is poor (dBm). 1443f988d640SKalle Valo * @usesnr: Use Signal to Noise Ratio in TPC 1444f988d640SKalle Valo * 1445f988d640SKalle Valo * returns: 0 on success 1446f988d640SKalle Valo */ 1447f988d640SKalle Valo int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, 1448f988d640SKalle Valo int8_t p2, int usesnr) 1449f988d640SKalle Valo { 1450f988d640SKalle Valo struct cmd_ds_802_11_tpc_cfg cmd; 1451f988d640SKalle Valo int ret; 1452f988d640SKalle Valo 1453f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 1454f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 1455f988d640SKalle Valo cmd.action = cpu_to_le16(CMD_ACT_SET); 1456f988d640SKalle Valo cmd.enable = !!enable; 1457f988d640SKalle Valo cmd.usesnr = !!usesnr; 1458f988d640SKalle Valo cmd.P0 = p0; 1459f988d640SKalle Valo cmd.P1 = p1; 1460f988d640SKalle Valo cmd.P2 = p2; 1461f988d640SKalle Valo 1462f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd); 1463f988d640SKalle Valo 1464f988d640SKalle Valo return ret; 1465f988d640SKalle Valo } 1466f988d640SKalle Valo 1467f988d640SKalle Valo /** 1468f988d640SKalle Valo * lbs_set_power_adapt_cfg - Configures the power adaptation settings 1469f988d640SKalle Valo * 1470f988d640SKalle Valo * @priv: A pointer to &struct lbs_private structure 1471f988d640SKalle Valo * @enable: Power adaptation enable 1472f988d640SKalle Valo * @p0: Power level for 1, 2, 5.5 and 11 Mbps (dBm). 1473f988d640SKalle Valo * @p1: Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm). 1474f988d640SKalle Valo * @p2: Power level for 48 and 54 Mbps (dBm). 1475f988d640SKalle Valo * 1476f988d640SKalle Valo * returns: 0 on Success 1477f988d640SKalle Valo */ 1478f988d640SKalle Valo 1479f988d640SKalle Valo int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, 1480f988d640SKalle Valo int8_t p1, int8_t p2) 1481f988d640SKalle Valo { 1482f988d640SKalle Valo struct cmd_ds_802_11_pa_cfg cmd; 1483f988d640SKalle Valo int ret; 1484f988d640SKalle Valo 1485f988d640SKalle Valo memset(&cmd, 0, sizeof(cmd)); 1486f988d640SKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 1487f988d640SKalle Valo cmd.action = cpu_to_le16(CMD_ACT_SET); 1488f988d640SKalle Valo cmd.enable = !!enable; 1489f988d640SKalle Valo cmd.P0 = p0; 1490f988d640SKalle Valo cmd.P1 = p1; 1491f988d640SKalle Valo cmd.P2 = p2; 1492f988d640SKalle Valo 1493f988d640SKalle Valo ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd); 1494f988d640SKalle Valo 1495f988d640SKalle Valo return ret; 1496f988d640SKalle Valo } 1497f988d640SKalle Valo 1498f988d640SKalle Valo 1499f988d640SKalle Valo struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, 1500f988d640SKalle Valo uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, 1501f988d640SKalle Valo int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), 1502f988d640SKalle Valo unsigned long callback_arg) 1503f988d640SKalle Valo { 1504f988d640SKalle Valo struct cmd_ctrl_node *cmdnode; 1505f988d640SKalle Valo 1506f988d640SKalle Valo if (priv->surpriseremoved) { 1507f988d640SKalle Valo lbs_deb_host("PREP_CMD: card removed\n"); 1508f988d640SKalle Valo cmdnode = ERR_PTR(-ENOENT); 1509f988d640SKalle Valo goto done; 1510f988d640SKalle Valo } 1511f988d640SKalle Valo 1512f988d640SKalle Valo /* No commands are allowed in Deep Sleep until we toggle the GPIO 1513f988d640SKalle Valo * to wake up the card and it has signaled that it's ready. 1514f988d640SKalle Valo */ 1515f988d640SKalle Valo if (!priv->is_auto_deep_sleep_enabled) { 1516f988d640SKalle Valo if (priv->is_deep_sleep) { 1517f988d640SKalle Valo lbs_deb_cmd("command not allowed in deep sleep\n"); 1518f988d640SKalle Valo cmdnode = ERR_PTR(-EBUSY); 1519f988d640SKalle Valo goto done; 1520f988d640SKalle Valo } 1521f988d640SKalle Valo } 1522f988d640SKalle Valo 1523f988d640SKalle Valo cmdnode = lbs_get_free_cmd_node(priv); 1524f988d640SKalle Valo if (cmdnode == NULL) { 1525f988d640SKalle Valo lbs_deb_host("PREP_CMD: cmdnode is NULL\n"); 1526f988d640SKalle Valo 1527f988d640SKalle Valo /* Wake up main thread to execute next command */ 1528f988d640SKalle Valo wake_up(&priv->waitq); 1529f988d640SKalle Valo cmdnode = ERR_PTR(-ENOBUFS); 1530f988d640SKalle Valo goto done; 1531f988d640SKalle Valo } 1532f988d640SKalle Valo 1533f988d640SKalle Valo cmdnode->callback = callback; 1534f988d640SKalle Valo cmdnode->callback_arg = callback_arg; 1535f988d640SKalle Valo 1536f988d640SKalle Valo /* Copy the incoming command to the buffer */ 1537f988d640SKalle Valo memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); 1538f988d640SKalle Valo 1539f988d640SKalle Valo /* Set command, clean result, move to buffer */ 1540f988d640SKalle Valo cmdnode->cmdbuf->command = cpu_to_le16(command); 1541f988d640SKalle Valo cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); 1542f988d640SKalle Valo cmdnode->cmdbuf->result = 0; 1543f988d640SKalle Valo 1544f988d640SKalle Valo lbs_deb_host("PREP_CMD: command 0x%04x\n", command); 1545f988d640SKalle Valo 1546f988d640SKalle Valo cmdnode->cmdwaitqwoken = 0; 1547f988d640SKalle Valo lbs_queue_cmd(priv, cmdnode); 1548f988d640SKalle Valo wake_up(&priv->waitq); 1549f988d640SKalle Valo 1550f988d640SKalle Valo done: 1551f988d640SKalle Valo return cmdnode; 1552f988d640SKalle Valo } 1553f988d640SKalle Valo 1554f988d640SKalle Valo void lbs_cmd_async(struct lbs_private *priv, uint16_t command, 1555f988d640SKalle Valo struct cmd_header *in_cmd, int in_cmd_size) 1556f988d640SKalle Valo { 1557f988d640SKalle Valo __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, 1558f988d640SKalle Valo lbs_cmd_async_callback, 0); 1559f988d640SKalle Valo } 1560f988d640SKalle Valo 1561f988d640SKalle Valo int __lbs_cmd(struct lbs_private *priv, uint16_t command, 1562f988d640SKalle Valo struct cmd_header *in_cmd, int in_cmd_size, 1563f988d640SKalle Valo int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), 1564f988d640SKalle Valo unsigned long callback_arg) 1565f988d640SKalle Valo { 1566f988d640SKalle Valo struct cmd_ctrl_node *cmdnode; 1567f988d640SKalle Valo unsigned long flags; 1568f988d640SKalle Valo int ret = 0; 1569f988d640SKalle Valo 1570f988d640SKalle Valo cmdnode = __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, 1571f988d640SKalle Valo callback, callback_arg); 1572f988d640SKalle Valo if (IS_ERR(cmdnode)) { 1573f988d640SKalle Valo ret = PTR_ERR(cmdnode); 1574f988d640SKalle Valo goto done; 1575f988d640SKalle Valo } 1576f988d640SKalle Valo 1577f988d640SKalle Valo might_sleep(); 1578f988d640SKalle Valo 1579f988d640SKalle Valo /* 1580f988d640SKalle Valo * Be careful with signals here. A signal may be received as the system 1581f988d640SKalle Valo * goes into suspend or resume. We do not want this to interrupt the 1582f988d640SKalle Valo * command, so we perform an uninterruptible sleep. 1583f988d640SKalle Valo */ 1584f988d640SKalle Valo wait_event(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); 1585f988d640SKalle Valo 1586f988d640SKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 1587f988d640SKalle Valo ret = cmdnode->result; 1588f988d640SKalle Valo if (ret) 1589f988d640SKalle Valo netdev_info(priv->dev, "PREP_CMD: command 0x%04x failed: %d\n", 1590f988d640SKalle Valo command, ret); 1591f988d640SKalle Valo 1592f988d640SKalle Valo __lbs_cleanup_and_insert_cmd(priv, cmdnode); 1593f988d640SKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 1594f988d640SKalle Valo 1595f988d640SKalle Valo done: 1596f988d640SKalle Valo return ret; 1597f988d640SKalle Valo } 1598f988d640SKalle Valo EXPORT_SYMBOL_GPL(__lbs_cmd); 1599