18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include "cmd.h" 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/module.h> 58c2ecf20Sopenharmony_ci#include <linux/slab.h> 68c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "wl1251.h" 98c2ecf20Sopenharmony_ci#include "reg.h" 108c2ecf20Sopenharmony_ci#include "io.h" 118c2ecf20Sopenharmony_ci#include "ps.h" 128c2ecf20Sopenharmony_ci#include "acx.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/** 158c2ecf20Sopenharmony_ci * send command to firmware 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * @wl: wl struct 188c2ecf20Sopenharmony_ci * @id: command id 198c2ecf20Sopenharmony_ci * @buf: buffer containing the command, must work with dma 208c2ecf20Sopenharmony_ci * @len: length of the buffer 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ciint wl1251_cmd_send(struct wl1251 *wl, u16 id, void *buf, size_t len) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct wl1251_cmd_header *cmd; 258c2ecf20Sopenharmony_ci unsigned long timeout; 268c2ecf20Sopenharmony_ci u32 intr; 278c2ecf20Sopenharmony_ci int ret = 0; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci cmd = buf; 308c2ecf20Sopenharmony_ci cmd->id = id; 318c2ecf20Sopenharmony_ci cmd->status = 0; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci WARN_ON(len % 4 != 0); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci wl1251_mem_write(wl, wl->cmd_box_addr, buf, len); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci wl1251_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(WL1251_COMMAND_TIMEOUT); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); 428c2ecf20Sopenharmony_ci while (!(intr & WL1251_ACX_INTR_CMD_COMPLETE)) { 438c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 448c2ecf20Sopenharmony_ci wl1251_error("command complete timeout"); 458c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 468c2ecf20Sopenharmony_ci goto out; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci msleep(1); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci wl1251_reg_write32(wl, ACX_REG_INTERRUPT_ACK, 558c2ecf20Sopenharmony_ci WL1251_ACX_INTR_CMD_COMPLETE); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ciout: 588c2ecf20Sopenharmony_ci return ret; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/** 628c2ecf20Sopenharmony_ci * send test command to firmware 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * @wl: wl struct 658c2ecf20Sopenharmony_ci * @buf: buffer containing the command, with all headers, must work with dma 668c2ecf20Sopenharmony_ci * @len: length of the buffer 678c2ecf20Sopenharmony_ci * @answer: is answer needed 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ciint wl1251_cmd_test(struct wl1251 *wl, void *buf, size_t buf_len, u8 answer) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci int ret; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_CMD, "cmd test"); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci ret = wl1251_cmd_send(wl, CMD_TEST, buf, buf_len); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (ret < 0) { 788c2ecf20Sopenharmony_ci wl1251_warning("TEST command failed"); 798c2ecf20Sopenharmony_ci return ret; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (answer) { 838c2ecf20Sopenharmony_ci struct wl1251_command *cmd_answer; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* 868c2ecf20Sopenharmony_ci * The test command got in, we can read the answer. 878c2ecf20Sopenharmony_ci * The answer would be a wl1251_command, where the 888c2ecf20Sopenharmony_ci * parameter array contains the actual answer. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci wl1251_mem_read(wl, wl->cmd_box_addr, buf, buf_len); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci cmd_answer = buf; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (cmd_answer->header.status != CMD_STATUS_SUCCESS) 958c2ecf20Sopenharmony_ci wl1251_error("TEST command answer error: %d", 968c2ecf20Sopenharmony_ci cmd_answer->header.status); 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/** 1038c2ecf20Sopenharmony_ci * read acx from firmware 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * @wl: wl struct 1068c2ecf20Sopenharmony_ci * @id: acx id 1078c2ecf20Sopenharmony_ci * @buf: buffer for the response, including all headers, must work with dma 1088c2ecf20Sopenharmony_ci * @len: length of buf 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ciint wl1251_cmd_interrogate(struct wl1251 *wl, u16 id, void *buf, size_t len) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct acx_header *acx = buf; 1138c2ecf20Sopenharmony_ci int ret; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_CMD, "cmd interrogate"); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci acx->id = id; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* payload length, does not include any headers */ 1208c2ecf20Sopenharmony_ci acx->len = len - sizeof(*acx); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci ret = wl1251_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx)); 1238c2ecf20Sopenharmony_ci if (ret < 0) { 1248c2ecf20Sopenharmony_ci wl1251_error("INTERROGATE command failed"); 1258c2ecf20Sopenharmony_ci goto out; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* the interrogate command got in, we can read the answer */ 1298c2ecf20Sopenharmony_ci wl1251_mem_read(wl, wl->cmd_box_addr, buf, len); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci acx = buf; 1328c2ecf20Sopenharmony_ci if (acx->cmd.status != CMD_STATUS_SUCCESS) 1338c2ecf20Sopenharmony_ci wl1251_error("INTERROGATE command error: %d", 1348c2ecf20Sopenharmony_ci acx->cmd.status); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciout: 1378c2ecf20Sopenharmony_ci return ret; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/** 1418c2ecf20Sopenharmony_ci * write acx value to firmware 1428c2ecf20Sopenharmony_ci * 1438c2ecf20Sopenharmony_ci * @wl: wl struct 1448c2ecf20Sopenharmony_ci * @id: acx id 1458c2ecf20Sopenharmony_ci * @buf: buffer containing acx, including all headers, must work with dma 1468c2ecf20Sopenharmony_ci * @len: length of buf 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ciint wl1251_cmd_configure(struct wl1251 *wl, u16 id, void *buf, size_t len) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct acx_header *acx = buf; 1518c2ecf20Sopenharmony_ci int ret; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_CMD, "cmd configure"); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci acx->id = id; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* payload length, does not include any headers */ 1588c2ecf20Sopenharmony_ci acx->len = len - sizeof(*acx); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci ret = wl1251_cmd_send(wl, CMD_CONFIGURE, acx, len); 1618c2ecf20Sopenharmony_ci if (ret < 0) { 1628c2ecf20Sopenharmony_ci wl1251_warning("CONFIGURE command NOK"); 1638c2ecf20Sopenharmony_ci return ret; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ciint wl1251_cmd_vbm(struct wl1251 *wl, u8 identity, 1708c2ecf20Sopenharmony_ci void *bitmap, u16 bitmap_len, u8 bitmap_control) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct wl1251_cmd_vbm_update *vbm; 1738c2ecf20Sopenharmony_ci int ret; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_CMD, "cmd vbm"); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci vbm = kzalloc(sizeof(*vbm), GFP_KERNEL); 1788c2ecf20Sopenharmony_ci if (!vbm) { 1798c2ecf20Sopenharmony_ci ret = -ENOMEM; 1808c2ecf20Sopenharmony_ci goto out; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* Count and period will be filled by the target */ 1848c2ecf20Sopenharmony_ci vbm->tim.bitmap_ctrl = bitmap_control; 1858c2ecf20Sopenharmony_ci if (bitmap_len > PARTIAL_VBM_MAX) { 1868c2ecf20Sopenharmony_ci wl1251_warning("cmd vbm len is %d B, truncating to %d", 1878c2ecf20Sopenharmony_ci bitmap_len, PARTIAL_VBM_MAX); 1888c2ecf20Sopenharmony_ci bitmap_len = PARTIAL_VBM_MAX; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci memcpy(vbm->tim.pvb_field, bitmap, bitmap_len); 1918c2ecf20Sopenharmony_ci vbm->tim.identity = identity; 1928c2ecf20Sopenharmony_ci vbm->tim.length = bitmap_len + 3; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci vbm->len = cpu_to_le16(bitmap_len + 5); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ret = wl1251_cmd_send(wl, CMD_VBM, vbm, sizeof(*vbm)); 1978c2ecf20Sopenharmony_ci if (ret < 0) { 1988c2ecf20Sopenharmony_ci wl1251_error("VBM command failed"); 1998c2ecf20Sopenharmony_ci goto out; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ciout: 2038c2ecf20Sopenharmony_ci kfree(vbm); 2048c2ecf20Sopenharmony_ci return ret; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ciint wl1251_cmd_data_path_rx(struct wl1251 *wl, u8 channel, bool enable) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct cmd_enabledisable_path *cmd; 2108c2ecf20Sopenharmony_ci int ret; 2118c2ecf20Sopenharmony_ci u16 cmd_rx; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_CMD, "cmd data path"); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 2168c2ecf20Sopenharmony_ci if (!cmd) { 2178c2ecf20Sopenharmony_ci ret = -ENOMEM; 2188c2ecf20Sopenharmony_ci goto out; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci cmd->channel = channel; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (enable) 2248c2ecf20Sopenharmony_ci cmd_rx = CMD_ENABLE_RX; 2258c2ecf20Sopenharmony_ci else 2268c2ecf20Sopenharmony_ci cmd_rx = CMD_DISABLE_RX; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci ret = wl1251_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd)); 2298c2ecf20Sopenharmony_ci if (ret < 0) { 2308c2ecf20Sopenharmony_ci wl1251_error("rx %s cmd for channel %d failed", 2318c2ecf20Sopenharmony_ci enable ? "start" : "stop", channel); 2328c2ecf20Sopenharmony_ci goto out; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_BOOT, "rx %s cmd channel %d", 2368c2ecf20Sopenharmony_ci enable ? "start" : "stop", channel); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ciout: 2398c2ecf20Sopenharmony_ci kfree(cmd); 2408c2ecf20Sopenharmony_ci return ret; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ciint wl1251_cmd_data_path_tx(struct wl1251 *wl, u8 channel, bool enable) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct cmd_enabledisable_path *cmd; 2468c2ecf20Sopenharmony_ci int ret; 2478c2ecf20Sopenharmony_ci u16 cmd_tx; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_CMD, "cmd data path"); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 2528c2ecf20Sopenharmony_ci if (!cmd) 2538c2ecf20Sopenharmony_ci return -ENOMEM; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci cmd->channel = channel; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (enable) 2588c2ecf20Sopenharmony_ci cmd_tx = CMD_ENABLE_TX; 2598c2ecf20Sopenharmony_ci else 2608c2ecf20Sopenharmony_ci cmd_tx = CMD_DISABLE_TX; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ret = wl1251_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd)); 2638c2ecf20Sopenharmony_ci if (ret < 0) 2648c2ecf20Sopenharmony_ci wl1251_error("tx %s cmd for channel %d failed", 2658c2ecf20Sopenharmony_ci enable ? "start" : "stop", channel); 2668c2ecf20Sopenharmony_ci else 2678c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_BOOT, "tx %s cmd channel %d", 2688c2ecf20Sopenharmony_ci enable ? "start" : "stop", channel); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci kfree(cmd); 2718c2ecf20Sopenharmony_ci return ret; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ciint wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel, 2758c2ecf20Sopenharmony_ci u16 beacon_interval, u8 dtim_interval) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct cmd_join *join; 2788c2ecf20Sopenharmony_ci int ret, i; 2798c2ecf20Sopenharmony_ci u8 *bssid; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci join = kzalloc(sizeof(*join), GFP_KERNEL); 2828c2ecf20Sopenharmony_ci if (!join) { 2838c2ecf20Sopenharmony_ci ret = -ENOMEM; 2848c2ecf20Sopenharmony_ci goto out; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_CMD, "cmd join%s ch %d %d/%d", 2888c2ecf20Sopenharmony_ci bss_type == BSS_TYPE_IBSS ? " ibss" : "", 2898c2ecf20Sopenharmony_ci channel, beacon_interval, dtim_interval); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* Reverse order BSSID */ 2928c2ecf20Sopenharmony_ci bssid = (u8 *) &join->bssid_lsb; 2938c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 2948c2ecf20Sopenharmony_ci bssid[i] = wl->bssid[ETH_ALEN - i - 1]; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci join->rx_config_options = wl->rx_config; 2978c2ecf20Sopenharmony_ci join->rx_filter_options = wl->rx_filter; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS | 3008c2ecf20Sopenharmony_ci RATE_MASK_5_5MBPS | RATE_MASK_11MBPS; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci join->beacon_interval = beacon_interval; 3038c2ecf20Sopenharmony_ci join->dtim_interval = dtim_interval; 3048c2ecf20Sopenharmony_ci join->bss_type = bss_type; 3058c2ecf20Sopenharmony_ci join->channel = channel; 3068c2ecf20Sopenharmony_ci join->ctrl = JOIN_CMD_CTRL_TX_FLUSH; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ret = wl1251_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join)); 3098c2ecf20Sopenharmony_ci if (ret < 0) { 3108c2ecf20Sopenharmony_ci wl1251_error("failed to initiate cmd join"); 3118c2ecf20Sopenharmony_ci goto out; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ciout: 3158c2ecf20Sopenharmony_ci kfree(join); 3168c2ecf20Sopenharmony_ci return ret; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ciint wl1251_cmd_ps_mode(struct wl1251 *wl, u8 ps_mode) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct wl1251_cmd_ps_params *ps_params = NULL; 3228c2ecf20Sopenharmony_ci int ret = 0; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_CMD, "cmd set ps mode"); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci ps_params = kzalloc(sizeof(*ps_params), GFP_KERNEL); 3278c2ecf20Sopenharmony_ci if (!ps_params) { 3288c2ecf20Sopenharmony_ci ret = -ENOMEM; 3298c2ecf20Sopenharmony_ci goto out; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ps_params->ps_mode = ps_mode; 3338c2ecf20Sopenharmony_ci ps_params->send_null_data = 1; 3348c2ecf20Sopenharmony_ci ps_params->retries = 5; 3358c2ecf20Sopenharmony_ci ps_params->hang_over_period = 128; 3368c2ecf20Sopenharmony_ci ps_params->null_data_rate = 1; /* 1 Mbps */ 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci ret = wl1251_cmd_send(wl, CMD_SET_PS_MODE, ps_params, 3398c2ecf20Sopenharmony_ci sizeof(*ps_params)); 3408c2ecf20Sopenharmony_ci if (ret < 0) { 3418c2ecf20Sopenharmony_ci wl1251_error("cmd set_ps_mode failed"); 3428c2ecf20Sopenharmony_ci goto out; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ciout: 3468c2ecf20Sopenharmony_ci kfree(ps_params); 3478c2ecf20Sopenharmony_ci return ret; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ciint wl1251_cmd_read_memory(struct wl1251 *wl, u32 addr, void *answer, 3518c2ecf20Sopenharmony_ci size_t len) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct cmd_read_write_memory *cmd; 3548c2ecf20Sopenharmony_ci int ret = 0; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_CMD, "cmd read memory"); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 3598c2ecf20Sopenharmony_ci if (!cmd) { 3608c2ecf20Sopenharmony_ci ret = -ENOMEM; 3618c2ecf20Sopenharmony_ci goto out; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci WARN_ON(len > MAX_READ_SIZE); 3658c2ecf20Sopenharmony_ci len = min_t(size_t, len, MAX_READ_SIZE); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci cmd->addr = addr; 3688c2ecf20Sopenharmony_ci cmd->size = len; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci ret = wl1251_cmd_send(wl, CMD_READ_MEMORY, cmd, sizeof(*cmd)); 3718c2ecf20Sopenharmony_ci if (ret < 0) { 3728c2ecf20Sopenharmony_ci wl1251_error("read memory command failed: %d", ret); 3738c2ecf20Sopenharmony_ci goto out; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* the read command got in, we can now read the answer */ 3778c2ecf20Sopenharmony_ci wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (cmd->header.status != CMD_STATUS_SUCCESS) 3808c2ecf20Sopenharmony_ci wl1251_error("error in read command result: %d", 3818c2ecf20Sopenharmony_ci cmd->header.status); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci memcpy(answer, cmd->value, len); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ciout: 3868c2ecf20Sopenharmony_ci kfree(cmd); 3878c2ecf20Sopenharmony_ci return ret; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ciint wl1251_cmd_template_set(struct wl1251 *wl, u16 cmd_id, 3918c2ecf20Sopenharmony_ci void *buf, size_t buf_len) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct wl1251_cmd_packet_template *cmd; 3948c2ecf20Sopenharmony_ci size_t cmd_len; 3958c2ecf20Sopenharmony_ci int ret = 0; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_CMD, "cmd template %d", cmd_id); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci WARN_ON(buf_len > WL1251_MAX_TEMPLATE_SIZE); 4008c2ecf20Sopenharmony_ci buf_len = min_t(size_t, buf_len, WL1251_MAX_TEMPLATE_SIZE); 4018c2ecf20Sopenharmony_ci cmd_len = ALIGN(sizeof(*cmd) + buf_len, 4); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci cmd = kzalloc(cmd_len, GFP_KERNEL); 4048c2ecf20Sopenharmony_ci if (!cmd) { 4058c2ecf20Sopenharmony_ci ret = -ENOMEM; 4068c2ecf20Sopenharmony_ci goto out; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci cmd->size = cpu_to_le16(buf_len); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (buf) 4128c2ecf20Sopenharmony_ci memcpy(cmd->data, buf, buf_len); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ret = wl1251_cmd_send(wl, cmd_id, cmd, cmd_len); 4158c2ecf20Sopenharmony_ci if (ret < 0) { 4168c2ecf20Sopenharmony_ci wl1251_warning("cmd set_template failed: %d", ret); 4178c2ecf20Sopenharmony_ci goto out; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ciout: 4218c2ecf20Sopenharmony_ci kfree(cmd); 4228c2ecf20Sopenharmony_ci return ret; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ciint wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len, 4268c2ecf20Sopenharmony_ci struct ieee80211_channel *channels[], 4278c2ecf20Sopenharmony_ci unsigned int n_channels, unsigned int n_probes) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct wl1251_cmd_scan *cmd; 4308c2ecf20Sopenharmony_ci int i, ret = 0; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_CMD, "cmd scan channels %d", n_channels); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci WARN_ON(n_channels > SCAN_MAX_NUM_OF_CHANNELS); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 4378c2ecf20Sopenharmony_ci if (!cmd) 4388c2ecf20Sopenharmony_ci return -ENOMEM; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci cmd->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD); 4418c2ecf20Sopenharmony_ci cmd->params.rx_filter_options = cpu_to_le32(CFG_RX_PRSP_EN | 4428c2ecf20Sopenharmony_ci CFG_RX_MGMT_EN | 4438c2ecf20Sopenharmony_ci CFG_RX_BCN_EN); 4448c2ecf20Sopenharmony_ci cmd->params.scan_options = 0; 4458c2ecf20Sopenharmony_ci /* 4468c2ecf20Sopenharmony_ci * Use high priority scan when not associated to prevent fw issue 4478c2ecf20Sopenharmony_ci * causing never-ending scans (sometimes 20+ minutes). 4488c2ecf20Sopenharmony_ci * Note: This bug may be caused by the fw's DTIM handling. 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_ci if (is_zero_ether_addr(wl->bssid)) 4518c2ecf20Sopenharmony_ci cmd->params.scan_options |= cpu_to_le16(WL1251_SCAN_OPT_PRIORITY_HIGH); 4528c2ecf20Sopenharmony_ci cmd->params.num_channels = n_channels; 4538c2ecf20Sopenharmony_ci cmd->params.num_probe_requests = n_probes; 4548c2ecf20Sopenharmony_ci cmd->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */ 4558c2ecf20Sopenharmony_ci cmd->params.tid_trigger = 0; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci for (i = 0; i < n_channels; i++) { 4588c2ecf20Sopenharmony_ci cmd->channels[i].min_duration = 4598c2ecf20Sopenharmony_ci cpu_to_le32(WL1251_SCAN_MIN_DURATION); 4608c2ecf20Sopenharmony_ci cmd->channels[i].max_duration = 4618c2ecf20Sopenharmony_ci cpu_to_le32(WL1251_SCAN_MAX_DURATION); 4628c2ecf20Sopenharmony_ci memset(&cmd->channels[i].bssid_lsb, 0xff, 4); 4638c2ecf20Sopenharmony_ci memset(&cmd->channels[i].bssid_msb, 0xff, 2); 4648c2ecf20Sopenharmony_ci cmd->channels[i].early_termination = 0; 4658c2ecf20Sopenharmony_ci cmd->channels[i].tx_power_att = 0; 4668c2ecf20Sopenharmony_ci cmd->channels[i].channel = channels[i]->hw_value; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (ssid) { 4708c2ecf20Sopenharmony_ci int len = clamp_val(ssid_len, 0, IEEE80211_MAX_SSID_LEN); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci cmd->params.ssid_len = len; 4738c2ecf20Sopenharmony_ci memcpy(cmd->params.ssid, ssid, len); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci ret = wl1251_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd)); 4778c2ecf20Sopenharmony_ci if (ret < 0) { 4788c2ecf20Sopenharmony_ci wl1251_error("cmd scan failed: %d", ret); 4798c2ecf20Sopenharmony_ci goto out; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (cmd->header.status != CMD_STATUS_SUCCESS) { 4858c2ecf20Sopenharmony_ci wl1251_error("cmd scan status wasn't success: %d", 4868c2ecf20Sopenharmony_ci cmd->header.status); 4878c2ecf20Sopenharmony_ci ret = -EIO; 4888c2ecf20Sopenharmony_ci goto out; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ciout: 4928c2ecf20Sopenharmony_ci kfree(cmd); 4938c2ecf20Sopenharmony_ci return ret; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ciint wl1251_cmd_trigger_scan_to(struct wl1251 *wl, u32 timeout) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct wl1251_cmd_trigger_scan_to *cmd; 4998c2ecf20Sopenharmony_ci int ret; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci wl1251_debug(DEBUG_CMD, "cmd trigger scan to"); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 5048c2ecf20Sopenharmony_ci if (!cmd) 5058c2ecf20Sopenharmony_ci return -ENOMEM; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci cmd->timeout = timeout; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci ret = wl1251_cmd_send(wl, CMD_TRIGGER_SCAN_TO, cmd, sizeof(*cmd)); 5108c2ecf20Sopenharmony_ci if (ret < 0) { 5118c2ecf20Sopenharmony_ci wl1251_error("cmd trigger scan to failed: %d", ret); 5128c2ecf20Sopenharmony_ci goto out; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ciout: 5168c2ecf20Sopenharmony_ci kfree(cmd); 5178c2ecf20Sopenharmony_ci return ret; 5188c2ecf20Sopenharmony_ci} 519