18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This file contains the handling of command. 48c2ecf20Sopenharmony_ci * It prepares command and sends it to firmware when it is ready. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/hardirq.h> 88c2ecf20Sopenharmony_ci#include <linux/kfifo.h> 98c2ecf20Sopenharmony_ci#include <linux/sched.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 128c2ecf20Sopenharmony_ci#include <linux/export.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "decl.h" 158c2ecf20Sopenharmony_ci#include "cfg.h" 168c2ecf20Sopenharmony_ci#include "cmd.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define CAL_NF(nf) ((s32)(-(s32)(nf))) 198c2ecf20Sopenharmony_ci#define CAL_RSSI(snr, nf) ((s32)((s32)(snr) + CAL_NF(nf))) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/** 228c2ecf20Sopenharmony_ci * lbs_cmd_copyback - Simple callback that copies response back into command 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 258c2ecf20Sopenharmony_ci * @extra: A pointer to the original command structure for which 268c2ecf20Sopenharmony_ci * 'resp' is a response 278c2ecf20Sopenharmony_ci * @resp: A pointer to the command response 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * returns: 0 on success, error on failure 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ciint lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, 328c2ecf20Sopenharmony_ci struct cmd_header *resp) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct cmd_header *buf = (void *)extra; 358c2ecf20Sopenharmony_ci uint16_t copy_len; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); 388c2ecf20Sopenharmony_ci memcpy(buf, resp, copy_len); 398c2ecf20Sopenharmony_ci return 0; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_cmd_copyback); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/** 448c2ecf20Sopenharmony_ci * lbs_cmd_async_callback - Simple callback that ignores the result. 458c2ecf20Sopenharmony_ci * Use this if you just want to send a command to the hardware, but don't 468c2ecf20Sopenharmony_ci * care for the result. 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * @priv: ignored 498c2ecf20Sopenharmony_ci * @extra: ignored 508c2ecf20Sopenharmony_ci * @resp: ignored 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * returns: 0 for success 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cistatic int lbs_cmd_async_callback(struct lbs_private *priv, unsigned long extra, 558c2ecf20Sopenharmony_ci struct cmd_header *resp) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci return 0; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/** 628c2ecf20Sopenharmony_ci * is_command_allowed_in_ps - tests if a command is allowed in Power Save mode 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * @cmd: the command ID 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci * returns: 1 if allowed, 0 if not allowed 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_cistatic u8 is_command_allowed_in_ps(u16 cmd) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci switch (cmd) { 718c2ecf20Sopenharmony_ci case CMD_802_11_RSSI: 728c2ecf20Sopenharmony_ci return 1; 738c2ecf20Sopenharmony_ci case CMD_802_11_HOST_SLEEP_CFG: 748c2ecf20Sopenharmony_ci return 1; 758c2ecf20Sopenharmony_ci default: 768c2ecf20Sopenharmony_ci break; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/** 828c2ecf20Sopenharmony_ci * lbs_update_hw_spec - Updates the hardware details like MAC address 838c2ecf20Sopenharmony_ci * and regulatory region 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * returns: 0 on success, error on failure 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ciint lbs_update_hw_spec(struct lbs_private *priv) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct cmd_ds_get_hw_spec cmd; 928c2ecf20Sopenharmony_ci int ret = -1; 938c2ecf20Sopenharmony_ci u32 i; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 968c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 978c2ecf20Sopenharmony_ci memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); 988c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); 998c2ecf20Sopenharmony_ci if (ret) 1008c2ecf20Sopenharmony_ci goto out; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* The firmware release is in an interesting format: the patch 1058c2ecf20Sopenharmony_ci * level is in the most significant nibble ... so fix that: */ 1068c2ecf20Sopenharmony_ci priv->fwrelease = le32_to_cpu(cmd.fwrelease); 1078c2ecf20Sopenharmony_ci priv->fwrelease = (priv->fwrelease << 8) | 1088c2ecf20Sopenharmony_ci (priv->fwrelease >> 24 & 0xff); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* Some firmware capabilities: 1118c2ecf20Sopenharmony_ci * CF card firmware 5.0.16p0: cap 0x00000303 1128c2ecf20Sopenharmony_ci * USB dongle firmware 5.110.17p2: cap 0x00000303 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci netdev_info(priv->dev, "%pM, fw %u.%u.%up%u, cap 0x%08x\n", 1158c2ecf20Sopenharmony_ci cmd.permanentaddr, 1168c2ecf20Sopenharmony_ci priv->fwrelease >> 24 & 0xff, 1178c2ecf20Sopenharmony_ci priv->fwrelease >> 16 & 0xff, 1188c2ecf20Sopenharmony_ci priv->fwrelease >> 8 & 0xff, 1198c2ecf20Sopenharmony_ci priv->fwrelease & 0xff, 1208c2ecf20Sopenharmony_ci priv->fwcapinfo); 1218c2ecf20Sopenharmony_ci lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", 1228c2ecf20Sopenharmony_ci cmd.hwifversion, cmd.version); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Clamp region code to 8-bit since FW spec indicates that it should 1258c2ecf20Sopenharmony_ci * only ever be 8-bit, even though the field size is 16-bit. Some firmware 1268c2ecf20Sopenharmony_ci * returns non-zero high 8 bits here. 1278c2ecf20Sopenharmony_ci * 1288c2ecf20Sopenharmony_ci * Firmware version 4.0.102 used in CF8381 has region code shifted. We 1298c2ecf20Sopenharmony_ci * need to check for this problem and handle it properly. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4) 1328c2ecf20Sopenharmony_ci priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF; 1338c2ecf20Sopenharmony_ci else 1348c2ecf20Sopenharmony_ci priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { 1378c2ecf20Sopenharmony_ci /* use the region code to search for the index */ 1388c2ecf20Sopenharmony_ci if (priv->regioncode == lbs_region_code_to_index[i]) 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* if it's unidentified region code, use the default (USA) */ 1438c2ecf20Sopenharmony_ci if (i >= MRVDRV_MAX_REGION_CODE) { 1448c2ecf20Sopenharmony_ci priv->regioncode = 0x10; 1458c2ecf20Sopenharmony_ci netdev_info(priv->dev, 1468c2ecf20Sopenharmony_ci "unidentified region code; using the default (USA)\n"); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (priv->current_addr[0] == 0xff) 1508c2ecf20Sopenharmony_ci memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (!priv->copied_hwaddr) { 1538c2ecf20Sopenharmony_ci memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN); 1548c2ecf20Sopenharmony_ci if (priv->mesh_dev) 1558c2ecf20Sopenharmony_ci memcpy(priv->mesh_dev->dev_addr, 1568c2ecf20Sopenharmony_ci priv->current_addr, ETH_ALEN); 1578c2ecf20Sopenharmony_ci priv->copied_hwaddr = 1; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ciout: 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy, 1658c2ecf20Sopenharmony_ci struct cmd_header *resp) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci if (priv->is_host_sleep_activated) { 1688c2ecf20Sopenharmony_ci priv->is_host_sleep_configured = 0; 1698c2ecf20Sopenharmony_ci if (priv->psstate == PS_STATE_FULL_POWER) { 1708c2ecf20Sopenharmony_ci priv->is_host_sleep_activated = 0; 1718c2ecf20Sopenharmony_ci wake_up_interruptible(&priv->host_sleep_q); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci } else { 1748c2ecf20Sopenharmony_ci priv->is_host_sleep_configured = 1; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ciint lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria, 1818c2ecf20Sopenharmony_ci struct wol_config *p_wol_config) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct cmd_ds_host_sleep cmd_config; 1848c2ecf20Sopenharmony_ci int ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* 1878c2ecf20Sopenharmony_ci * Certain firmware versions do not support EHS_REMOVE_WAKEUP command 1888c2ecf20Sopenharmony_ci * and the card will return a failure. Since we need to be 1898c2ecf20Sopenharmony_ci * able to reset the mask, in those cases we set a 0 mask instead. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci if (criteria == EHS_REMOVE_WAKEUP && !priv->ehs_remove_supported) 1928c2ecf20Sopenharmony_ci criteria = 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci cmd_config.hdr.size = cpu_to_le16(sizeof(cmd_config)); 1958c2ecf20Sopenharmony_ci cmd_config.criteria = cpu_to_le32(criteria); 1968c2ecf20Sopenharmony_ci cmd_config.gpio = priv->wol_gpio; 1978c2ecf20Sopenharmony_ci cmd_config.gap = priv->wol_gap; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (p_wol_config != NULL) 2008c2ecf20Sopenharmony_ci memcpy((uint8_t *)&cmd_config.wol_conf, (uint8_t *)p_wol_config, 2018c2ecf20Sopenharmony_ci sizeof(struct wol_config)); 2028c2ecf20Sopenharmony_ci else 2038c2ecf20Sopenharmony_ci cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr, 2068c2ecf20Sopenharmony_ci le16_to_cpu(cmd_config.hdr.size), 2078c2ecf20Sopenharmony_ci lbs_ret_host_sleep_cfg, 0); 2088c2ecf20Sopenharmony_ci if (!ret) { 2098c2ecf20Sopenharmony_ci if (p_wol_config) 2108c2ecf20Sopenharmony_ci memcpy((uint8_t *) p_wol_config, 2118c2ecf20Sopenharmony_ci (uint8_t *)&cmd_config.wol_conf, 2128c2ecf20Sopenharmony_ci sizeof(struct wol_config)); 2138c2ecf20Sopenharmony_ci } else { 2148c2ecf20Sopenharmony_ci netdev_info(priv->dev, "HOST_SLEEP_CFG failed %d\n", ret); 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return ret; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(lbs_host_sleep_cfg); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/** 2228c2ecf20Sopenharmony_ci * lbs_set_ps_mode - Sets the Power Save mode 2238c2ecf20Sopenharmony_ci * 2248c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 2258c2ecf20Sopenharmony_ci * @cmd_action: The Power Save operation (PS_MODE_ACTION_ENTER_PS or 2268c2ecf20Sopenharmony_ci * PS_MODE_ACTION_EXIT_PS) 2278c2ecf20Sopenharmony_ci * @block: Whether to block on a response or not 2288c2ecf20Sopenharmony_ci * 2298c2ecf20Sopenharmony_ci * returns: 0 on success, error on failure 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ciint lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct cmd_ds_802_11_ps_mode cmd; 2348c2ecf20Sopenharmony_ci int ret = 0; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 2378c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 2388c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(cmd_action); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (cmd_action == PS_MODE_ACTION_ENTER_PS) { 2418c2ecf20Sopenharmony_ci lbs_deb_cmd("PS_MODE: action ENTER_PS\n"); 2428c2ecf20Sopenharmony_ci cmd.multipledtim = cpu_to_le16(1); /* Default DTIM multiple */ 2438c2ecf20Sopenharmony_ci } else if (cmd_action == PS_MODE_ACTION_EXIT_PS) { 2448c2ecf20Sopenharmony_ci lbs_deb_cmd("PS_MODE: action EXIT_PS\n"); 2458c2ecf20Sopenharmony_ci } else { 2468c2ecf20Sopenharmony_ci /* We don't handle CONFIRM_SLEEP here because it needs to 2478c2ecf20Sopenharmony_ci * be fastpathed to the firmware. 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_ci lbs_deb_cmd("PS_MODE: unknown action 0x%X\n", cmd_action); 2508c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 2518c2ecf20Sopenharmony_ci goto out; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (block) 2558c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_PS_MODE, &cmd); 2568c2ecf20Sopenharmony_ci else 2578c2ecf20Sopenharmony_ci lbs_cmd_async(priv, CMD_802_11_PS_MODE, &cmd.hdr, sizeof (cmd)); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ciout: 2608c2ecf20Sopenharmony_ci return ret; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ciint lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, 2648c2ecf20Sopenharmony_ci struct sleep_params *sp) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct cmd_ds_802_11_sleep_params cmd; 2678c2ecf20Sopenharmony_ci int ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (cmd_action == CMD_ACT_GET) { 2708c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 2718c2ecf20Sopenharmony_ci } else { 2728c2ecf20Sopenharmony_ci cmd.error = cpu_to_le16(sp->sp_error); 2738c2ecf20Sopenharmony_ci cmd.offset = cpu_to_le16(sp->sp_offset); 2748c2ecf20Sopenharmony_ci cmd.stabletime = cpu_to_le16(sp->sp_stabletime); 2758c2ecf20Sopenharmony_ci cmd.calcontrol = sp->sp_calcontrol; 2768c2ecf20Sopenharmony_ci cmd.externalsleepclk = sp->sp_extsleepclk; 2778c2ecf20Sopenharmony_ci cmd.reserved = cpu_to_le16(sp->sp_reserved); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 2808c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(cmd_action); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_SLEEP_PARAMS, &cmd); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (!ret) { 2858c2ecf20Sopenharmony_ci lbs_deb_cmd("error 0x%x, offset 0x%x, stabletime 0x%x, " 2868c2ecf20Sopenharmony_ci "calcontrol 0x%x extsleepclk 0x%x\n", 2878c2ecf20Sopenharmony_ci le16_to_cpu(cmd.error), le16_to_cpu(cmd.offset), 2888c2ecf20Sopenharmony_ci le16_to_cpu(cmd.stabletime), cmd.calcontrol, 2898c2ecf20Sopenharmony_ci cmd.externalsleepclk); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci sp->sp_error = le16_to_cpu(cmd.error); 2928c2ecf20Sopenharmony_ci sp->sp_offset = le16_to_cpu(cmd.offset); 2938c2ecf20Sopenharmony_ci sp->sp_stabletime = le16_to_cpu(cmd.stabletime); 2948c2ecf20Sopenharmony_ci sp->sp_calcontrol = cmd.calcontrol; 2958c2ecf20Sopenharmony_ci sp->sp_extsleepclk = cmd.externalsleepclk; 2968c2ecf20Sopenharmony_ci sp->sp_reserved = le16_to_cpu(cmd.reserved); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return ret; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int lbs_wait_for_ds_awake(struct lbs_private *priv) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int ret = 0; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (priv->is_deep_sleep) { 3078c2ecf20Sopenharmony_ci if (!wait_event_interruptible_timeout(priv->ds_awake_q, 3088c2ecf20Sopenharmony_ci !priv->is_deep_sleep, (10 * HZ))) { 3098c2ecf20Sopenharmony_ci netdev_err(priv->dev, "ds_awake_q: timer expired\n"); 3108c2ecf20Sopenharmony_ci ret = -1; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return ret; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ciint lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci int ret = 0; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (deep_sleep) { 3228c2ecf20Sopenharmony_ci if (priv->is_deep_sleep != 1) { 3238c2ecf20Sopenharmony_ci lbs_deb_cmd("deep sleep: sleep\n"); 3248c2ecf20Sopenharmony_ci BUG_ON(!priv->enter_deep_sleep); 3258c2ecf20Sopenharmony_ci ret = priv->enter_deep_sleep(priv); 3268c2ecf20Sopenharmony_ci if (!ret) { 3278c2ecf20Sopenharmony_ci netif_stop_queue(priv->dev); 3288c2ecf20Sopenharmony_ci netif_carrier_off(priv->dev); 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci } else { 3318c2ecf20Sopenharmony_ci netdev_err(priv->dev, "deep sleep: already enabled\n"); 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci } else { 3348c2ecf20Sopenharmony_ci if (priv->is_deep_sleep) { 3358c2ecf20Sopenharmony_ci lbs_deb_cmd("deep sleep: wakeup\n"); 3368c2ecf20Sopenharmony_ci BUG_ON(!priv->exit_deep_sleep); 3378c2ecf20Sopenharmony_ci ret = priv->exit_deep_sleep(priv); 3388c2ecf20Sopenharmony_ci if (!ret) { 3398c2ecf20Sopenharmony_ci ret = lbs_wait_for_ds_awake(priv); 3408c2ecf20Sopenharmony_ci if (ret) 3418c2ecf20Sopenharmony_ci netdev_err(priv->dev, 3428c2ecf20Sopenharmony_ci "deep sleep: wakeup failed\n"); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return ret; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic int lbs_ret_host_sleep_activate(struct lbs_private *priv, 3518c2ecf20Sopenharmony_ci unsigned long dummy, 3528c2ecf20Sopenharmony_ci struct cmd_header *cmd) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci priv->is_host_sleep_activated = 1; 3558c2ecf20Sopenharmony_ci wake_up_interruptible(&priv->host_sleep_q); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ciint lbs_set_host_sleep(struct lbs_private *priv, int host_sleep) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct cmd_header cmd; 3638c2ecf20Sopenharmony_ci int ret = 0; 3648c2ecf20Sopenharmony_ci uint32_t criteria = EHS_REMOVE_WAKEUP; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (host_sleep) { 3678c2ecf20Sopenharmony_ci if (priv->is_host_sleep_activated != 1) { 3688c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 3698c2ecf20Sopenharmony_ci ret = lbs_host_sleep_cfg(priv, priv->wol_criteria, 3708c2ecf20Sopenharmony_ci (struct wol_config *)NULL); 3718c2ecf20Sopenharmony_ci if (ret) { 3728c2ecf20Sopenharmony_ci netdev_info(priv->dev, 3738c2ecf20Sopenharmony_ci "Host sleep configuration failed: %d\n", 3748c2ecf20Sopenharmony_ci ret); 3758c2ecf20Sopenharmony_ci return ret; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci if (priv->psstate == PS_STATE_FULL_POWER) { 3788c2ecf20Sopenharmony_ci ret = __lbs_cmd(priv, 3798c2ecf20Sopenharmony_ci CMD_802_11_HOST_SLEEP_ACTIVATE, 3808c2ecf20Sopenharmony_ci &cmd, 3818c2ecf20Sopenharmony_ci sizeof(cmd), 3828c2ecf20Sopenharmony_ci lbs_ret_host_sleep_activate, 0); 3838c2ecf20Sopenharmony_ci if (ret) 3848c2ecf20Sopenharmony_ci netdev_info(priv->dev, 3858c2ecf20Sopenharmony_ci "HOST_SLEEP_ACTIVATE failed: %d\n", 3868c2ecf20Sopenharmony_ci ret); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (!wait_event_interruptible_timeout( 3908c2ecf20Sopenharmony_ci priv->host_sleep_q, 3918c2ecf20Sopenharmony_ci priv->is_host_sleep_activated, 3928c2ecf20Sopenharmony_ci (10 * HZ))) { 3938c2ecf20Sopenharmony_ci netdev_err(priv->dev, 3948c2ecf20Sopenharmony_ci "host_sleep_q: timer expired\n"); 3958c2ecf20Sopenharmony_ci ret = -1; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci } else { 3988c2ecf20Sopenharmony_ci netdev_err(priv->dev, "host sleep: already enabled\n"); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci } else { 4018c2ecf20Sopenharmony_ci if (priv->is_host_sleep_activated) 4028c2ecf20Sopenharmony_ci ret = lbs_host_sleep_cfg(priv, criteria, 4038c2ecf20Sopenharmony_ci (struct wol_config *)NULL); 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return ret; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/** 4108c2ecf20Sopenharmony_ci * lbs_set_snmp_mib - Set an SNMP MIB value 4118c2ecf20Sopenharmony_ci * 4128c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 4138c2ecf20Sopenharmony_ci * @oid: The OID to set in the firmware 4148c2ecf20Sopenharmony_ci * @val: Value to set the OID to 4158c2ecf20Sopenharmony_ci * 4168c2ecf20Sopenharmony_ci * returns: 0 on success, error on failure 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ciint lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct cmd_ds_802_11_snmp_mib cmd; 4218c2ecf20Sopenharmony_ci int ret; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof (cmd)); 4248c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 4258c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_SET); 4268c2ecf20Sopenharmony_ci cmd.oid = cpu_to_le16((u16) oid); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci switch (oid) { 4298c2ecf20Sopenharmony_ci case SNMP_MIB_OID_BSS_TYPE: 4308c2ecf20Sopenharmony_ci cmd.bufsize = cpu_to_le16(sizeof(u8)); 4318c2ecf20Sopenharmony_ci cmd.value[0] = val; 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci case SNMP_MIB_OID_11D_ENABLE: 4348c2ecf20Sopenharmony_ci case SNMP_MIB_OID_FRAG_THRESHOLD: 4358c2ecf20Sopenharmony_ci case SNMP_MIB_OID_RTS_THRESHOLD: 4368c2ecf20Sopenharmony_ci case SNMP_MIB_OID_SHORT_RETRY_LIMIT: 4378c2ecf20Sopenharmony_ci case SNMP_MIB_OID_LONG_RETRY_LIMIT: 4388c2ecf20Sopenharmony_ci cmd.bufsize = cpu_to_le16(sizeof(u16)); 4398c2ecf20Sopenharmony_ci *((__le16 *)(&cmd.value)) = cpu_to_le16(val); 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci default: 4428c2ecf20Sopenharmony_ci lbs_deb_cmd("SNMP_CMD: (set) unhandled OID 0x%x\n", oid); 4438c2ecf20Sopenharmony_ci ret = -EINVAL; 4448c2ecf20Sopenharmony_ci goto out; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci lbs_deb_cmd("SNMP_CMD: (set) oid 0x%x, oid size 0x%x, value 0x%x\n", 4488c2ecf20Sopenharmony_ci le16_to_cpu(cmd.oid), le16_to_cpu(cmd.bufsize), val); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ciout: 4538c2ecf20Sopenharmony_ci return ret; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/** 4578c2ecf20Sopenharmony_ci * lbs_get_snmp_mib - Get an SNMP MIB value 4588c2ecf20Sopenharmony_ci * 4598c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 4608c2ecf20Sopenharmony_ci * @oid: The OID to retrieve from the firmware 4618c2ecf20Sopenharmony_ci * @out_val: Location for the returned value 4628c2ecf20Sopenharmony_ci * 4638c2ecf20Sopenharmony_ci * returns: 0 on success, error on failure 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ciint lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct cmd_ds_802_11_snmp_mib cmd; 4688c2ecf20Sopenharmony_ci int ret; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof (cmd)); 4718c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 4728c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_GET); 4738c2ecf20Sopenharmony_ci cmd.oid = cpu_to_le16(oid); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); 4768c2ecf20Sopenharmony_ci if (ret) 4778c2ecf20Sopenharmony_ci goto out; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci switch (le16_to_cpu(cmd.bufsize)) { 4808c2ecf20Sopenharmony_ci case sizeof(u8): 4818c2ecf20Sopenharmony_ci *out_val = cmd.value[0]; 4828c2ecf20Sopenharmony_ci break; 4838c2ecf20Sopenharmony_ci case sizeof(u16): 4848c2ecf20Sopenharmony_ci *out_val = le16_to_cpu(*((__le16 *)(&cmd.value))); 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci default: 4878c2ecf20Sopenharmony_ci lbs_deb_cmd("SNMP_CMD: (get) unhandled OID 0x%x size %d\n", 4888c2ecf20Sopenharmony_ci oid, le16_to_cpu(cmd.bufsize)); 4898c2ecf20Sopenharmony_ci break; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ciout: 4938c2ecf20Sopenharmony_ci return ret; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci/** 4978c2ecf20Sopenharmony_ci * lbs_get_tx_power - Get the min, max, and current TX power 4988c2ecf20Sopenharmony_ci * 4998c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 5008c2ecf20Sopenharmony_ci * @curlevel: Current power level in dBm 5018c2ecf20Sopenharmony_ci * @minlevel: Minimum supported power level in dBm (optional) 5028c2ecf20Sopenharmony_ci * @maxlevel: Maximum supported power level in dBm (optional) 5038c2ecf20Sopenharmony_ci * 5048c2ecf20Sopenharmony_ci * returns: 0 on success, error on failure 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_ciint lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, 5078c2ecf20Sopenharmony_ci s16 *maxlevel) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct cmd_ds_802_11_rf_tx_power cmd; 5108c2ecf20Sopenharmony_ci int ret; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 5138c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 5148c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_GET); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); 5178c2ecf20Sopenharmony_ci if (ret == 0) { 5188c2ecf20Sopenharmony_ci *curlevel = le16_to_cpu(cmd.curlevel); 5198c2ecf20Sopenharmony_ci if (minlevel) 5208c2ecf20Sopenharmony_ci *minlevel = cmd.minlevel; 5218c2ecf20Sopenharmony_ci if (maxlevel) 5228c2ecf20Sopenharmony_ci *maxlevel = cmd.maxlevel; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return ret; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci/** 5298c2ecf20Sopenharmony_ci * lbs_set_tx_power - Set the TX power 5308c2ecf20Sopenharmony_ci * 5318c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 5328c2ecf20Sopenharmony_ci * @dbm: The desired power level in dBm 5338c2ecf20Sopenharmony_ci * 5348c2ecf20Sopenharmony_ci * returns: 0 on success, error on failure 5358c2ecf20Sopenharmony_ci */ 5368c2ecf20Sopenharmony_ciint lbs_set_tx_power(struct lbs_private *priv, s16 dbm) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct cmd_ds_802_11_rf_tx_power cmd; 5398c2ecf20Sopenharmony_ci int ret; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 5428c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 5438c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_SET); 5448c2ecf20Sopenharmony_ci cmd.curlevel = cpu_to_le16(dbm); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci lbs_deb_cmd("SET_RF_TX_POWER: %d dBm\n", dbm); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return ret; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci/** 5548c2ecf20Sopenharmony_ci * lbs_set_monitor_mode - Enable or disable monitor mode 5558c2ecf20Sopenharmony_ci * (only implemented on OLPC usb8388 FW) 5568c2ecf20Sopenharmony_ci * 5578c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 5588c2ecf20Sopenharmony_ci * @enable: 1 to enable monitor mode, 0 to disable 5598c2ecf20Sopenharmony_ci * 5608c2ecf20Sopenharmony_ci * returns: 0 on success, error on failure 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_ciint lbs_set_monitor_mode(struct lbs_private *priv, int enable) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct cmd_ds_802_11_monitor_mode cmd; 5658c2ecf20Sopenharmony_ci int ret; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 5688c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 5698c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_SET); 5708c2ecf20Sopenharmony_ci if (enable) 5718c2ecf20Sopenharmony_ci cmd.mode = cpu_to_le16(0x1); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci lbs_deb_cmd("SET_MONITOR_MODE: %d\n", enable); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_MONITOR_MODE, &cmd); 5768c2ecf20Sopenharmony_ci if (ret == 0) { 5778c2ecf20Sopenharmony_ci priv->dev->type = enable ? ARPHRD_IEEE80211_RADIOTAP : 5788c2ecf20Sopenharmony_ci ARPHRD_ETHER; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci return ret; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci/** 5858c2ecf20Sopenharmony_ci * lbs_get_channel - Get the radio channel 5868c2ecf20Sopenharmony_ci * 5878c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 5888c2ecf20Sopenharmony_ci * 5898c2ecf20Sopenharmony_ci * returns: The channel on success, error on failure 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_cistatic int lbs_get_channel(struct lbs_private *priv) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci struct cmd_ds_802_11_rf_channel cmd; 5948c2ecf20Sopenharmony_ci int ret = 0; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 5978c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 5988c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_GET); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); 6018c2ecf20Sopenharmony_ci if (ret) 6028c2ecf20Sopenharmony_ci goto out; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci ret = le16_to_cpu(cmd.channel); 6058c2ecf20Sopenharmony_ci lbs_deb_cmd("current radio channel is %d\n", ret); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ciout: 6088c2ecf20Sopenharmony_ci return ret; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ciint lbs_update_channel(struct lbs_private *priv) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci int ret; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* the channel in f/w could be out of sync; get the current channel */ 6168c2ecf20Sopenharmony_ci ret = lbs_get_channel(priv); 6178c2ecf20Sopenharmony_ci if (ret > 0) { 6188c2ecf20Sopenharmony_ci priv->channel = ret; 6198c2ecf20Sopenharmony_ci ret = 0; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return ret; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci/** 6268c2ecf20Sopenharmony_ci * lbs_set_channel - Set the radio channel 6278c2ecf20Sopenharmony_ci * 6288c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 6298c2ecf20Sopenharmony_ci * @channel: The desired channel, or 0 to clear a locked channel 6308c2ecf20Sopenharmony_ci * 6318c2ecf20Sopenharmony_ci * returns: 0 on success, error on failure 6328c2ecf20Sopenharmony_ci */ 6338c2ecf20Sopenharmony_ciint lbs_set_channel(struct lbs_private *priv, u8 channel) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct cmd_ds_802_11_rf_channel cmd; 6368c2ecf20Sopenharmony_ci#ifdef DEBUG 6378c2ecf20Sopenharmony_ci u8 old_channel = priv->channel; 6388c2ecf20Sopenharmony_ci#endif 6398c2ecf20Sopenharmony_ci int ret = 0; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 6428c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 6438c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); 6448c2ecf20Sopenharmony_ci cmd.channel = cpu_to_le16(channel); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); 6478c2ecf20Sopenharmony_ci if (ret) 6488c2ecf20Sopenharmony_ci goto out; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci priv->channel = (uint8_t) le16_to_cpu(cmd.channel); 6518c2ecf20Sopenharmony_ci lbs_deb_cmd("channel switch from %d to %d\n", old_channel, 6528c2ecf20Sopenharmony_ci priv->channel); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ciout: 6558c2ecf20Sopenharmony_ci return ret; 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci/** 6598c2ecf20Sopenharmony_ci * lbs_get_rssi - Get current RSSI and noise floor 6608c2ecf20Sopenharmony_ci * 6618c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 6628c2ecf20Sopenharmony_ci * @rssi: On successful return, signal level in mBm 6638c2ecf20Sopenharmony_ci * @nf: On successful return, Noise floor 6648c2ecf20Sopenharmony_ci * 6658c2ecf20Sopenharmony_ci * returns: The channel on success, error on failure 6668c2ecf20Sopenharmony_ci */ 6678c2ecf20Sopenharmony_ciint lbs_get_rssi(struct lbs_private *priv, s8 *rssi, s8 *nf) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci struct cmd_ds_802_11_rssi cmd; 6708c2ecf20Sopenharmony_ci int ret = 0; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci BUG_ON(rssi == NULL); 6738c2ecf20Sopenharmony_ci BUG_ON(nf == NULL); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 6768c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 6778c2ecf20Sopenharmony_ci /* Average SNR over last 8 beacons */ 6788c2ecf20Sopenharmony_ci cmd.n_or_snr = cpu_to_le16(8); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_RSSI, &cmd); 6818c2ecf20Sopenharmony_ci if (ret == 0) { 6828c2ecf20Sopenharmony_ci *nf = CAL_NF(le16_to_cpu(cmd.nf)); 6838c2ecf20Sopenharmony_ci *rssi = CAL_RSSI(le16_to_cpu(cmd.n_or_snr), le16_to_cpu(cmd.nf)); 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci return ret; 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci/** 6908c2ecf20Sopenharmony_ci * lbs_set_11d_domain_info - Send regulatory and 802.11d domain information 6918c2ecf20Sopenharmony_ci * to the firmware 6928c2ecf20Sopenharmony_ci * 6938c2ecf20Sopenharmony_ci * @priv: pointer to &struct lbs_private 6948c2ecf20Sopenharmony_ci * 6958c2ecf20Sopenharmony_ci * returns: 0 on success, error code on failure 6968c2ecf20Sopenharmony_ci*/ 6978c2ecf20Sopenharmony_ciint lbs_set_11d_domain_info(struct lbs_private *priv) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci struct wiphy *wiphy = priv->wdev->wiphy; 7008c2ecf20Sopenharmony_ci struct ieee80211_supported_band **bands = wiphy->bands; 7018c2ecf20Sopenharmony_ci struct cmd_ds_802_11d_domain_info cmd; 7028c2ecf20Sopenharmony_ci struct mrvl_ie_domain_param_set *domain = &cmd.domain; 7038c2ecf20Sopenharmony_ci struct ieee80211_country_ie_triplet *t; 7048c2ecf20Sopenharmony_ci enum nl80211_band band; 7058c2ecf20Sopenharmony_ci struct ieee80211_channel *ch; 7068c2ecf20Sopenharmony_ci u8 num_triplet = 0; 7078c2ecf20Sopenharmony_ci u8 num_parsed_chan = 0; 7088c2ecf20Sopenharmony_ci u8 first_channel = 0, next_chan = 0, max_pwr = 0; 7098c2ecf20Sopenharmony_ci u8 i, flag = 0; 7108c2ecf20Sopenharmony_ci size_t triplet_size; 7118c2ecf20Sopenharmony_ci int ret = 0; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (!priv->country_code[0]) 7148c2ecf20Sopenharmony_ci goto out; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 7178c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_SET); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci lbs_deb_11d("Setting country code '%c%c'\n", 7208c2ecf20Sopenharmony_ci priv->country_code[0], priv->country_code[1]); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* Set country code */ 7258c2ecf20Sopenharmony_ci domain->country_code[0] = priv->country_code[0]; 7268c2ecf20Sopenharmony_ci domain->country_code[1] = priv->country_code[1]; 7278c2ecf20Sopenharmony_ci domain->country_code[2] = ' '; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* Now set up the channel triplets; firmware is somewhat picky here 7308c2ecf20Sopenharmony_ci * and doesn't validate channel numbers and spans; hence it would 7318c2ecf20Sopenharmony_ci * interpret a triplet of (36, 4, 20) as channels 36, 37, 38, 39. Since 7328c2ecf20Sopenharmony_ci * the last 3 aren't valid channels, the driver is responsible for 7338c2ecf20Sopenharmony_ci * splitting that up into 4 triplet pairs of (36, 1, 20) + (40, 1, 20) 7348c2ecf20Sopenharmony_ci * etc. 7358c2ecf20Sopenharmony_ci */ 7368c2ecf20Sopenharmony_ci for (band = 0; 7378c2ecf20Sopenharmony_ci (band < NUM_NL80211_BANDS) && (num_triplet < MAX_11D_TRIPLETS); 7388c2ecf20Sopenharmony_ci band++) { 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (!bands[band]) 7418c2ecf20Sopenharmony_ci continue; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci for (i = 0; 7448c2ecf20Sopenharmony_ci (i < bands[band]->n_channels) && (num_triplet < MAX_11D_TRIPLETS); 7458c2ecf20Sopenharmony_ci i++) { 7468c2ecf20Sopenharmony_ci ch = &bands[band]->channels[i]; 7478c2ecf20Sopenharmony_ci if (ch->flags & IEEE80211_CHAN_DISABLED) 7488c2ecf20Sopenharmony_ci continue; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (!flag) { 7518c2ecf20Sopenharmony_ci flag = 1; 7528c2ecf20Sopenharmony_ci next_chan = first_channel = (u32) ch->hw_value; 7538c2ecf20Sopenharmony_ci max_pwr = ch->max_power; 7548c2ecf20Sopenharmony_ci num_parsed_chan = 1; 7558c2ecf20Sopenharmony_ci continue; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if ((ch->hw_value == next_chan + 1) && 7598c2ecf20Sopenharmony_ci (ch->max_power == max_pwr)) { 7608c2ecf20Sopenharmony_ci /* Consolidate adjacent channels */ 7618c2ecf20Sopenharmony_ci next_chan++; 7628c2ecf20Sopenharmony_ci num_parsed_chan++; 7638c2ecf20Sopenharmony_ci } else { 7648c2ecf20Sopenharmony_ci /* Add this triplet */ 7658c2ecf20Sopenharmony_ci lbs_deb_11d("11D triplet (%d, %d, %d)\n", 7668c2ecf20Sopenharmony_ci first_channel, num_parsed_chan, 7678c2ecf20Sopenharmony_ci max_pwr); 7688c2ecf20Sopenharmony_ci t = &domain->triplet[num_triplet]; 7698c2ecf20Sopenharmony_ci t->chans.first_channel = first_channel; 7708c2ecf20Sopenharmony_ci t->chans.num_channels = num_parsed_chan; 7718c2ecf20Sopenharmony_ci t->chans.max_power = max_pwr; 7728c2ecf20Sopenharmony_ci num_triplet++; 7738c2ecf20Sopenharmony_ci flag = 0; 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (flag) { 7788c2ecf20Sopenharmony_ci /* Add last triplet */ 7798c2ecf20Sopenharmony_ci lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel, 7808c2ecf20Sopenharmony_ci num_parsed_chan, max_pwr); 7818c2ecf20Sopenharmony_ci t = &domain->triplet[num_triplet]; 7828c2ecf20Sopenharmony_ci t->chans.first_channel = first_channel; 7838c2ecf20Sopenharmony_ci t->chans.num_channels = num_parsed_chan; 7848c2ecf20Sopenharmony_ci t->chans.max_power = max_pwr; 7858c2ecf20Sopenharmony_ci num_triplet++; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci lbs_deb_11d("# triplets %d\n", num_triplet); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* Set command header sizes */ 7928c2ecf20Sopenharmony_ci triplet_size = num_triplet * sizeof(struct ieee80211_country_ie_triplet); 7938c2ecf20Sopenharmony_ci domain->header.len = cpu_to_le16(sizeof(domain->country_code) + 7948c2ecf20Sopenharmony_ci triplet_size); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci lbs_deb_hex(LBS_DEB_11D, "802.11D domain param set", 7978c2ecf20Sopenharmony_ci (u8 *) &cmd.domain.country_code, 7988c2ecf20Sopenharmony_ci le16_to_cpu(domain->header.len)); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd.hdr) + 8018c2ecf20Sopenharmony_ci sizeof(cmd.action) + 8028c2ecf20Sopenharmony_ci sizeof(cmd.domain.header) + 8038c2ecf20Sopenharmony_ci sizeof(cmd.domain.country_code) + 8048c2ecf20Sopenharmony_ci triplet_size); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ciout: 8098c2ecf20Sopenharmony_ci return ret; 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci/** 8138c2ecf20Sopenharmony_ci * lbs_get_reg - Read a MAC, Baseband, or RF register 8148c2ecf20Sopenharmony_ci * 8158c2ecf20Sopenharmony_ci * @priv: pointer to &struct lbs_private 8168c2ecf20Sopenharmony_ci * @reg: register command, one of CMD_MAC_REG_ACCESS, 8178c2ecf20Sopenharmony_ci * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS 8188c2ecf20Sopenharmony_ci * @offset: byte offset of the register to get 8198c2ecf20Sopenharmony_ci * @value: on success, the value of the register at 'offset' 8208c2ecf20Sopenharmony_ci * 8218c2ecf20Sopenharmony_ci * returns: 0 on success, error code on failure 8228c2ecf20Sopenharmony_ci*/ 8238c2ecf20Sopenharmony_ciint lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct cmd_ds_reg_access cmd; 8268c2ecf20Sopenharmony_ci int ret = 0; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci BUG_ON(value == NULL); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 8318c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 8328c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_GET); 8338c2ecf20Sopenharmony_ci cmd.offset = cpu_to_le16(offset); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (reg != CMD_MAC_REG_ACCESS && 8368c2ecf20Sopenharmony_ci reg != CMD_BBP_REG_ACCESS && 8378c2ecf20Sopenharmony_ci reg != CMD_RF_REG_ACCESS) { 8388c2ecf20Sopenharmony_ci ret = -EINVAL; 8398c2ecf20Sopenharmony_ci goto out; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, reg, &cmd); 8438c2ecf20Sopenharmony_ci if (!ret) { 8448c2ecf20Sopenharmony_ci if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) 8458c2ecf20Sopenharmony_ci *value = cmd.value.bbp_rf; 8468c2ecf20Sopenharmony_ci else if (reg == CMD_MAC_REG_ACCESS) 8478c2ecf20Sopenharmony_ci *value = le32_to_cpu(cmd.value.mac); 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ciout: 8518c2ecf20Sopenharmony_ci return ret; 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci/** 8558c2ecf20Sopenharmony_ci * lbs_set_reg - Write a MAC, Baseband, or RF register 8568c2ecf20Sopenharmony_ci * 8578c2ecf20Sopenharmony_ci * @priv: pointer to &struct lbs_private 8588c2ecf20Sopenharmony_ci * @reg: register command, one of CMD_MAC_REG_ACCESS, 8598c2ecf20Sopenharmony_ci * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS 8608c2ecf20Sopenharmony_ci * @offset: byte offset of the register to set 8618c2ecf20Sopenharmony_ci * @value: the value to write to the register at 'offset' 8628c2ecf20Sopenharmony_ci * 8638c2ecf20Sopenharmony_ci * returns: 0 on success, error code on failure 8648c2ecf20Sopenharmony_ci*/ 8658c2ecf20Sopenharmony_ciint lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci struct cmd_ds_reg_access cmd; 8688c2ecf20Sopenharmony_ci int ret = 0; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 8718c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 8728c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_SET); 8738c2ecf20Sopenharmony_ci cmd.offset = cpu_to_le16(offset); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) 8768c2ecf20Sopenharmony_ci cmd.value.bbp_rf = (u8) (value & 0xFF); 8778c2ecf20Sopenharmony_ci else if (reg == CMD_MAC_REG_ACCESS) 8788c2ecf20Sopenharmony_ci cmd.value.mac = cpu_to_le32(value); 8798c2ecf20Sopenharmony_ci else { 8808c2ecf20Sopenharmony_ci ret = -EINVAL; 8818c2ecf20Sopenharmony_ci goto out; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, reg, &cmd); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ciout: 8878c2ecf20Sopenharmony_ci return ret; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic void lbs_queue_cmd(struct lbs_private *priv, 8918c2ecf20Sopenharmony_ci struct cmd_ctrl_node *cmdnode) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci unsigned long flags; 8948c2ecf20Sopenharmony_ci int addtail = 1; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (!cmdnode) { 8978c2ecf20Sopenharmony_ci lbs_deb_host("QUEUE_CMD: cmdnode is NULL\n"); 8988c2ecf20Sopenharmony_ci return; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci if (!cmdnode->cmdbuf->size) { 9018c2ecf20Sopenharmony_ci lbs_deb_host("DNLD_CMD: cmd size is zero\n"); 9028c2ecf20Sopenharmony_ci return; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci cmdnode->result = 0; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci /* Exit_PS command needs to be queued in the header always. */ 9078c2ecf20Sopenharmony_ci if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) { 9088c2ecf20Sopenharmony_ci struct cmd_ds_802_11_ps_mode *psm = (void *)cmdnode->cmdbuf; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci if (psm->action == cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { 9118c2ecf20Sopenharmony_ci if (priv->psstate != PS_STATE_FULL_POWER) 9128c2ecf20Sopenharmony_ci addtail = 0; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_WAKEUP_CONFIRM) 9178c2ecf20Sopenharmony_ci addtail = 0; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (addtail) 9228c2ecf20Sopenharmony_ci list_add_tail(&cmdnode->list, &priv->cmdpendingq); 9238c2ecf20Sopenharmony_ci else 9248c2ecf20Sopenharmony_ci list_add(&cmdnode->list, &priv->cmdpendingq); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci lbs_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n", 9298c2ecf20Sopenharmony_ci le16_to_cpu(cmdnode->cmdbuf->command)); 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic void lbs_submit_command(struct lbs_private *priv, 9338c2ecf20Sopenharmony_ci struct cmd_ctrl_node *cmdnode) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci unsigned long flags; 9368c2ecf20Sopenharmony_ci struct cmd_header *cmd; 9378c2ecf20Sopenharmony_ci uint16_t cmdsize; 9388c2ecf20Sopenharmony_ci uint16_t command; 9398c2ecf20Sopenharmony_ci int timeo = 3 * HZ; 9408c2ecf20Sopenharmony_ci int ret; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci cmd = cmdnode->cmdbuf; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 9458c2ecf20Sopenharmony_ci priv->seqnum++; 9468c2ecf20Sopenharmony_ci cmd->seqnum = cpu_to_le16(priv->seqnum); 9478c2ecf20Sopenharmony_ci priv->cur_cmd = cmdnode; 9488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci cmdsize = le16_to_cpu(cmd->size); 9518c2ecf20Sopenharmony_ci command = le16_to_cpu(cmd->command); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci /* These commands take longer */ 9548c2ecf20Sopenharmony_ci if (command == CMD_802_11_SCAN || command == CMD_802_11_ASSOCIATE) 9558c2ecf20Sopenharmony_ci timeo = 5 * HZ; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci lbs_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n", 9588c2ecf20Sopenharmony_ci command, le16_to_cpu(cmd->seqnum), cmdsize); 9598c2ecf20Sopenharmony_ci lbs_deb_hex(LBS_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci if (ret) { 9648c2ecf20Sopenharmony_ci netdev_info(priv->dev, "DNLD_CMD: hw_host_to_card failed: %d\n", 9658c2ecf20Sopenharmony_ci ret); 9668c2ecf20Sopenharmony_ci /* Reset dnld state machine, report failure */ 9678c2ecf20Sopenharmony_ci priv->dnld_sent = DNLD_RES_RECEIVED; 9688c2ecf20Sopenharmony_ci lbs_complete_command(priv, cmdnode, ret); 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci if (command == CMD_802_11_DEEP_SLEEP) { 9728c2ecf20Sopenharmony_ci if (priv->is_auto_deep_sleep_enabled) { 9738c2ecf20Sopenharmony_ci priv->wakeup_dev_required = 1; 9748c2ecf20Sopenharmony_ci priv->dnld_sent = 0; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci priv->is_deep_sleep = 1; 9778c2ecf20Sopenharmony_ci lbs_complete_command(priv, cmdnode, 0); 9788c2ecf20Sopenharmony_ci } else { 9798c2ecf20Sopenharmony_ci /* Setup the timer after transmit command */ 9808c2ecf20Sopenharmony_ci mod_timer(&priv->command_timer, jiffies + timeo); 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci/* 9858c2ecf20Sopenharmony_ci * This function inserts command node to cmdfreeq 9868c2ecf20Sopenharmony_ci * after cleans it. Requires priv->driver_lock held. 9878c2ecf20Sopenharmony_ci */ 9888c2ecf20Sopenharmony_cistatic void __lbs_cleanup_and_insert_cmd(struct lbs_private *priv, 9898c2ecf20Sopenharmony_ci struct cmd_ctrl_node *cmdnode) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci if (!cmdnode) 9928c2ecf20Sopenharmony_ci return; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci cmdnode->callback = NULL; 9958c2ecf20Sopenharmony_ci cmdnode->callback_arg = 0; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci list_add_tail(&cmdnode->list, &priv->cmdfreeq); 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic void lbs_cleanup_and_insert_cmd(struct lbs_private *priv, 10038c2ecf20Sopenharmony_ci struct cmd_ctrl_node *ptempcmd) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci unsigned long flags; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 10088c2ecf20Sopenharmony_ci __lbs_cleanup_and_insert_cmd(priv, ptempcmd); 10098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_civoid __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, 10138c2ecf20Sopenharmony_ci int result) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci /* 10168c2ecf20Sopenharmony_ci * Normally, commands are removed from cmdpendingq before being 10178c2ecf20Sopenharmony_ci * submitted. However, we can arrive here on alternative codepaths 10188c2ecf20Sopenharmony_ci * where the command is still pending. Make sure the command really 10198c2ecf20Sopenharmony_ci * isn't part of a list at this point. 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_ci list_del_init(&cmd->list); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci cmd->result = result; 10248c2ecf20Sopenharmony_ci cmd->cmdwaitqwoken = 1; 10258c2ecf20Sopenharmony_ci wake_up(&cmd->cmdwait_q); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci if (!cmd->callback || cmd->callback == lbs_cmd_async_callback) 10288c2ecf20Sopenharmony_ci __lbs_cleanup_and_insert_cmd(priv, cmd); 10298c2ecf20Sopenharmony_ci priv->cur_cmd = NULL; 10308c2ecf20Sopenharmony_ci wake_up(&priv->waitq); 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_civoid lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, 10348c2ecf20Sopenharmony_ci int result) 10358c2ecf20Sopenharmony_ci{ 10368c2ecf20Sopenharmony_ci unsigned long flags; 10378c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 10388c2ecf20Sopenharmony_ci __lbs_complete_command(priv, cmd, result); 10398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 10408c2ecf20Sopenharmony_ci} 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ciint lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on) 10438c2ecf20Sopenharmony_ci{ 10448c2ecf20Sopenharmony_ci struct cmd_ds_802_11_radio_control cmd; 10458c2ecf20Sopenharmony_ci int ret = -EINVAL; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 10488c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_SET); 10498c2ecf20Sopenharmony_ci cmd.control = 0; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* Only v8 and below support setting the preamble */ 10528c2ecf20Sopenharmony_ci if (priv->fwrelease < 0x09000000) { 10538c2ecf20Sopenharmony_ci switch (preamble) { 10548c2ecf20Sopenharmony_ci case RADIO_PREAMBLE_SHORT: 10558c2ecf20Sopenharmony_ci case RADIO_PREAMBLE_AUTO: 10568c2ecf20Sopenharmony_ci case RADIO_PREAMBLE_LONG: 10578c2ecf20Sopenharmony_ci cmd.control = cpu_to_le16(preamble); 10588c2ecf20Sopenharmony_ci break; 10598c2ecf20Sopenharmony_ci default: 10608c2ecf20Sopenharmony_ci goto out; 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci if (radio_on) 10658c2ecf20Sopenharmony_ci cmd.control |= cpu_to_le16(0x1); 10668c2ecf20Sopenharmony_ci else { 10678c2ecf20Sopenharmony_ci cmd.control &= cpu_to_le16(~0x1); 10688c2ecf20Sopenharmony_ci priv->txpower_cur = 0; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci lbs_deb_cmd("RADIO_CONTROL: radio %s, preamble %d\n", 10728c2ecf20Sopenharmony_ci radio_on ? "ON" : "OFF", preamble); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci priv->radio_on = radio_on; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ciout: 10798c2ecf20Sopenharmony_ci return ret; 10808c2ecf20Sopenharmony_ci} 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_civoid lbs_set_mac_control(struct lbs_private *priv) 10838c2ecf20Sopenharmony_ci{ 10848c2ecf20Sopenharmony_ci struct cmd_ds_mac_control cmd; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 10878c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(priv->mac_control); 10888c2ecf20Sopenharmony_ci cmd.reserved = 0; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci lbs_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd)); 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ciint lbs_set_mac_control_sync(struct lbs_private *priv) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci struct cmd_ds_mac_control cmd; 10968c2ecf20Sopenharmony_ci int ret = 0; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 10998c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(priv->mac_control); 11008c2ecf20Sopenharmony_ci cmd.reserved = 0; 11018c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_MAC_CONTROL, &cmd); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci return ret; 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci/** 11078c2ecf20Sopenharmony_ci * lbs_allocate_cmd_buffer - allocates the command buffer and links 11088c2ecf20Sopenharmony_ci * it to command free queue 11098c2ecf20Sopenharmony_ci * 11108c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 11118c2ecf20Sopenharmony_ci * 11128c2ecf20Sopenharmony_ci * returns: 0 for success or -1 on error 11138c2ecf20Sopenharmony_ci */ 11148c2ecf20Sopenharmony_ciint lbs_allocate_cmd_buffer(struct lbs_private *priv) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci int ret = 0; 11178c2ecf20Sopenharmony_ci u32 bufsize; 11188c2ecf20Sopenharmony_ci u32 i; 11198c2ecf20Sopenharmony_ci struct cmd_ctrl_node *cmdarray; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci /* Allocate and initialize the command array */ 11228c2ecf20Sopenharmony_ci bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; 11238c2ecf20Sopenharmony_ci if (!(cmdarray = kzalloc(bufsize, GFP_KERNEL))) { 11248c2ecf20Sopenharmony_ci lbs_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n"); 11258c2ecf20Sopenharmony_ci ret = -1; 11268c2ecf20Sopenharmony_ci goto done; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci priv->cmd_array = cmdarray; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* Allocate and initialize each command buffer in the command array */ 11318c2ecf20Sopenharmony_ci for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { 11328c2ecf20Sopenharmony_ci cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); 11338c2ecf20Sopenharmony_ci if (!cmdarray[i].cmdbuf) { 11348c2ecf20Sopenharmony_ci lbs_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n"); 11358c2ecf20Sopenharmony_ci ret = -1; 11368c2ecf20Sopenharmony_ci goto done; 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { 11418c2ecf20Sopenharmony_ci init_waitqueue_head(&cmdarray[i].cmdwait_q); 11428c2ecf20Sopenharmony_ci lbs_cleanup_and_insert_cmd(priv, &cmdarray[i]); 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci ret = 0; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_cidone: 11478c2ecf20Sopenharmony_ci return ret; 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci/** 11518c2ecf20Sopenharmony_ci * lbs_free_cmd_buffer - free the command buffer 11528c2ecf20Sopenharmony_ci * 11538c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 11548c2ecf20Sopenharmony_ci * 11558c2ecf20Sopenharmony_ci * returns: 0 for success 11568c2ecf20Sopenharmony_ci */ 11578c2ecf20Sopenharmony_ciint lbs_free_cmd_buffer(struct lbs_private *priv) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct cmd_ctrl_node *cmdarray; 11608c2ecf20Sopenharmony_ci unsigned int i; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* need to check if cmd array is allocated or not */ 11638c2ecf20Sopenharmony_ci if (priv->cmd_array == NULL) { 11648c2ecf20Sopenharmony_ci lbs_deb_host("FREE_CMD_BUF: cmd_array is NULL\n"); 11658c2ecf20Sopenharmony_ci goto done; 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci cmdarray = priv->cmd_array; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci /* Release shared memory buffers */ 11718c2ecf20Sopenharmony_ci for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { 11728c2ecf20Sopenharmony_ci if (cmdarray[i].cmdbuf) { 11738c2ecf20Sopenharmony_ci kfree(cmdarray[i].cmdbuf); 11748c2ecf20Sopenharmony_ci cmdarray[i].cmdbuf = NULL; 11758c2ecf20Sopenharmony_ci } 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci /* Release cmd_ctrl_node */ 11798c2ecf20Sopenharmony_ci if (priv->cmd_array) { 11808c2ecf20Sopenharmony_ci kfree(priv->cmd_array); 11818c2ecf20Sopenharmony_ci priv->cmd_array = NULL; 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_cidone: 11858c2ecf20Sopenharmony_ci return 0; 11868c2ecf20Sopenharmony_ci} 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci/** 11898c2ecf20Sopenharmony_ci * lbs_get_free_cmd_node - gets a free command node if available in 11908c2ecf20Sopenharmony_ci * command free queue 11918c2ecf20Sopenharmony_ci * 11928c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 11938c2ecf20Sopenharmony_ci * 11948c2ecf20Sopenharmony_ci * returns: A pointer to &cmd_ctrl_node structure on success 11958c2ecf20Sopenharmony_ci * or %NULL on error 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_cistatic struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv) 11988c2ecf20Sopenharmony_ci{ 11998c2ecf20Sopenharmony_ci struct cmd_ctrl_node *tempnode; 12008c2ecf20Sopenharmony_ci unsigned long flags; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci if (!priv) 12038c2ecf20Sopenharmony_ci return NULL; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci if (!list_empty(&priv->cmdfreeq)) { 12088c2ecf20Sopenharmony_ci tempnode = list_first_entry(&priv->cmdfreeq, 12098c2ecf20Sopenharmony_ci struct cmd_ctrl_node, list); 12108c2ecf20Sopenharmony_ci list_del_init(&tempnode->list); 12118c2ecf20Sopenharmony_ci } else { 12128c2ecf20Sopenharmony_ci lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); 12138c2ecf20Sopenharmony_ci tempnode = NULL; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci return tempnode; 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci/** 12228c2ecf20Sopenharmony_ci * lbs_execute_next_command - execute next command in command 12238c2ecf20Sopenharmony_ci * pending queue. Will put firmware back to PS mode if applicable. 12248c2ecf20Sopenharmony_ci * 12258c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 12268c2ecf20Sopenharmony_ci * 12278c2ecf20Sopenharmony_ci * returns: 0 on success or -1 on error 12288c2ecf20Sopenharmony_ci */ 12298c2ecf20Sopenharmony_ciint lbs_execute_next_command(struct lbs_private *priv) 12308c2ecf20Sopenharmony_ci{ 12318c2ecf20Sopenharmony_ci struct cmd_ctrl_node *cmdnode = NULL; 12328c2ecf20Sopenharmony_ci struct cmd_header *cmd; 12338c2ecf20Sopenharmony_ci unsigned long flags; 12348c2ecf20Sopenharmony_ci int ret = 0; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci /* Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the 12378c2ecf20Sopenharmony_ci * only caller to us is lbs_thread() and we get even when a 12388c2ecf20Sopenharmony_ci * data packet is received */ 12398c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci if (priv->cur_cmd) { 12428c2ecf20Sopenharmony_ci netdev_alert(priv->dev, 12438c2ecf20Sopenharmony_ci "EXEC_NEXT_CMD: already processing command!\n"); 12448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 12458c2ecf20Sopenharmony_ci ret = -1; 12468c2ecf20Sopenharmony_ci goto done; 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci if (!list_empty(&priv->cmdpendingq)) { 12508c2ecf20Sopenharmony_ci cmdnode = list_first_entry(&priv->cmdpendingq, 12518c2ecf20Sopenharmony_ci struct cmd_ctrl_node, list); 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci if (cmdnode) { 12578c2ecf20Sopenharmony_ci cmd = cmdnode->cmdbuf; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci if (is_command_allowed_in_ps(le16_to_cpu(cmd->command))) { 12608c2ecf20Sopenharmony_ci if ((priv->psstate == PS_STATE_SLEEP) || 12618c2ecf20Sopenharmony_ci (priv->psstate == PS_STATE_PRE_SLEEP)) { 12628c2ecf20Sopenharmony_ci lbs_deb_host( 12638c2ecf20Sopenharmony_ci "EXEC_NEXT_CMD: cannot send cmd 0x%04x in psstate %d\n", 12648c2ecf20Sopenharmony_ci le16_to_cpu(cmd->command), 12658c2ecf20Sopenharmony_ci priv->psstate); 12668c2ecf20Sopenharmony_ci ret = -1; 12678c2ecf20Sopenharmony_ci goto done; 12688c2ecf20Sopenharmony_ci } 12698c2ecf20Sopenharmony_ci lbs_deb_host("EXEC_NEXT_CMD: OK to send command " 12708c2ecf20Sopenharmony_ci "0x%04x in psstate %d\n", 12718c2ecf20Sopenharmony_ci le16_to_cpu(cmd->command), priv->psstate); 12728c2ecf20Sopenharmony_ci } else if (priv->psstate != PS_STATE_FULL_POWER) { 12738c2ecf20Sopenharmony_ci /* 12748c2ecf20Sopenharmony_ci * 1. Non-PS command: 12758c2ecf20Sopenharmony_ci * Queue it. set needtowakeup to TRUE if current state 12768c2ecf20Sopenharmony_ci * is SLEEP, otherwise call send EXIT_PS. 12778c2ecf20Sopenharmony_ci * 2. PS command but not EXIT_PS: 12788c2ecf20Sopenharmony_ci * Ignore it. 12798c2ecf20Sopenharmony_ci * 3. PS command EXIT_PS: 12808c2ecf20Sopenharmony_ci * Set needtowakeup to TRUE if current state is SLEEP, 12818c2ecf20Sopenharmony_ci * otherwise send this command down to firmware 12828c2ecf20Sopenharmony_ci * immediately. 12838c2ecf20Sopenharmony_ci */ 12848c2ecf20Sopenharmony_ci if (cmd->command != cpu_to_le16(CMD_802_11_PS_MODE)) { 12858c2ecf20Sopenharmony_ci /* Prepare to send Exit PS, 12868c2ecf20Sopenharmony_ci * this non PS command will be sent later */ 12878c2ecf20Sopenharmony_ci if ((priv->psstate == PS_STATE_SLEEP) 12888c2ecf20Sopenharmony_ci || (priv->psstate == PS_STATE_PRE_SLEEP) 12898c2ecf20Sopenharmony_ci ) { 12908c2ecf20Sopenharmony_ci /* w/ new scheme, it will not reach here. 12918c2ecf20Sopenharmony_ci since it is blocked in main_thread. */ 12928c2ecf20Sopenharmony_ci priv->needtowakeup = 1; 12938c2ecf20Sopenharmony_ci } else { 12948c2ecf20Sopenharmony_ci lbs_set_ps_mode(priv, 12958c2ecf20Sopenharmony_ci PS_MODE_ACTION_EXIT_PS, 12968c2ecf20Sopenharmony_ci false); 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci ret = 0; 13008c2ecf20Sopenharmony_ci goto done; 13018c2ecf20Sopenharmony_ci } else { 13028c2ecf20Sopenharmony_ci /* 13038c2ecf20Sopenharmony_ci * PS command. Ignore it if it is not Exit_PS. 13048c2ecf20Sopenharmony_ci * otherwise send it down immediately. 13058c2ecf20Sopenharmony_ci */ 13068c2ecf20Sopenharmony_ci struct cmd_ds_802_11_ps_mode *psm = (void *)cmd; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci lbs_deb_host( 13098c2ecf20Sopenharmony_ci "EXEC_NEXT_CMD: PS cmd, action 0x%02x\n", 13108c2ecf20Sopenharmony_ci psm->action); 13118c2ecf20Sopenharmony_ci if (psm->action != 13128c2ecf20Sopenharmony_ci cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { 13138c2ecf20Sopenharmony_ci lbs_deb_host( 13148c2ecf20Sopenharmony_ci "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n"); 13158c2ecf20Sopenharmony_ci lbs_complete_command(priv, cmdnode, 0); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci ret = 0; 13188c2ecf20Sopenharmony_ci goto done; 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci if ((priv->psstate == PS_STATE_SLEEP) || 13228c2ecf20Sopenharmony_ci (priv->psstate == PS_STATE_PRE_SLEEP)) { 13238c2ecf20Sopenharmony_ci lbs_deb_host( 13248c2ecf20Sopenharmony_ci "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n"); 13258c2ecf20Sopenharmony_ci lbs_complete_command(priv, cmdnode, 0); 13268c2ecf20Sopenharmony_ci priv->needtowakeup = 1; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci ret = 0; 13298c2ecf20Sopenharmony_ci goto done; 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci lbs_deb_host( 13338c2ecf20Sopenharmony_ci "EXEC_NEXT_CMD: sending EXIT_PS\n"); 13348c2ecf20Sopenharmony_ci } 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 13378c2ecf20Sopenharmony_ci list_del_init(&cmdnode->list); 13388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 13398c2ecf20Sopenharmony_ci lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", 13408c2ecf20Sopenharmony_ci le16_to_cpu(cmd->command)); 13418c2ecf20Sopenharmony_ci lbs_submit_command(priv, cmdnode); 13428c2ecf20Sopenharmony_ci } else { 13438c2ecf20Sopenharmony_ci /* 13448c2ecf20Sopenharmony_ci * check if in power save mode, if yes, put the device back 13458c2ecf20Sopenharmony_ci * to PS mode 13468c2ecf20Sopenharmony_ci */ 13478c2ecf20Sopenharmony_ci if ((priv->psmode != LBS802_11POWERMODECAM) && 13488c2ecf20Sopenharmony_ci (priv->psstate == PS_STATE_FULL_POWER) && 13498c2ecf20Sopenharmony_ci (priv->connect_status == LBS_CONNECTED)) { 13508c2ecf20Sopenharmony_ci lbs_deb_host( 13518c2ecf20Sopenharmony_ci "EXEC_NEXT_CMD: cmdpendingq empty, go back to PS_SLEEP"); 13528c2ecf20Sopenharmony_ci lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS, 13538c2ecf20Sopenharmony_ci false); 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci ret = 0; 13588c2ecf20Sopenharmony_cidone: 13598c2ecf20Sopenharmony_ci return ret; 13608c2ecf20Sopenharmony_ci} 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_cistatic void lbs_send_confirmsleep(struct lbs_private *priv) 13638c2ecf20Sopenharmony_ci{ 13648c2ecf20Sopenharmony_ci unsigned long flags; 13658c2ecf20Sopenharmony_ci int ret; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci lbs_deb_hex(LBS_DEB_HOST, "sleep confirm", (u8 *) &confirm_sleep, 13688c2ecf20Sopenharmony_ci sizeof(confirm_sleep)); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &confirm_sleep, 13718c2ecf20Sopenharmony_ci sizeof(confirm_sleep)); 13728c2ecf20Sopenharmony_ci if (ret) { 13738c2ecf20Sopenharmony_ci netdev_alert(priv->dev, "confirm_sleep failed\n"); 13748c2ecf20Sopenharmony_ci return; 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci /* We don't get a response on the sleep-confirmation */ 13808c2ecf20Sopenharmony_ci priv->dnld_sent = DNLD_RES_RECEIVED; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci if (priv->is_host_sleep_configured) { 13838c2ecf20Sopenharmony_ci priv->is_host_sleep_activated = 1; 13848c2ecf20Sopenharmony_ci wake_up_interruptible(&priv->host_sleep_q); 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci /* If nothing to do, go back to sleep (?) */ 13888c2ecf20Sopenharmony_ci if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx]) 13898c2ecf20Sopenharmony_ci priv->psstate = PS_STATE_SLEEP; 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 13928c2ecf20Sopenharmony_ci} 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci/** 13958c2ecf20Sopenharmony_ci * lbs_ps_confirm_sleep - checks condition and prepares to 13968c2ecf20Sopenharmony_ci * send sleep confirm command to firmware if ok 13978c2ecf20Sopenharmony_ci * 13988c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 13998c2ecf20Sopenharmony_ci * 14008c2ecf20Sopenharmony_ci * returns: n/a 14018c2ecf20Sopenharmony_ci */ 14028c2ecf20Sopenharmony_civoid lbs_ps_confirm_sleep(struct lbs_private *priv) 14038c2ecf20Sopenharmony_ci{ 14048c2ecf20Sopenharmony_ci unsigned long flags =0; 14058c2ecf20Sopenharmony_ci int allowed = 1; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 14088c2ecf20Sopenharmony_ci if (priv->dnld_sent) { 14098c2ecf20Sopenharmony_ci allowed = 0; 14108c2ecf20Sopenharmony_ci lbs_deb_host("dnld_sent was set\n"); 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci /* In-progress command? */ 14148c2ecf20Sopenharmony_ci if (priv->cur_cmd) { 14158c2ecf20Sopenharmony_ci allowed = 0; 14168c2ecf20Sopenharmony_ci lbs_deb_host("cur_cmd was set\n"); 14178c2ecf20Sopenharmony_ci } 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci /* Pending events or command responses? */ 14208c2ecf20Sopenharmony_ci if (kfifo_len(&priv->event_fifo) || priv->resp_len[priv->resp_idx]) { 14218c2ecf20Sopenharmony_ci allowed = 0; 14228c2ecf20Sopenharmony_ci lbs_deb_host("pending events or command responses\n"); 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci if (allowed) { 14278c2ecf20Sopenharmony_ci lbs_deb_host("sending lbs_ps_confirm_sleep\n"); 14288c2ecf20Sopenharmony_ci lbs_send_confirmsleep(priv); 14298c2ecf20Sopenharmony_ci } else { 14308c2ecf20Sopenharmony_ci lbs_deb_host("sleep confirm has been delayed\n"); 14318c2ecf20Sopenharmony_ci } 14328c2ecf20Sopenharmony_ci} 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci/** 14368c2ecf20Sopenharmony_ci * lbs_set_tpc_cfg - Configures the transmission power control functionality 14378c2ecf20Sopenharmony_ci * 14388c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 14398c2ecf20Sopenharmony_ci * @enable: Transmission power control enable 14408c2ecf20Sopenharmony_ci * @p0: Power level when link quality is good (dBm). 14418c2ecf20Sopenharmony_ci * @p1: Power level when link quality is fair (dBm). 14428c2ecf20Sopenharmony_ci * @p2: Power level when link quality is poor (dBm). 14438c2ecf20Sopenharmony_ci * @usesnr: Use Signal to Noise Ratio in TPC 14448c2ecf20Sopenharmony_ci * 14458c2ecf20Sopenharmony_ci * returns: 0 on success 14468c2ecf20Sopenharmony_ci */ 14478c2ecf20Sopenharmony_ciint lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, 14488c2ecf20Sopenharmony_ci int8_t p2, int usesnr) 14498c2ecf20Sopenharmony_ci{ 14508c2ecf20Sopenharmony_ci struct cmd_ds_802_11_tpc_cfg cmd; 14518c2ecf20Sopenharmony_ci int ret; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 14548c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 14558c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_SET); 14568c2ecf20Sopenharmony_ci cmd.enable = !!enable; 14578c2ecf20Sopenharmony_ci cmd.usesnr = !!usesnr; 14588c2ecf20Sopenharmony_ci cmd.P0 = p0; 14598c2ecf20Sopenharmony_ci cmd.P1 = p1; 14608c2ecf20Sopenharmony_ci cmd.P2 = p2; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci return ret; 14658c2ecf20Sopenharmony_ci} 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci/** 14688c2ecf20Sopenharmony_ci * lbs_set_power_adapt_cfg - Configures the power adaptation settings 14698c2ecf20Sopenharmony_ci * 14708c2ecf20Sopenharmony_ci * @priv: A pointer to &struct lbs_private structure 14718c2ecf20Sopenharmony_ci * @enable: Power adaptation enable 14728c2ecf20Sopenharmony_ci * @p0: Power level for 1, 2, 5.5 and 11 Mbps (dBm). 14738c2ecf20Sopenharmony_ci * @p1: Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm). 14748c2ecf20Sopenharmony_ci * @p2: Power level for 48 and 54 Mbps (dBm). 14758c2ecf20Sopenharmony_ci * 14768c2ecf20Sopenharmony_ci * returns: 0 on Success 14778c2ecf20Sopenharmony_ci */ 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ciint lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, 14808c2ecf20Sopenharmony_ci int8_t p1, int8_t p2) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci struct cmd_ds_802_11_pa_cfg cmd; 14838c2ecf20Sopenharmony_ci int ret; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci memset(&cmd, 0, sizeof(cmd)); 14868c2ecf20Sopenharmony_ci cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 14878c2ecf20Sopenharmony_ci cmd.action = cpu_to_le16(CMD_ACT_SET); 14888c2ecf20Sopenharmony_ci cmd.enable = !!enable; 14898c2ecf20Sopenharmony_ci cmd.P0 = p0; 14908c2ecf20Sopenharmony_ci cmd.P1 = p1; 14918c2ecf20Sopenharmony_ci cmd.P2 = p2; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd); 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci return ret; 14968c2ecf20Sopenharmony_ci} 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_cistruct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, 15008c2ecf20Sopenharmony_ci uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, 15018c2ecf20Sopenharmony_ci int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), 15028c2ecf20Sopenharmony_ci unsigned long callback_arg) 15038c2ecf20Sopenharmony_ci{ 15048c2ecf20Sopenharmony_ci struct cmd_ctrl_node *cmdnode; 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci if (priv->surpriseremoved) { 15078c2ecf20Sopenharmony_ci lbs_deb_host("PREP_CMD: card removed\n"); 15088c2ecf20Sopenharmony_ci cmdnode = ERR_PTR(-ENOENT); 15098c2ecf20Sopenharmony_ci goto done; 15108c2ecf20Sopenharmony_ci } 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci /* No commands are allowed in Deep Sleep until we toggle the GPIO 15138c2ecf20Sopenharmony_ci * to wake up the card and it has signaled that it's ready. 15148c2ecf20Sopenharmony_ci */ 15158c2ecf20Sopenharmony_ci if (!priv->is_auto_deep_sleep_enabled) { 15168c2ecf20Sopenharmony_ci if (priv->is_deep_sleep) { 15178c2ecf20Sopenharmony_ci lbs_deb_cmd("command not allowed in deep sleep\n"); 15188c2ecf20Sopenharmony_ci cmdnode = ERR_PTR(-EBUSY); 15198c2ecf20Sopenharmony_ci goto done; 15208c2ecf20Sopenharmony_ci } 15218c2ecf20Sopenharmony_ci } 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci cmdnode = lbs_get_free_cmd_node(priv); 15248c2ecf20Sopenharmony_ci if (cmdnode == NULL) { 15258c2ecf20Sopenharmony_ci lbs_deb_host("PREP_CMD: cmdnode is NULL\n"); 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci /* Wake up main thread to execute next command */ 15288c2ecf20Sopenharmony_ci wake_up(&priv->waitq); 15298c2ecf20Sopenharmony_ci cmdnode = ERR_PTR(-ENOBUFS); 15308c2ecf20Sopenharmony_ci goto done; 15318c2ecf20Sopenharmony_ci } 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci cmdnode->callback = callback; 15348c2ecf20Sopenharmony_ci cmdnode->callback_arg = callback_arg; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* Copy the incoming command to the buffer */ 15378c2ecf20Sopenharmony_ci memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci /* Set command, clean result, move to buffer */ 15408c2ecf20Sopenharmony_ci cmdnode->cmdbuf->command = cpu_to_le16(command); 15418c2ecf20Sopenharmony_ci cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); 15428c2ecf20Sopenharmony_ci cmdnode->cmdbuf->result = 0; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci lbs_deb_host("PREP_CMD: command 0x%04x\n", command); 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci cmdnode->cmdwaitqwoken = 0; 15478c2ecf20Sopenharmony_ci lbs_queue_cmd(priv, cmdnode); 15488c2ecf20Sopenharmony_ci wake_up(&priv->waitq); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci done: 15518c2ecf20Sopenharmony_ci return cmdnode; 15528c2ecf20Sopenharmony_ci} 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_civoid lbs_cmd_async(struct lbs_private *priv, uint16_t command, 15558c2ecf20Sopenharmony_ci struct cmd_header *in_cmd, int in_cmd_size) 15568c2ecf20Sopenharmony_ci{ 15578c2ecf20Sopenharmony_ci __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, 15588c2ecf20Sopenharmony_ci lbs_cmd_async_callback, 0); 15598c2ecf20Sopenharmony_ci} 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ciint __lbs_cmd(struct lbs_private *priv, uint16_t command, 15628c2ecf20Sopenharmony_ci struct cmd_header *in_cmd, int in_cmd_size, 15638c2ecf20Sopenharmony_ci int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), 15648c2ecf20Sopenharmony_ci unsigned long callback_arg) 15658c2ecf20Sopenharmony_ci{ 15668c2ecf20Sopenharmony_ci struct cmd_ctrl_node *cmdnode; 15678c2ecf20Sopenharmony_ci unsigned long flags; 15688c2ecf20Sopenharmony_ci int ret = 0; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci cmdnode = __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, 15718c2ecf20Sopenharmony_ci callback, callback_arg); 15728c2ecf20Sopenharmony_ci if (IS_ERR(cmdnode)) { 15738c2ecf20Sopenharmony_ci ret = PTR_ERR(cmdnode); 15748c2ecf20Sopenharmony_ci goto done; 15758c2ecf20Sopenharmony_ci } 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci might_sleep(); 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci /* 15808c2ecf20Sopenharmony_ci * Be careful with signals here. A signal may be received as the system 15818c2ecf20Sopenharmony_ci * goes into suspend or resume. We do not want this to interrupt the 15828c2ecf20Sopenharmony_ci * command, so we perform an uninterruptible sleep. 15838c2ecf20Sopenharmony_ci */ 15848c2ecf20Sopenharmony_ci wait_event(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 15878c2ecf20Sopenharmony_ci ret = cmdnode->result; 15888c2ecf20Sopenharmony_ci if (ret) 15898c2ecf20Sopenharmony_ci netdev_info(priv->dev, "PREP_CMD: command 0x%04x failed: %d\n", 15908c2ecf20Sopenharmony_ci command, ret); 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci __lbs_cleanup_and_insert_cmd(priv, cmdnode); 15938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_cidone: 15968c2ecf20Sopenharmony_ci return ret; 15978c2ecf20Sopenharmony_ci} 15988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__lbs_cmd); 1599