18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * WSM host interface (HI) implementation for 48c2ecf20Sopenharmony_ci * ST-Ericsson CW1200 mac80211 drivers. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2010, ST-Ericsson 78c2ecf20Sopenharmony_ci * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 118c2ecf20Sopenharmony_ci#include <linux/wait.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/sched.h> 148c2ecf20Sopenharmony_ci#include <linux/random.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "cw1200.h" 178c2ecf20Sopenharmony_ci#include "wsm.h" 188c2ecf20Sopenharmony_ci#include "bh.h" 198c2ecf20Sopenharmony_ci#include "sta.h" 208c2ecf20Sopenharmony_ci#include "debug.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */ 238c2ecf20Sopenharmony_ci#define WSM_CMD_START_TIMEOUT (7 * HZ) 248c2ecf20Sopenharmony_ci#define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */ 258c2ecf20Sopenharmony_ci#define WSM_CMD_MAX_TIMEOUT (3 * HZ) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define WSM_SKIP(buf, size) \ 288c2ecf20Sopenharmony_ci do { \ 298c2ecf20Sopenharmony_ci if ((buf)->data + size > (buf)->end) \ 308c2ecf20Sopenharmony_ci goto underflow; \ 318c2ecf20Sopenharmony_ci (buf)->data += size; \ 328c2ecf20Sopenharmony_ci } while (0) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define WSM_GET(buf, ptr, size) \ 358c2ecf20Sopenharmony_ci do { \ 368c2ecf20Sopenharmony_ci if ((buf)->data + size > (buf)->end) \ 378c2ecf20Sopenharmony_ci goto underflow; \ 388c2ecf20Sopenharmony_ci memcpy(ptr, (buf)->data, size); \ 398c2ecf20Sopenharmony_ci (buf)->data += size; \ 408c2ecf20Sopenharmony_ci } while (0) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define __WSM_GET(buf, type, type2, cvt) \ 438c2ecf20Sopenharmony_ci ({ \ 448c2ecf20Sopenharmony_ci type val; \ 458c2ecf20Sopenharmony_ci if ((buf)->data + sizeof(type) > (buf)->end) \ 468c2ecf20Sopenharmony_ci goto underflow; \ 478c2ecf20Sopenharmony_ci val = cvt(*(type2 *)(buf)->data); \ 488c2ecf20Sopenharmony_ci (buf)->data += sizeof(type); \ 498c2ecf20Sopenharmony_ci val; \ 508c2ecf20Sopenharmony_ci }) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define WSM_GET8(buf) __WSM_GET(buf, u8, u8, (u8)) 538c2ecf20Sopenharmony_ci#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16, __le16_to_cpu) 548c2ecf20Sopenharmony_ci#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32, __le32_to_cpu) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define WSM_PUT(buf, ptr, size) \ 578c2ecf20Sopenharmony_ci do { \ 588c2ecf20Sopenharmony_ci if ((buf)->data + size > (buf)->end) \ 598c2ecf20Sopenharmony_ci if (wsm_buf_reserve((buf), size)) \ 608c2ecf20Sopenharmony_ci goto nomem; \ 618c2ecf20Sopenharmony_ci memcpy((buf)->data, ptr, size); \ 628c2ecf20Sopenharmony_ci (buf)->data += size; \ 638c2ecf20Sopenharmony_ci } while (0) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define __WSM_PUT(buf, val, type, type2, cvt) \ 668c2ecf20Sopenharmony_ci do { \ 678c2ecf20Sopenharmony_ci if ((buf)->data + sizeof(type) > (buf)->end) \ 688c2ecf20Sopenharmony_ci if (wsm_buf_reserve((buf), sizeof(type))) \ 698c2ecf20Sopenharmony_ci goto nomem; \ 708c2ecf20Sopenharmony_ci *(type2 *)(buf)->data = cvt(val); \ 718c2ecf20Sopenharmony_ci (buf)->data += sizeof(type); \ 728c2ecf20Sopenharmony_ci } while (0) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, u8, (u8)) 758c2ecf20Sopenharmony_ci#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __le16, __cpu_to_le16) 768c2ecf20Sopenharmony_ci#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __le32, __cpu_to_le32) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void wsm_buf_reset(struct wsm_buf *buf); 798c2ecf20Sopenharmony_cistatic int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int wsm_cmd_send(struct cw1200_common *priv, 828c2ecf20Sopenharmony_ci struct wsm_buf *buf, 838c2ecf20Sopenharmony_ci void *arg, u16 cmd, long tmo); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define wsm_cmd_lock(__priv) mutex_lock(&((__priv)->wsm_cmd_mux)) 868c2ecf20Sopenharmony_ci#define wsm_cmd_unlock(__priv) mutex_unlock(&((__priv)->wsm_cmd_mux)) 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* ******************************************************************** */ 898c2ecf20Sopenharmony_ci/* WSM API implementation */ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int wsm_generic_confirm(struct cw1200_common *priv, 928c2ecf20Sopenharmony_ci void *arg, 938c2ecf20Sopenharmony_ci struct wsm_buf *buf) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci u32 status = WSM_GET32(buf); 968c2ecf20Sopenharmony_ci if (status != WSM_STATUS_SUCCESS) 978c2ecf20Sopenharmony_ci return -EINVAL; 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ciunderflow: 1018c2ecf20Sopenharmony_ci WARN_ON(1); 1028c2ecf20Sopenharmony_ci return -EINVAL; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ciint wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci int ret; 1088c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime); 1138c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime); 1148c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->dot11RtsThreshold); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* DPD block. */ 1178c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->dpdData_size + 12); 1188c2ecf20Sopenharmony_ci WSM_PUT16(buf, 1); /* DPD version */ 1198c2ecf20Sopenharmony_ci WSM_PUT(buf, arg->dot11StationId, ETH_ALEN); 1208c2ecf20Sopenharmony_ci WSM_PUT16(buf, 5); /* DPD flags */ 1218c2ecf20Sopenharmony_ci WSM_PUT(buf, arg->dpdData, arg->dpdData_size); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, arg, 1248c2ecf20Sopenharmony_ci WSM_CONFIGURATION_REQ_ID, WSM_CMD_TIMEOUT); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 1278c2ecf20Sopenharmony_ci return ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cinomem: 1308c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 1318c2ecf20Sopenharmony_ci return -ENOMEM; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int wsm_configuration_confirm(struct cw1200_common *priv, 1358c2ecf20Sopenharmony_ci struct wsm_configuration *arg, 1368c2ecf20Sopenharmony_ci struct wsm_buf *buf) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci int i; 1398c2ecf20Sopenharmony_ci int status; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci status = WSM_GET32(buf); 1428c2ecf20Sopenharmony_ci if (WARN_ON(status != WSM_STATUS_SUCCESS)) 1438c2ecf20Sopenharmony_ci return -EINVAL; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci WSM_GET(buf, arg->dot11StationId, ETH_ALEN); 1468c2ecf20Sopenharmony_ci arg->dot11FrequencyBandsSupported = WSM_GET8(buf); 1478c2ecf20Sopenharmony_ci WSM_SKIP(buf, 1); 1488c2ecf20Sopenharmony_ci arg->supportedRateMask = WSM_GET32(buf); 1498c2ecf20Sopenharmony_ci for (i = 0; i < 2; ++i) { 1508c2ecf20Sopenharmony_ci arg->txPowerRange[i].min_power_level = WSM_GET32(buf); 1518c2ecf20Sopenharmony_ci arg->txPowerRange[i].max_power_level = WSM_GET32(buf); 1528c2ecf20Sopenharmony_ci arg->txPowerRange[i].stepping = WSM_GET32(buf); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ciunderflow: 1578c2ecf20Sopenharmony_ci WARN_ON(1); 1588c2ecf20Sopenharmony_ci return -EINVAL; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/* ******************************************************************** */ 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciint wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci int ret; 1668c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 1678c2ecf20Sopenharmony_ci u16 cmd = WSM_RESET_REQ_ID | WSM_TX_LINK_ID(arg->link_id); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->reset_statistics ? 0 : 1); 1728c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT); 1738c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 1748c2ecf20Sopenharmony_ci return ret; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cinomem: 1778c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 1788c2ecf20Sopenharmony_ci return -ENOMEM; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* ******************************************************************** */ 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistruct wsm_mib { 1848c2ecf20Sopenharmony_ci u16 mib_id; 1858c2ecf20Sopenharmony_ci void *buf; 1868c2ecf20Sopenharmony_ci size_t buf_size; 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ciint wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *_buf, 1908c2ecf20Sopenharmony_ci size_t buf_size) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci int ret; 1938c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 1948c2ecf20Sopenharmony_ci struct wsm_mib mib_buf = { 1958c2ecf20Sopenharmony_ci .mib_id = mib_id, 1968c2ecf20Sopenharmony_ci .buf = _buf, 1978c2ecf20Sopenharmony_ci .buf_size = buf_size, 1988c2ecf20Sopenharmony_ci }; 1998c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci WSM_PUT16(buf, mib_id); 2028c2ecf20Sopenharmony_ci WSM_PUT16(buf, 0); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, &mib_buf, 2058c2ecf20Sopenharmony_ci WSM_READ_MIB_REQ_ID, WSM_CMD_TIMEOUT); 2068c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 2078c2ecf20Sopenharmony_ci return ret; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cinomem: 2108c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 2118c2ecf20Sopenharmony_ci return -ENOMEM; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int wsm_read_mib_confirm(struct cw1200_common *priv, 2158c2ecf20Sopenharmony_ci struct wsm_mib *arg, 2168c2ecf20Sopenharmony_ci struct wsm_buf *buf) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci u16 size; 2198c2ecf20Sopenharmony_ci if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) 2208c2ecf20Sopenharmony_ci return -EINVAL; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (WARN_ON(WSM_GET16(buf) != arg->mib_id)) 2238c2ecf20Sopenharmony_ci return -EINVAL; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci size = WSM_GET16(buf); 2268c2ecf20Sopenharmony_ci if (size > arg->buf_size) 2278c2ecf20Sopenharmony_ci size = arg->buf_size; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci WSM_GET(buf, arg->buf, size); 2308c2ecf20Sopenharmony_ci arg->buf_size = size; 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ciunderflow: 2348c2ecf20Sopenharmony_ci WARN_ON(1); 2358c2ecf20Sopenharmony_ci return -EINVAL; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/* ******************************************************************** */ 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ciint wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *_buf, 2418c2ecf20Sopenharmony_ci size_t buf_size) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci int ret; 2448c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 2458c2ecf20Sopenharmony_ci struct wsm_mib mib_buf = { 2468c2ecf20Sopenharmony_ci .mib_id = mib_id, 2478c2ecf20Sopenharmony_ci .buf = _buf, 2488c2ecf20Sopenharmony_ci .buf_size = buf_size, 2498c2ecf20Sopenharmony_ci }; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci WSM_PUT16(buf, mib_id); 2548c2ecf20Sopenharmony_ci WSM_PUT16(buf, buf_size); 2558c2ecf20Sopenharmony_ci WSM_PUT(buf, _buf, buf_size); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, &mib_buf, 2588c2ecf20Sopenharmony_ci WSM_WRITE_MIB_REQ_ID, WSM_CMD_TIMEOUT); 2598c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 2608c2ecf20Sopenharmony_ci return ret; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cinomem: 2638c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 2648c2ecf20Sopenharmony_ci return -ENOMEM; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int wsm_write_mib_confirm(struct cw1200_common *priv, 2688c2ecf20Sopenharmony_ci struct wsm_mib *arg, 2698c2ecf20Sopenharmony_ci struct wsm_buf *buf) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci int ret; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci ret = wsm_generic_confirm(priv, arg, buf); 2748c2ecf20Sopenharmony_ci if (ret) 2758c2ecf20Sopenharmony_ci return ret; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (arg->mib_id == WSM_MIB_ID_OPERATIONAL_POWER_MODE) { 2788c2ecf20Sopenharmony_ci /* OperationalMode: update PM status. */ 2798c2ecf20Sopenharmony_ci const char *p = arg->buf; 2808c2ecf20Sopenharmony_ci cw1200_enable_powersave(priv, (p[0] & 0x0F) ? true : false); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/* ******************************************************************** */ 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ciint wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci int i; 2908c2ecf20Sopenharmony_ci int ret; 2918c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (arg->num_channels > 48) 2948c2ecf20Sopenharmony_ci return -EINVAL; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (arg->num_ssids > 2) 2978c2ecf20Sopenharmony_ci return -EINVAL; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (arg->band > 1) 3008c2ecf20Sopenharmony_ci return -EINVAL; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->band); 3058c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->type); 3068c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->flags); 3078c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->max_tx_rate); 3088c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->auto_scan_interval); 3098c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->num_probes); 3108c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->num_channels); 3118c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->num_ssids); 3128c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->probe_delay); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci for (i = 0; i < arg->num_channels; ++i) { 3158c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->ch[i].number); 3168c2ecf20Sopenharmony_ci WSM_PUT16(buf, 0); 3178c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->ch[i].min_chan_time); 3188c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->ch[i].max_chan_time); 3198c2ecf20Sopenharmony_ci WSM_PUT32(buf, 0); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci for (i = 0; i < arg->num_ssids; ++i) { 3238c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->ssids[i].length); 3248c2ecf20Sopenharmony_ci WSM_PUT(buf, &arg->ssids[i].ssid[0], 3258c2ecf20Sopenharmony_ci sizeof(arg->ssids[i].ssid)); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 3298c2ecf20Sopenharmony_ci WSM_START_SCAN_REQ_ID, WSM_CMD_TIMEOUT); 3308c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 3318c2ecf20Sopenharmony_ci return ret; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cinomem: 3348c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 3358c2ecf20Sopenharmony_ci return -ENOMEM; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci/* ******************************************************************** */ 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ciint wsm_stop_scan(struct cw1200_common *priv) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci int ret; 3438c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 3448c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 3458c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 3468c2ecf20Sopenharmony_ci WSM_STOP_SCAN_REQ_ID, WSM_CMD_TIMEOUT); 3478c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 3488c2ecf20Sopenharmony_ci return ret; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int wsm_tx_confirm(struct cw1200_common *priv, 3538c2ecf20Sopenharmony_ci struct wsm_buf *buf, 3548c2ecf20Sopenharmony_ci int link_id) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct wsm_tx_confirm tx_confirm; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci tx_confirm.packet_id = WSM_GET32(buf); 3598c2ecf20Sopenharmony_ci tx_confirm.status = WSM_GET32(buf); 3608c2ecf20Sopenharmony_ci tx_confirm.tx_rate = WSM_GET8(buf); 3618c2ecf20Sopenharmony_ci tx_confirm.ack_failures = WSM_GET8(buf); 3628c2ecf20Sopenharmony_ci tx_confirm.flags = WSM_GET16(buf); 3638c2ecf20Sopenharmony_ci tx_confirm.media_delay = WSM_GET32(buf); 3648c2ecf20Sopenharmony_ci tx_confirm.tx_queue_delay = WSM_GET32(buf); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci cw1200_tx_confirm_cb(priv, link_id, &tx_confirm); 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ciunderflow: 3708c2ecf20Sopenharmony_ci WARN_ON(1); 3718c2ecf20Sopenharmony_ci return -EINVAL; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int wsm_multi_tx_confirm(struct cw1200_common *priv, 3758c2ecf20Sopenharmony_ci struct wsm_buf *buf, int link_id) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci int ret; 3788c2ecf20Sopenharmony_ci int count; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci count = WSM_GET32(buf); 3818c2ecf20Sopenharmony_ci if (WARN_ON(count <= 0)) 3828c2ecf20Sopenharmony_ci return -EINVAL; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (count > 1) { 3858c2ecf20Sopenharmony_ci /* We already released one buffer, now for the rest */ 3868c2ecf20Sopenharmony_ci ret = wsm_release_tx_buffer(priv, count - 1); 3878c2ecf20Sopenharmony_ci if (ret < 0) 3888c2ecf20Sopenharmony_ci return ret; 3898c2ecf20Sopenharmony_ci else if (ret > 0) 3908c2ecf20Sopenharmony_ci cw1200_bh_wakeup(priv); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci cw1200_debug_txed_multi(priv, count); 3948c2ecf20Sopenharmony_ci do { 3958c2ecf20Sopenharmony_ci ret = wsm_tx_confirm(priv, buf, link_id); 3968c2ecf20Sopenharmony_ci } while (!ret && --count); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return ret; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ciunderflow: 4018c2ecf20Sopenharmony_ci WARN_ON(1); 4028c2ecf20Sopenharmony_ci return -EINVAL; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/* ******************************************************************** */ 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int wsm_join_confirm(struct cw1200_common *priv, 4088c2ecf20Sopenharmony_ci struct wsm_join_cnf *arg, 4098c2ecf20Sopenharmony_ci struct wsm_buf *buf) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci arg->status = WSM_GET32(buf); 4128c2ecf20Sopenharmony_ci if (WARN_ON(arg->status) != WSM_STATUS_SUCCESS) 4138c2ecf20Sopenharmony_ci return -EINVAL; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci arg->min_power_level = WSM_GET32(buf); 4168c2ecf20Sopenharmony_ci arg->max_power_level = WSM_GET32(buf); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ciunderflow: 4218c2ecf20Sopenharmony_ci WARN_ON(1); 4228c2ecf20Sopenharmony_ci return -EINVAL; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ciint wsm_join(struct cw1200_common *priv, struct wsm_join *arg) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci int ret; 4288c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 4298c2ecf20Sopenharmony_ci struct wsm_join_cnf resp; 4308c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->mode); 4338c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->band); 4348c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->channel_number); 4358c2ecf20Sopenharmony_ci WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid)); 4368c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->atim_window); 4378c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->preamble_type); 4388c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->probe_for_join); 4398c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->dtim_period); 4408c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->flags); 4418c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->ssid_len); 4428c2ecf20Sopenharmony_ci WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid)); 4438c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->beacon_interval); 4448c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->basic_rate_set); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci priv->tx_burst_idx = -1; 4478c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, &resp, 4488c2ecf20Sopenharmony_ci WSM_JOIN_REQ_ID, WSM_CMD_TIMEOUT); 4498c2ecf20Sopenharmony_ci /* TODO: Update state based on resp.min|max_power_level */ 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci priv->join_complete_status = resp.status; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 4548c2ecf20Sopenharmony_ci return ret; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cinomem: 4578c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 4588c2ecf20Sopenharmony_ci return -ENOMEM; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci/* ******************************************************************** */ 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ciint wsm_set_bss_params(struct cw1200_common *priv, 4648c2ecf20Sopenharmony_ci const struct wsm_set_bss_params *arg) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci int ret; 4678c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci WSM_PUT8(buf, (arg->reset_beacon_loss ? 0x1 : 0)); 4728c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->beacon_lost_count); 4738c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->aid); 4748c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->operational_rate_set); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 4778c2ecf20Sopenharmony_ci WSM_SET_BSS_PARAMS_REQ_ID, WSM_CMD_TIMEOUT); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 4808c2ecf20Sopenharmony_ci return ret; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cinomem: 4838c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 4848c2ecf20Sopenharmony_ci return -ENOMEM; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci/* ******************************************************************** */ 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ciint wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci int ret; 4928c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci WSM_PUT(buf, arg, sizeof(*arg)); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 4998c2ecf20Sopenharmony_ci WSM_ADD_KEY_REQ_ID, WSM_CMD_TIMEOUT); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 5028c2ecf20Sopenharmony_ci return ret; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cinomem: 5058c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 5068c2ecf20Sopenharmony_ci return -ENOMEM; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci/* ******************************************************************** */ 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ciint wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci int ret; 5148c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->index); 5198c2ecf20Sopenharmony_ci WSM_PUT8(buf, 0); 5208c2ecf20Sopenharmony_ci WSM_PUT16(buf, 0); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 5238c2ecf20Sopenharmony_ci WSM_REMOVE_KEY_REQ_ID, WSM_CMD_TIMEOUT); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 5268c2ecf20Sopenharmony_ci return ret; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cinomem: 5298c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 5308c2ecf20Sopenharmony_ci return -ENOMEM; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci/* ******************************************************************** */ 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ciint wsm_set_tx_queue_params(struct cw1200_common *priv, 5368c2ecf20Sopenharmony_ci const struct wsm_set_tx_queue_params *arg, u8 id) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci int ret; 5398c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 5408c2ecf20Sopenharmony_ci u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1}; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci WSM_PUT8(buf, queue_id_to_wmm_aci[id]); 5458c2ecf20Sopenharmony_ci WSM_PUT8(buf, 0); 5468c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->ackPolicy); 5478c2ecf20Sopenharmony_ci WSM_PUT8(buf, 0); 5488c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->maxTransmitLifetime); 5498c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->allowedMediumTime); 5508c2ecf20Sopenharmony_ci WSM_PUT16(buf, 0); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 5558c2ecf20Sopenharmony_ci return ret; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cinomem: 5588c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 5598c2ecf20Sopenharmony_ci return -ENOMEM; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci/* ******************************************************************** */ 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ciint wsm_set_edca_params(struct cw1200_common *priv, 5658c2ecf20Sopenharmony_ci const struct wsm_edca_params *arg) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci int ret; 5688c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* Implemented according to specification. */ 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->params[3].cwmin); 5758c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->params[2].cwmin); 5768c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->params[1].cwmin); 5778c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->params[0].cwmin); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->params[3].cwmax); 5808c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->params[2].cwmax); 5818c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->params[1].cwmax); 5828c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->params[0].cwmax); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->params[3].aifns); 5858c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->params[2].aifns); 5868c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->params[1].aifns); 5878c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->params[0].aifns); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->params[3].txop_limit); 5908c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->params[2].txop_limit); 5918c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->params[1].txop_limit); 5928c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->params[0].txop_limit); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->params[3].max_rx_lifetime); 5958c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->params[2].max_rx_lifetime); 5968c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->params[1].max_rx_lifetime); 5978c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->params[0].max_rx_lifetime); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 6008c2ecf20Sopenharmony_ci WSM_EDCA_PARAMS_REQ_ID, WSM_CMD_TIMEOUT); 6018c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 6028c2ecf20Sopenharmony_ci return ret; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_cinomem: 6058c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 6068c2ecf20Sopenharmony_ci return -ENOMEM; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci/* ******************************************************************** */ 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ciint wsm_switch_channel(struct cw1200_common *priv, 6128c2ecf20Sopenharmony_ci const struct wsm_switch_channel *arg) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci int ret; 6158c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->mode); 6208c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->switch_count); 6218c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->channel_number); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci priv->channel_switch_in_progress = 1; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 6268c2ecf20Sopenharmony_ci WSM_SWITCH_CHANNEL_REQ_ID, WSM_CMD_TIMEOUT); 6278c2ecf20Sopenharmony_ci if (ret) 6288c2ecf20Sopenharmony_ci priv->channel_switch_in_progress = 0; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 6318c2ecf20Sopenharmony_ci return ret; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cinomem: 6348c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 6358c2ecf20Sopenharmony_ci return -ENOMEM; 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci/* ******************************************************************** */ 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ciint wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci int ret; 6438c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 6448c2ecf20Sopenharmony_ci priv->ps_mode_switch_in_progress = 1; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->mode); 6498c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->fast_psm_idle_period); 6508c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->ap_psm_change_period); 6518c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->min_auto_pspoll_period); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 6548c2ecf20Sopenharmony_ci WSM_SET_PM_REQ_ID, WSM_CMD_TIMEOUT); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 6578c2ecf20Sopenharmony_ci return ret; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cinomem: 6608c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 6618c2ecf20Sopenharmony_ci return -ENOMEM; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci/* ******************************************************************** */ 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ciint wsm_start(struct cw1200_common *priv, const struct wsm_start *arg) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci int ret; 6698c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->mode); 6748c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->band); 6758c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->channel_number); 6768c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->ct_window); 6778c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->beacon_interval); 6788c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->dtim_period); 6798c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->preamble); 6808c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->probe_delay); 6818c2ecf20Sopenharmony_ci WSM_PUT8(buf, arg->ssid_len); 6828c2ecf20Sopenharmony_ci WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); 6838c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->basic_rate_set); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci priv->tx_burst_idx = -1; 6868c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 6878c2ecf20Sopenharmony_ci WSM_START_REQ_ID, WSM_CMD_START_TIMEOUT); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 6908c2ecf20Sopenharmony_ci return ret; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cinomem: 6938c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 6948c2ecf20Sopenharmony_ci return -ENOMEM; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci/* ******************************************************************** */ 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ciint wsm_beacon_transmit(struct cw1200_common *priv, 7008c2ecf20Sopenharmony_ci const struct wsm_beacon_transmit *arg) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci int ret; 7038c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci WSM_PUT32(buf, arg->enable_beaconing ? 1 : 0); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 7108c2ecf20Sopenharmony_ci WSM_BEACON_TRANSMIT_REQ_ID, WSM_CMD_TIMEOUT); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 7138c2ecf20Sopenharmony_ci return ret; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cinomem: 7168c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 7178c2ecf20Sopenharmony_ci return -ENOMEM; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci/* ******************************************************************** */ 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ciint wsm_start_find(struct cw1200_common *priv) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci int ret; 7258c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 7288c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT); 7298c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 7308c2ecf20Sopenharmony_ci return ret; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci/* ******************************************************************** */ 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ciint wsm_stop_find(struct cw1200_common *priv) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci int ret; 7388c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 7418c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT); 7428c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 7438c2ecf20Sopenharmony_ci return ret; 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci/* ******************************************************************** */ 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ciint wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci int ret; 7518c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 7528c2ecf20Sopenharmony_ci u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr)); 7578c2ecf20Sopenharmony_ci WSM_PUT16(buf, 0); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 7628c2ecf20Sopenharmony_ci return ret; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cinomem: 7658c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 7668c2ecf20Sopenharmony_ci return -ENOMEM; 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci/* ******************************************************************** */ 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ciint wsm_update_ie(struct cw1200_common *priv, 7728c2ecf20Sopenharmony_ci const struct wsm_update_ie *arg) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci int ret; 7758c2ecf20Sopenharmony_ci struct wsm_buf *buf = &priv->wsm_cmd_buf; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->what); 7808c2ecf20Sopenharmony_ci WSM_PUT16(buf, arg->count); 7818c2ecf20Sopenharmony_ci WSM_PUT(buf, arg->ies, arg->length); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 7868c2ecf20Sopenharmony_ci return ret; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cinomem: 7898c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 7908c2ecf20Sopenharmony_ci return -ENOMEM; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci/* ******************************************************************** */ 7948c2ecf20Sopenharmony_ciint wsm_set_probe_responder(struct cw1200_common *priv, bool enable) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci priv->rx_filter.probeResponder = enable; 7978c2ecf20Sopenharmony_ci return wsm_set_rx_filter(priv, &priv->rx_filter); 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci/* ******************************************************************** */ 8018c2ecf20Sopenharmony_ci/* WSM indication events implementation */ 8028c2ecf20Sopenharmony_ciconst char * const cw1200_fw_types[] = { 8038c2ecf20Sopenharmony_ci "ETF", 8048c2ecf20Sopenharmony_ci "WFM", 8058c2ecf20Sopenharmony_ci "WSM", 8068c2ecf20Sopenharmony_ci "HI test", 8078c2ecf20Sopenharmony_ci "Platform test" 8088c2ecf20Sopenharmony_ci}; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic int wsm_startup_indication(struct cw1200_common *priv, 8118c2ecf20Sopenharmony_ci struct wsm_buf *buf) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci priv->wsm_caps.input_buffers = WSM_GET16(buf); 8148c2ecf20Sopenharmony_ci priv->wsm_caps.input_buffer_size = WSM_GET16(buf); 8158c2ecf20Sopenharmony_ci priv->wsm_caps.hw_id = WSM_GET16(buf); 8168c2ecf20Sopenharmony_ci priv->wsm_caps.hw_subid = WSM_GET16(buf); 8178c2ecf20Sopenharmony_ci priv->wsm_caps.status = WSM_GET16(buf); 8188c2ecf20Sopenharmony_ci priv->wsm_caps.fw_cap = WSM_GET16(buf); 8198c2ecf20Sopenharmony_ci priv->wsm_caps.fw_type = WSM_GET16(buf); 8208c2ecf20Sopenharmony_ci priv->wsm_caps.fw_api = WSM_GET16(buf); 8218c2ecf20Sopenharmony_ci priv->wsm_caps.fw_build = WSM_GET16(buf); 8228c2ecf20Sopenharmony_ci priv->wsm_caps.fw_ver = WSM_GET16(buf); 8238c2ecf20Sopenharmony_ci WSM_GET(buf, priv->wsm_caps.fw_label, sizeof(priv->wsm_caps.fw_label)); 8248c2ecf20Sopenharmony_ci priv->wsm_caps.fw_label[sizeof(priv->wsm_caps.fw_label) - 1] = 0; /* Do not trust FW too much... */ 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (WARN_ON(priv->wsm_caps.status)) 8278c2ecf20Sopenharmony_ci return -EINVAL; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (WARN_ON(priv->wsm_caps.fw_type > 4)) 8308c2ecf20Sopenharmony_ci return -EINVAL; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci pr_info("CW1200 WSM init done.\n" 8338c2ecf20Sopenharmony_ci " Input buffers: %d x %d bytes\n" 8348c2ecf20Sopenharmony_ci " Hardware: %d.%d\n" 8358c2ecf20Sopenharmony_ci " %s firmware [%s], ver: %d, build: %d," 8368c2ecf20Sopenharmony_ci " api: %d, cap: 0x%.4X\n", 8378c2ecf20Sopenharmony_ci priv->wsm_caps.input_buffers, 8388c2ecf20Sopenharmony_ci priv->wsm_caps.input_buffer_size, 8398c2ecf20Sopenharmony_ci priv->wsm_caps.hw_id, priv->wsm_caps.hw_subid, 8408c2ecf20Sopenharmony_ci cw1200_fw_types[priv->wsm_caps.fw_type], 8418c2ecf20Sopenharmony_ci priv->wsm_caps.fw_label, priv->wsm_caps.fw_ver, 8428c2ecf20Sopenharmony_ci priv->wsm_caps.fw_build, 8438c2ecf20Sopenharmony_ci priv->wsm_caps.fw_api, priv->wsm_caps.fw_cap); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* Disable unsupported frequency bands */ 8468c2ecf20Sopenharmony_ci if (!(priv->wsm_caps.fw_cap & 0x1)) 8478c2ecf20Sopenharmony_ci priv->hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL; 8488c2ecf20Sopenharmony_ci if (!(priv->wsm_caps.fw_cap & 0x2)) 8498c2ecf20Sopenharmony_ci priv->hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci priv->firmware_ready = 1; 8528c2ecf20Sopenharmony_ci wake_up(&priv->wsm_startup_done); 8538c2ecf20Sopenharmony_ci return 0; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ciunderflow: 8568c2ecf20Sopenharmony_ci WARN_ON(1); 8578c2ecf20Sopenharmony_ci return -EINVAL; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic int wsm_receive_indication(struct cw1200_common *priv, 8618c2ecf20Sopenharmony_ci int link_id, 8628c2ecf20Sopenharmony_ci struct wsm_buf *buf, 8638c2ecf20Sopenharmony_ci struct sk_buff **skb_p) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci struct wsm_rx rx; 8668c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr; 8678c2ecf20Sopenharmony_ci size_t hdr_len; 8688c2ecf20Sopenharmony_ci __le16 fctl; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci rx.status = WSM_GET32(buf); 8718c2ecf20Sopenharmony_ci rx.channel_number = WSM_GET16(buf); 8728c2ecf20Sopenharmony_ci rx.rx_rate = WSM_GET8(buf); 8738c2ecf20Sopenharmony_ci rx.rcpi_rssi = WSM_GET8(buf); 8748c2ecf20Sopenharmony_ci rx.flags = WSM_GET32(buf); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci /* FW Workaround: Drop probe resp or 8778c2ecf20Sopenharmony_ci beacon when RSSI is 0 8788c2ecf20Sopenharmony_ci */ 8798c2ecf20Sopenharmony_ci hdr = (struct ieee80211_hdr *)(*skb_p)->data; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci if (!rx.rcpi_rssi && 8828c2ecf20Sopenharmony_ci (ieee80211_is_probe_resp(hdr->frame_control) || 8838c2ecf20Sopenharmony_ci ieee80211_is_beacon(hdr->frame_control))) 8848c2ecf20Sopenharmony_ci return 0; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci /* If no RSSI subscription has been made, 8878c2ecf20Sopenharmony_ci * convert RCPI to RSSI here 8888c2ecf20Sopenharmony_ci */ 8898c2ecf20Sopenharmony_ci if (!priv->cqm_use_rssi) 8908c2ecf20Sopenharmony_ci rx.rcpi_rssi = rx.rcpi_rssi / 2 - 110; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci fctl = *(__le16 *)buf->data; 8938c2ecf20Sopenharmony_ci hdr_len = buf->data - buf->begin; 8948c2ecf20Sopenharmony_ci skb_pull(*skb_p, hdr_len); 8958c2ecf20Sopenharmony_ci if (!rx.status && ieee80211_is_deauth(fctl)) { 8968c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_STA) { 8978c2ecf20Sopenharmony_ci /* Shedule unjoin work */ 8988c2ecf20Sopenharmony_ci pr_debug("[WSM] Issue unjoin command (RX).\n"); 8998c2ecf20Sopenharmony_ci wsm_lock_tx_async(priv); 9008c2ecf20Sopenharmony_ci if (queue_work(priv->workqueue, 9018c2ecf20Sopenharmony_ci &priv->unjoin_work) <= 0) 9028c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci cw1200_rx_cb(priv, &rx, link_id, skb_p); 9068c2ecf20Sopenharmony_ci if (*skb_p) 9078c2ecf20Sopenharmony_ci skb_push(*skb_p, hdr_len); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci return 0; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ciunderflow: 9128c2ecf20Sopenharmony_ci return -EINVAL; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf) 9168c2ecf20Sopenharmony_ci{ 9178c2ecf20Sopenharmony_ci int first; 9188c2ecf20Sopenharmony_ci struct cw1200_wsm_event *event; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { 9218c2ecf20Sopenharmony_ci /* STA is stopped. */ 9228c2ecf20Sopenharmony_ci return 0; 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL); 9268c2ecf20Sopenharmony_ci if (!event) 9278c2ecf20Sopenharmony_ci return -ENOMEM; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci event->evt.id = WSM_GET32(buf); 9308c2ecf20Sopenharmony_ci event->evt.data = WSM_GET32(buf); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci pr_debug("[WSM] Event: %d(%d)\n", 9338c2ecf20Sopenharmony_ci event->evt.id, event->evt.data); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci spin_lock(&priv->event_queue_lock); 9368c2ecf20Sopenharmony_ci first = list_empty(&priv->event_queue); 9378c2ecf20Sopenharmony_ci list_add_tail(&event->link, &priv->event_queue); 9388c2ecf20Sopenharmony_ci spin_unlock(&priv->event_queue_lock); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if (first) 9418c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->event_handler); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci return 0; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ciunderflow: 9468c2ecf20Sopenharmony_ci kfree(event); 9478c2ecf20Sopenharmony_ci return -EINVAL; 9488c2ecf20Sopenharmony_ci} 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cistatic int wsm_channel_switch_indication(struct cw1200_common *priv, 9518c2ecf20Sopenharmony_ci struct wsm_buf *buf) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci WARN_ON(WSM_GET32(buf)); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci priv->channel_switch_in_progress = 0; 9568c2ecf20Sopenharmony_ci wake_up(&priv->channel_switch_done); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci return 0; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ciunderflow: 9638c2ecf20Sopenharmony_ci return -EINVAL; 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic int wsm_set_pm_indication(struct cw1200_common *priv, 9678c2ecf20Sopenharmony_ci struct wsm_buf *buf) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci /* TODO: Check buf (struct wsm_set_pm_complete) for validity */ 9708c2ecf20Sopenharmony_ci if (priv->ps_mode_switch_in_progress) { 9718c2ecf20Sopenharmony_ci priv->ps_mode_switch_in_progress = 0; 9728c2ecf20Sopenharmony_ci wake_up(&priv->ps_mode_switch_done); 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci return 0; 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_cistatic int wsm_scan_started(struct cw1200_common *priv, void *arg, 9788c2ecf20Sopenharmony_ci struct wsm_buf *buf) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci u32 status = WSM_GET32(buf); 9818c2ecf20Sopenharmony_ci if (status != WSM_STATUS_SUCCESS) { 9828c2ecf20Sopenharmony_ci cw1200_scan_failed_cb(priv); 9838c2ecf20Sopenharmony_ci return -EINVAL; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci return 0; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ciunderflow: 9888c2ecf20Sopenharmony_ci WARN_ON(1); 9898c2ecf20Sopenharmony_ci return -EINVAL; 9908c2ecf20Sopenharmony_ci} 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cistatic int wsm_scan_complete_indication(struct cw1200_common *priv, 9938c2ecf20Sopenharmony_ci struct wsm_buf *buf) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci struct wsm_scan_complete arg; 9968c2ecf20Sopenharmony_ci arg.status = WSM_GET32(buf); 9978c2ecf20Sopenharmony_ci arg.psm = WSM_GET8(buf); 9988c2ecf20Sopenharmony_ci arg.num_channels = WSM_GET8(buf); 9998c2ecf20Sopenharmony_ci cw1200_scan_complete_cb(priv, &arg); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci return 0; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ciunderflow: 10048c2ecf20Sopenharmony_ci return -EINVAL; 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_cistatic int wsm_join_complete_indication(struct cw1200_common *priv, 10088c2ecf20Sopenharmony_ci struct wsm_buf *buf) 10098c2ecf20Sopenharmony_ci{ 10108c2ecf20Sopenharmony_ci struct wsm_join_complete arg; 10118c2ecf20Sopenharmony_ci arg.status = WSM_GET32(buf); 10128c2ecf20Sopenharmony_ci pr_debug("[WSM] Join complete indication, status: %d\n", arg.status); 10138c2ecf20Sopenharmony_ci cw1200_join_complete_cb(priv, &arg); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci return 0; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ciunderflow: 10188c2ecf20Sopenharmony_ci return -EINVAL; 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_cistatic int wsm_find_complete_indication(struct cw1200_common *priv, 10228c2ecf20Sopenharmony_ci struct wsm_buf *buf) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci pr_warn("Implement find_complete_indication\n"); 10258c2ecf20Sopenharmony_ci return 0; 10268c2ecf20Sopenharmony_ci} 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_cistatic int wsm_ba_timeout_indication(struct cw1200_common *priv, 10298c2ecf20Sopenharmony_ci struct wsm_buf *buf) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci u8 tid; 10328c2ecf20Sopenharmony_ci u8 addr[ETH_ALEN]; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci WSM_GET32(buf); 10358c2ecf20Sopenharmony_ci tid = WSM_GET8(buf); 10368c2ecf20Sopenharmony_ci WSM_GET8(buf); 10378c2ecf20Sopenharmony_ci WSM_GET(buf, addr, ETH_ALEN); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci pr_info("BlockACK timeout, tid %d, addr %pM\n", 10408c2ecf20Sopenharmony_ci tid, addr); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci return 0; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ciunderflow: 10458c2ecf20Sopenharmony_ci return -EINVAL; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_cistatic int wsm_suspend_resume_indication(struct cw1200_common *priv, 10498c2ecf20Sopenharmony_ci int link_id, struct wsm_buf *buf) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci u32 flags; 10528c2ecf20Sopenharmony_ci struct wsm_suspend_resume arg; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci flags = WSM_GET32(buf); 10558c2ecf20Sopenharmony_ci arg.link_id = link_id; 10568c2ecf20Sopenharmony_ci arg.stop = !(flags & 1); 10578c2ecf20Sopenharmony_ci arg.multicast = !!(flags & 8); 10588c2ecf20Sopenharmony_ci arg.queue = (flags >> 1) & 3; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci cw1200_suspend_resume(priv, &arg); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci return 0; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ciunderflow: 10658c2ecf20Sopenharmony_ci return -EINVAL; 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci/* ******************************************************************** */ 10708c2ecf20Sopenharmony_ci/* WSM TX */ 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic int wsm_cmd_send(struct cw1200_common *priv, 10738c2ecf20Sopenharmony_ci struct wsm_buf *buf, 10748c2ecf20Sopenharmony_ci void *arg, u16 cmd, long tmo) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci size_t buf_len = buf->data - buf->begin; 10778c2ecf20Sopenharmony_ci int ret; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci /* Don't bother if we're dead. */ 10808c2ecf20Sopenharmony_ci if (priv->bh_error) { 10818c2ecf20Sopenharmony_ci ret = 0; 10828c2ecf20Sopenharmony_ci goto done; 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci /* Block until the cmd buffer is completed. Tortuous. */ 10868c2ecf20Sopenharmony_ci spin_lock(&priv->wsm_cmd.lock); 10878c2ecf20Sopenharmony_ci while (!priv->wsm_cmd.done) { 10888c2ecf20Sopenharmony_ci spin_unlock(&priv->wsm_cmd.lock); 10898c2ecf20Sopenharmony_ci spin_lock(&priv->wsm_cmd.lock); 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci priv->wsm_cmd.done = 0; 10928c2ecf20Sopenharmony_ci spin_unlock(&priv->wsm_cmd.lock); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci if (cmd == WSM_WRITE_MIB_REQ_ID || 10958c2ecf20Sopenharmony_ci cmd == WSM_READ_MIB_REQ_ID) 10968c2ecf20Sopenharmony_ci pr_debug("[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%zu)\n", 10978c2ecf20Sopenharmony_ci cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]), 10988c2ecf20Sopenharmony_ci buf_len); 10998c2ecf20Sopenharmony_ci else 11008c2ecf20Sopenharmony_ci pr_debug("[WSM] >>> 0x%.4X (%zu)\n", cmd, buf_len); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci /* Due to buggy SPI on CW1200, we need to 11038c2ecf20Sopenharmony_ci * pad the message by a few bytes to ensure 11048c2ecf20Sopenharmony_ci * that it's completely received. 11058c2ecf20Sopenharmony_ci */ 11068c2ecf20Sopenharmony_ci buf_len += 4; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci /* Fill HI message header */ 11098c2ecf20Sopenharmony_ci /* BH will add sequence number */ 11108c2ecf20Sopenharmony_ci ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); 11118c2ecf20Sopenharmony_ci ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci spin_lock(&priv->wsm_cmd.lock); 11148c2ecf20Sopenharmony_ci BUG_ON(priv->wsm_cmd.ptr); 11158c2ecf20Sopenharmony_ci priv->wsm_cmd.ptr = buf->begin; 11168c2ecf20Sopenharmony_ci priv->wsm_cmd.len = buf_len; 11178c2ecf20Sopenharmony_ci priv->wsm_cmd.arg = arg; 11188c2ecf20Sopenharmony_ci priv->wsm_cmd.cmd = cmd; 11198c2ecf20Sopenharmony_ci spin_unlock(&priv->wsm_cmd.lock); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci cw1200_bh_wakeup(priv); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci /* Wait for command completion */ 11248c2ecf20Sopenharmony_ci ret = wait_event_timeout(priv->wsm_cmd_wq, 11258c2ecf20Sopenharmony_ci priv->wsm_cmd.done, tmo); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci if (!ret && !priv->wsm_cmd.done) { 11288c2ecf20Sopenharmony_ci spin_lock(&priv->wsm_cmd.lock); 11298c2ecf20Sopenharmony_ci priv->wsm_cmd.done = 1; 11308c2ecf20Sopenharmony_ci priv->wsm_cmd.ptr = NULL; 11318c2ecf20Sopenharmony_ci spin_unlock(&priv->wsm_cmd.lock); 11328c2ecf20Sopenharmony_ci if (priv->bh_error) { 11338c2ecf20Sopenharmony_ci /* Return ok to help system cleanup */ 11348c2ecf20Sopenharmony_ci ret = 0; 11358c2ecf20Sopenharmony_ci } else { 11368c2ecf20Sopenharmony_ci pr_err("CMD req (0x%04x) stuck in firmware, killing BH\n", priv->wsm_cmd.cmd); 11378c2ecf20Sopenharmony_ci print_hex_dump_bytes("REQDUMP: ", DUMP_PREFIX_NONE, 11388c2ecf20Sopenharmony_ci buf->begin, buf_len); 11398c2ecf20Sopenharmony_ci pr_err("Outstanding outgoing frames: %d\n", priv->hw_bufs_used); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci /* Kill BH thread to report the error to the top layer. */ 11428c2ecf20Sopenharmony_ci atomic_add(1, &priv->bh_term); 11438c2ecf20Sopenharmony_ci wake_up(&priv->bh_wq); 11448c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci } else { 11478c2ecf20Sopenharmony_ci spin_lock(&priv->wsm_cmd.lock); 11488c2ecf20Sopenharmony_ci BUG_ON(!priv->wsm_cmd.done); 11498c2ecf20Sopenharmony_ci ret = priv->wsm_cmd.ret; 11508c2ecf20Sopenharmony_ci spin_unlock(&priv->wsm_cmd.lock); 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_cidone: 11538c2ecf20Sopenharmony_ci wsm_buf_reset(buf); 11548c2ecf20Sopenharmony_ci return ret; 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci/* ******************************************************************** */ 11588c2ecf20Sopenharmony_ci/* WSM TX port control */ 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_civoid wsm_lock_tx(struct cw1200_common *priv) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci wsm_cmd_lock(priv); 11638c2ecf20Sopenharmony_ci if (atomic_add_return(1, &priv->tx_lock) == 1) { 11648c2ecf20Sopenharmony_ci if (wsm_flush_tx(priv)) 11658c2ecf20Sopenharmony_ci pr_debug("[WSM] TX is locked.\n"); 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci wsm_cmd_unlock(priv); 11688c2ecf20Sopenharmony_ci} 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_civoid wsm_lock_tx_async(struct cw1200_common *priv) 11718c2ecf20Sopenharmony_ci{ 11728c2ecf20Sopenharmony_ci if (atomic_add_return(1, &priv->tx_lock) == 1) 11738c2ecf20Sopenharmony_ci pr_debug("[WSM] TX is locked (async).\n"); 11748c2ecf20Sopenharmony_ci} 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_cibool wsm_flush_tx(struct cw1200_common *priv) 11778c2ecf20Sopenharmony_ci{ 11788c2ecf20Sopenharmony_ci unsigned long timestamp = jiffies; 11798c2ecf20Sopenharmony_ci bool pending = false; 11808c2ecf20Sopenharmony_ci long timeout; 11818c2ecf20Sopenharmony_ci int i; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* Flush must be called with TX lock held. */ 11848c2ecf20Sopenharmony_ci BUG_ON(!atomic_read(&priv->tx_lock)); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* First check if we really need to do something. 11878c2ecf20Sopenharmony_ci * It is safe to use unprotected access, as hw_bufs_used 11888c2ecf20Sopenharmony_ci * can only decrements. 11898c2ecf20Sopenharmony_ci */ 11908c2ecf20Sopenharmony_ci if (!priv->hw_bufs_used) 11918c2ecf20Sopenharmony_ci return true; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci if (priv->bh_error) { 11948c2ecf20Sopenharmony_ci /* In case of failure do not wait for magic. */ 11958c2ecf20Sopenharmony_ci pr_err("[WSM] Fatal error occurred, will not flush TX.\n"); 11968c2ecf20Sopenharmony_ci return false; 11978c2ecf20Sopenharmony_ci } else { 11988c2ecf20Sopenharmony_ci /* Get a timestamp of "oldest" frame */ 11998c2ecf20Sopenharmony_ci for (i = 0; i < 4; ++i) 12008c2ecf20Sopenharmony_ci pending |= cw1200_queue_get_xmit_timestamp( 12018c2ecf20Sopenharmony_ci &priv->tx_queue[i], 12028c2ecf20Sopenharmony_ci ×tamp, 0xffffffff); 12038c2ecf20Sopenharmony_ci /* If there's nothing pending, we're good */ 12048c2ecf20Sopenharmony_ci if (!pending) 12058c2ecf20Sopenharmony_ci return true; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies; 12088c2ecf20Sopenharmony_ci if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq, 12098c2ecf20Sopenharmony_ci !priv->hw_bufs_used, 12108c2ecf20Sopenharmony_ci timeout) <= 0) { 12118c2ecf20Sopenharmony_ci /* Hmmm... Not good. Frame had stuck in firmware. */ 12128c2ecf20Sopenharmony_ci priv->bh_error = 1; 12138c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "[WSM] TX Frames (%d) stuck in firmware, killing BH\n", priv->hw_bufs_used); 12148c2ecf20Sopenharmony_ci wake_up(&priv->bh_wq); 12158c2ecf20Sopenharmony_ci return false; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci /* Ok, everything is flushed. */ 12198c2ecf20Sopenharmony_ci return true; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_civoid wsm_unlock_tx(struct cw1200_common *priv) 12248c2ecf20Sopenharmony_ci{ 12258c2ecf20Sopenharmony_ci int tx_lock; 12268c2ecf20Sopenharmony_ci tx_lock = atomic_sub_return(1, &priv->tx_lock); 12278c2ecf20Sopenharmony_ci BUG_ON(tx_lock < 0); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci if (tx_lock == 0) { 12308c2ecf20Sopenharmony_ci if (!priv->bh_error) 12318c2ecf20Sopenharmony_ci cw1200_bh_wakeup(priv); 12328c2ecf20Sopenharmony_ci pr_debug("[WSM] TX is unlocked.\n"); 12338c2ecf20Sopenharmony_ci } 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci/* ******************************************************************** */ 12378c2ecf20Sopenharmony_ci/* WSM RX */ 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ciint wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci struct wsm_buf buf; 12428c2ecf20Sopenharmony_ci u32 reason; 12438c2ecf20Sopenharmony_ci u32 reg[18]; 12448c2ecf20Sopenharmony_ci char fname[48]; 12458c2ecf20Sopenharmony_ci unsigned int i; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci static const char * const reason_str[] = { 12488c2ecf20Sopenharmony_ci "undefined instruction", 12498c2ecf20Sopenharmony_ci "prefetch abort", 12508c2ecf20Sopenharmony_ci "data abort", 12518c2ecf20Sopenharmony_ci "unknown error", 12528c2ecf20Sopenharmony_ci }; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci buf.begin = buf.data = data; 12558c2ecf20Sopenharmony_ci buf.end = &buf.begin[len]; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci reason = WSM_GET32(&buf); 12588c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(reg); ++i) 12598c2ecf20Sopenharmony_ci reg[i] = WSM_GET32(&buf); 12608c2ecf20Sopenharmony_ci WSM_GET(&buf, fname, sizeof(fname)); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci if (reason < 4) 12638c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 12648c2ecf20Sopenharmony_ci "Firmware exception: %s.\n", 12658c2ecf20Sopenharmony_ci reason_str[reason]); 12668c2ecf20Sopenharmony_ci else 12678c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 12688c2ecf20Sopenharmony_ci "Firmware assert at %.*s, line %d\n", 12698c2ecf20Sopenharmony_ci (int) sizeof(fname), fname, reg[1]); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci for (i = 0; i < 12; i += 4) 12728c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 12738c2ecf20Sopenharmony_ci "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n", 12748c2ecf20Sopenharmony_ci i + 0, reg[i + 0], i + 1, reg[i + 1], 12758c2ecf20Sopenharmony_ci i + 2, reg[i + 2], i + 3, reg[i + 3]); 12768c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 12778c2ecf20Sopenharmony_ci "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n", 12788c2ecf20Sopenharmony_ci reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]); 12798c2ecf20Sopenharmony_ci i += 4; 12808c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, 12818c2ecf20Sopenharmony_ci "CPSR: 0x%.8X, SPSR: 0x%.8X\n", 12828c2ecf20Sopenharmony_ci reg[i + 0], reg[i + 1]); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE, 12858c2ecf20Sopenharmony_ci fname, sizeof(fname)); 12868c2ecf20Sopenharmony_ci return 0; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ciunderflow: 12898c2ecf20Sopenharmony_ci wiphy_err(priv->hw->wiphy, "Firmware exception.\n"); 12908c2ecf20Sopenharmony_ci print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, 12918c2ecf20Sopenharmony_ci data, len); 12928c2ecf20Sopenharmony_ci return -EINVAL; 12938c2ecf20Sopenharmony_ci} 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ciint wsm_handle_rx(struct cw1200_common *priv, u16 id, 12968c2ecf20Sopenharmony_ci struct wsm_hdr *wsm, struct sk_buff **skb_p) 12978c2ecf20Sopenharmony_ci{ 12988c2ecf20Sopenharmony_ci int ret = 0; 12998c2ecf20Sopenharmony_ci struct wsm_buf wsm_buf; 13008c2ecf20Sopenharmony_ci int link_id = (id >> 6) & 0x0F; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci /* Strip link id. */ 13038c2ecf20Sopenharmony_ci id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci wsm_buf.begin = (u8 *)&wsm[0]; 13068c2ecf20Sopenharmony_ci wsm_buf.data = (u8 *)&wsm[1]; 13078c2ecf20Sopenharmony_ci wsm_buf.end = &wsm_buf.begin[__le16_to_cpu(wsm->len)]; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci pr_debug("[WSM] <<< 0x%.4X (%td)\n", id, 13108c2ecf20Sopenharmony_ci wsm_buf.end - wsm_buf.begin); 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if (id == WSM_TX_CONFIRM_IND_ID) { 13138c2ecf20Sopenharmony_ci ret = wsm_tx_confirm(priv, &wsm_buf, link_id); 13148c2ecf20Sopenharmony_ci } else if (id == WSM_MULTI_TX_CONFIRM_ID) { 13158c2ecf20Sopenharmony_ci ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id); 13168c2ecf20Sopenharmony_ci } else if (id & 0x0400) { 13178c2ecf20Sopenharmony_ci void *wsm_arg; 13188c2ecf20Sopenharmony_ci u16 wsm_cmd; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci /* Do not trust FW too much. Protection against repeated 13218c2ecf20Sopenharmony_ci * response and race condition removal (see above). 13228c2ecf20Sopenharmony_ci */ 13238c2ecf20Sopenharmony_ci spin_lock(&priv->wsm_cmd.lock); 13248c2ecf20Sopenharmony_ci wsm_arg = priv->wsm_cmd.arg; 13258c2ecf20Sopenharmony_ci wsm_cmd = priv->wsm_cmd.cmd & 13268c2ecf20Sopenharmony_ci ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); 13278c2ecf20Sopenharmony_ci priv->wsm_cmd.cmd = 0xFFFF; 13288c2ecf20Sopenharmony_ci spin_unlock(&priv->wsm_cmd.lock); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci if (WARN_ON((id & ~0x0400) != wsm_cmd)) { 13318c2ecf20Sopenharmony_ci /* Note that any non-zero is a fatal retcode. */ 13328c2ecf20Sopenharmony_ci ret = -EINVAL; 13338c2ecf20Sopenharmony_ci goto out; 13348c2ecf20Sopenharmony_ci } 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci /* Note that wsm_arg can be NULL in case of timeout in 13378c2ecf20Sopenharmony_ci * wsm_cmd_send(). 13388c2ecf20Sopenharmony_ci */ 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci switch (id) { 13418c2ecf20Sopenharmony_ci case WSM_READ_MIB_RESP_ID: 13428c2ecf20Sopenharmony_ci if (wsm_arg) 13438c2ecf20Sopenharmony_ci ret = wsm_read_mib_confirm(priv, wsm_arg, 13448c2ecf20Sopenharmony_ci &wsm_buf); 13458c2ecf20Sopenharmony_ci break; 13468c2ecf20Sopenharmony_ci case WSM_WRITE_MIB_RESP_ID: 13478c2ecf20Sopenharmony_ci if (wsm_arg) 13488c2ecf20Sopenharmony_ci ret = wsm_write_mib_confirm(priv, wsm_arg, 13498c2ecf20Sopenharmony_ci &wsm_buf); 13508c2ecf20Sopenharmony_ci break; 13518c2ecf20Sopenharmony_ci case WSM_START_SCAN_RESP_ID: 13528c2ecf20Sopenharmony_ci if (wsm_arg) 13538c2ecf20Sopenharmony_ci ret = wsm_scan_started(priv, wsm_arg, &wsm_buf); 13548c2ecf20Sopenharmony_ci break; 13558c2ecf20Sopenharmony_ci case WSM_CONFIGURATION_RESP_ID: 13568c2ecf20Sopenharmony_ci if (wsm_arg) 13578c2ecf20Sopenharmony_ci ret = wsm_configuration_confirm(priv, wsm_arg, 13588c2ecf20Sopenharmony_ci &wsm_buf); 13598c2ecf20Sopenharmony_ci break; 13608c2ecf20Sopenharmony_ci case WSM_JOIN_RESP_ID: 13618c2ecf20Sopenharmony_ci if (wsm_arg) 13628c2ecf20Sopenharmony_ci ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf); 13638c2ecf20Sopenharmony_ci break; 13648c2ecf20Sopenharmony_ci case WSM_STOP_SCAN_RESP_ID: 13658c2ecf20Sopenharmony_ci case WSM_RESET_RESP_ID: 13668c2ecf20Sopenharmony_ci case WSM_ADD_KEY_RESP_ID: 13678c2ecf20Sopenharmony_ci case WSM_REMOVE_KEY_RESP_ID: 13688c2ecf20Sopenharmony_ci case WSM_SET_PM_RESP_ID: 13698c2ecf20Sopenharmony_ci case WSM_SET_BSS_PARAMS_RESP_ID: 13708c2ecf20Sopenharmony_ci case 0x0412: /* set_tx_queue_params */ 13718c2ecf20Sopenharmony_ci case WSM_EDCA_PARAMS_RESP_ID: 13728c2ecf20Sopenharmony_ci case WSM_SWITCH_CHANNEL_RESP_ID: 13738c2ecf20Sopenharmony_ci case WSM_START_RESP_ID: 13748c2ecf20Sopenharmony_ci case WSM_BEACON_TRANSMIT_RESP_ID: 13758c2ecf20Sopenharmony_ci case 0x0419: /* start_find */ 13768c2ecf20Sopenharmony_ci case 0x041A: /* stop_find */ 13778c2ecf20Sopenharmony_ci case 0x041B: /* update_ie */ 13788c2ecf20Sopenharmony_ci case 0x041C: /* map_link */ 13798c2ecf20Sopenharmony_ci WARN_ON(wsm_arg != NULL); 13808c2ecf20Sopenharmony_ci ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf); 13818c2ecf20Sopenharmony_ci if (ret) { 13828c2ecf20Sopenharmony_ci wiphy_warn(priv->hw->wiphy, 13838c2ecf20Sopenharmony_ci "wsm_generic_confirm failed for request 0x%04x.\n", 13848c2ecf20Sopenharmony_ci id & ~0x0400); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci /* often 0x407 and 0x410 occur, this means we're dead.. */ 13878c2ecf20Sopenharmony_ci if (priv->join_status >= CW1200_JOIN_STATUS_JOINING) { 13888c2ecf20Sopenharmony_ci wsm_lock_tx(priv); 13898c2ecf20Sopenharmony_ci if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) 13908c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci } 13938c2ecf20Sopenharmony_ci break; 13948c2ecf20Sopenharmony_ci default: 13958c2ecf20Sopenharmony_ci wiphy_warn(priv->hw->wiphy, 13968c2ecf20Sopenharmony_ci "Unrecognized confirmation 0x%04x\n", 13978c2ecf20Sopenharmony_ci id & ~0x0400); 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci spin_lock(&priv->wsm_cmd.lock); 14018c2ecf20Sopenharmony_ci priv->wsm_cmd.ret = ret; 14028c2ecf20Sopenharmony_ci priv->wsm_cmd.done = 1; 14038c2ecf20Sopenharmony_ci spin_unlock(&priv->wsm_cmd.lock); 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci ret = 0; /* Error response from device should ne stop BH. */ 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci wake_up(&priv->wsm_cmd_wq); 14088c2ecf20Sopenharmony_ci } else if (id & 0x0800) { 14098c2ecf20Sopenharmony_ci switch (id) { 14108c2ecf20Sopenharmony_ci case WSM_STARTUP_IND_ID: 14118c2ecf20Sopenharmony_ci ret = wsm_startup_indication(priv, &wsm_buf); 14128c2ecf20Sopenharmony_ci break; 14138c2ecf20Sopenharmony_ci case WSM_RECEIVE_IND_ID: 14148c2ecf20Sopenharmony_ci ret = wsm_receive_indication(priv, link_id, 14158c2ecf20Sopenharmony_ci &wsm_buf, skb_p); 14168c2ecf20Sopenharmony_ci break; 14178c2ecf20Sopenharmony_ci case 0x0805: 14188c2ecf20Sopenharmony_ci ret = wsm_event_indication(priv, &wsm_buf); 14198c2ecf20Sopenharmony_ci break; 14208c2ecf20Sopenharmony_ci case WSM_SCAN_COMPLETE_IND_ID: 14218c2ecf20Sopenharmony_ci ret = wsm_scan_complete_indication(priv, &wsm_buf); 14228c2ecf20Sopenharmony_ci break; 14238c2ecf20Sopenharmony_ci case 0x0808: 14248c2ecf20Sopenharmony_ci ret = wsm_ba_timeout_indication(priv, &wsm_buf); 14258c2ecf20Sopenharmony_ci break; 14268c2ecf20Sopenharmony_ci case 0x0809: 14278c2ecf20Sopenharmony_ci ret = wsm_set_pm_indication(priv, &wsm_buf); 14288c2ecf20Sopenharmony_ci break; 14298c2ecf20Sopenharmony_ci case 0x080A: 14308c2ecf20Sopenharmony_ci ret = wsm_channel_switch_indication(priv, &wsm_buf); 14318c2ecf20Sopenharmony_ci break; 14328c2ecf20Sopenharmony_ci case 0x080B: 14338c2ecf20Sopenharmony_ci ret = wsm_find_complete_indication(priv, &wsm_buf); 14348c2ecf20Sopenharmony_ci break; 14358c2ecf20Sopenharmony_ci case 0x080C: 14368c2ecf20Sopenharmony_ci ret = wsm_suspend_resume_indication(priv, 14378c2ecf20Sopenharmony_ci link_id, &wsm_buf); 14388c2ecf20Sopenharmony_ci break; 14398c2ecf20Sopenharmony_ci case 0x080F: 14408c2ecf20Sopenharmony_ci ret = wsm_join_complete_indication(priv, &wsm_buf); 14418c2ecf20Sopenharmony_ci break; 14428c2ecf20Sopenharmony_ci default: 14438c2ecf20Sopenharmony_ci pr_warn("Unrecognised WSM ID %04x\n", id); 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci } else { 14468c2ecf20Sopenharmony_ci WARN_ON(1); 14478c2ecf20Sopenharmony_ci ret = -EINVAL; 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ciout: 14508c2ecf20Sopenharmony_ci return ret; 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_cistatic bool wsm_handle_tx_data(struct cw1200_common *priv, 14548c2ecf20Sopenharmony_ci struct wsm_tx *wsm, 14558c2ecf20Sopenharmony_ci const struct ieee80211_tx_info *tx_info, 14568c2ecf20Sopenharmony_ci const struct cw1200_txpriv *txpriv, 14578c2ecf20Sopenharmony_ci struct cw1200_queue *queue) 14588c2ecf20Sopenharmony_ci{ 14598c2ecf20Sopenharmony_ci bool handled = false; 14608c2ecf20Sopenharmony_ci const struct ieee80211_hdr *frame = 14618c2ecf20Sopenharmony_ci (struct ieee80211_hdr *)&((u8 *)wsm)[txpriv->offset]; 14628c2ecf20Sopenharmony_ci __le16 fctl = frame->frame_control; 14638c2ecf20Sopenharmony_ci enum { 14648c2ecf20Sopenharmony_ci do_probe, 14658c2ecf20Sopenharmony_ci do_drop, 14668c2ecf20Sopenharmony_ci do_wep, 14678c2ecf20Sopenharmony_ci do_tx, 14688c2ecf20Sopenharmony_ci } action = do_tx; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci switch (priv->mode) { 14718c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 14728c2ecf20Sopenharmony_ci if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) 14738c2ecf20Sopenharmony_ci action = do_tx; 14748c2ecf20Sopenharmony_ci else if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) 14758c2ecf20Sopenharmony_ci action = do_drop; 14768c2ecf20Sopenharmony_ci break; 14778c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 14788c2ecf20Sopenharmony_ci if (!priv->join_status) { 14798c2ecf20Sopenharmony_ci action = do_drop; 14808c2ecf20Sopenharmony_ci } else if (!(BIT(txpriv->raw_link_id) & 14818c2ecf20Sopenharmony_ci (BIT(0) | priv->link_id_map))) { 14828c2ecf20Sopenharmony_ci wiphy_warn(priv->hw->wiphy, 14838c2ecf20Sopenharmony_ci "A frame with expired link id is dropped.\n"); 14848c2ecf20Sopenharmony_ci action = do_drop; 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci if (cw1200_queue_get_generation(wsm->packet_id) > 14878c2ecf20Sopenharmony_ci CW1200_MAX_REQUEUE_ATTEMPTS) { 14888c2ecf20Sopenharmony_ci /* HACK!!! WSM324 firmware has tendency to requeue 14898c2ecf20Sopenharmony_ci * multicast frames in a loop, causing performance 14908c2ecf20Sopenharmony_ci * drop and high power consumption of the driver. 14918c2ecf20Sopenharmony_ci * In this situation it is better just to drop 14928c2ecf20Sopenharmony_ci * the problematic frame. 14938c2ecf20Sopenharmony_ci */ 14948c2ecf20Sopenharmony_ci wiphy_warn(priv->hw->wiphy, 14958c2ecf20Sopenharmony_ci "Too many attempts to requeue a frame; dropped.\n"); 14968c2ecf20Sopenharmony_ci action = do_drop; 14978c2ecf20Sopenharmony_ci } 14988c2ecf20Sopenharmony_ci break; 14998c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 15008c2ecf20Sopenharmony_ci if (priv->join_status != CW1200_JOIN_STATUS_IBSS) 15018c2ecf20Sopenharmony_ci action = do_drop; 15028c2ecf20Sopenharmony_ci break; 15038c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 15048c2ecf20Sopenharmony_ci action = do_tx; /* TODO: Test me! */ 15058c2ecf20Sopenharmony_ci break; 15068c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 15078c2ecf20Sopenharmony_ci default: 15088c2ecf20Sopenharmony_ci action = do_drop; 15098c2ecf20Sopenharmony_ci break; 15108c2ecf20Sopenharmony_ci } 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci if (action == do_tx) { 15138c2ecf20Sopenharmony_ci if (ieee80211_is_nullfunc(fctl)) { 15148c2ecf20Sopenharmony_ci spin_lock(&priv->bss_loss_lock); 15158c2ecf20Sopenharmony_ci if (priv->bss_loss_state) { 15168c2ecf20Sopenharmony_ci priv->bss_loss_confirm_id = wsm->packet_id; 15178c2ecf20Sopenharmony_ci wsm->queue_id = WSM_QUEUE_VOICE; 15188c2ecf20Sopenharmony_ci } 15198c2ecf20Sopenharmony_ci spin_unlock(&priv->bss_loss_lock); 15208c2ecf20Sopenharmony_ci } else if (ieee80211_is_probe_req(fctl)) { 15218c2ecf20Sopenharmony_ci action = do_probe; 15228c2ecf20Sopenharmony_ci } else if (ieee80211_is_deauth(fctl) && 15238c2ecf20Sopenharmony_ci priv->mode != NL80211_IFTYPE_AP) { 15248c2ecf20Sopenharmony_ci pr_debug("[WSM] Issue unjoin command due to tx deauth.\n"); 15258c2ecf20Sopenharmony_ci wsm_lock_tx_async(priv); 15268c2ecf20Sopenharmony_ci if (queue_work(priv->workqueue, 15278c2ecf20Sopenharmony_ci &priv->unjoin_work) <= 0) 15288c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 15298c2ecf20Sopenharmony_ci } else if (ieee80211_has_protected(fctl) && 15308c2ecf20Sopenharmony_ci tx_info->control.hw_key && 15318c2ecf20Sopenharmony_ci tx_info->control.hw_key->keyidx != priv->wep_default_key_id && 15328c2ecf20Sopenharmony_ci (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 || 15338c2ecf20Sopenharmony_ci tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) { 15348c2ecf20Sopenharmony_ci action = do_wep; 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci switch (action) { 15398c2ecf20Sopenharmony_ci case do_probe: 15408c2ecf20Sopenharmony_ci /* An interesting FW "feature". Device filters probe responses. 15418c2ecf20Sopenharmony_ci * The easiest way to get it back is to convert 15428c2ecf20Sopenharmony_ci * probe request into WSM start_scan command. 15438c2ecf20Sopenharmony_ci */ 15448c2ecf20Sopenharmony_ci pr_debug("[WSM] Convert probe request to scan.\n"); 15458c2ecf20Sopenharmony_ci wsm_lock_tx_async(priv); 15468c2ecf20Sopenharmony_ci priv->pending_frame_id = wsm->packet_id; 15478c2ecf20Sopenharmony_ci if (queue_delayed_work(priv->workqueue, 15488c2ecf20Sopenharmony_ci &priv->scan.probe_work, 0) <= 0) 15498c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 15508c2ecf20Sopenharmony_ci handled = true; 15518c2ecf20Sopenharmony_ci break; 15528c2ecf20Sopenharmony_ci case do_drop: 15538c2ecf20Sopenharmony_ci pr_debug("[WSM] Drop frame (0x%.4X).\n", fctl); 15548c2ecf20Sopenharmony_ci BUG_ON(cw1200_queue_remove(queue, wsm->packet_id)); 15558c2ecf20Sopenharmony_ci handled = true; 15568c2ecf20Sopenharmony_ci break; 15578c2ecf20Sopenharmony_ci case do_wep: 15588c2ecf20Sopenharmony_ci pr_debug("[WSM] Issue set_default_wep_key.\n"); 15598c2ecf20Sopenharmony_ci wsm_lock_tx_async(priv); 15608c2ecf20Sopenharmony_ci priv->wep_default_key_id = tx_info->control.hw_key->keyidx; 15618c2ecf20Sopenharmony_ci priv->pending_frame_id = wsm->packet_id; 15628c2ecf20Sopenharmony_ci if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0) 15638c2ecf20Sopenharmony_ci wsm_unlock_tx(priv); 15648c2ecf20Sopenharmony_ci handled = true; 15658c2ecf20Sopenharmony_ci break; 15668c2ecf20Sopenharmony_ci case do_tx: 15678c2ecf20Sopenharmony_ci pr_debug("[WSM] Transmit frame.\n"); 15688c2ecf20Sopenharmony_ci break; 15698c2ecf20Sopenharmony_ci default: 15708c2ecf20Sopenharmony_ci /* Do nothing */ 15718c2ecf20Sopenharmony_ci break; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci return handled; 15748c2ecf20Sopenharmony_ci} 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_cistatic int cw1200_get_prio_queue(struct cw1200_common *priv, 15778c2ecf20Sopenharmony_ci u32 link_id_map, int *total) 15788c2ecf20Sopenharmony_ci{ 15798c2ecf20Sopenharmony_ci static const int urgent = BIT(CW1200_LINK_ID_AFTER_DTIM) | 15808c2ecf20Sopenharmony_ci BIT(CW1200_LINK_ID_UAPSD); 15818c2ecf20Sopenharmony_ci struct wsm_edca_queue_params *edca; 15828c2ecf20Sopenharmony_ci unsigned score, best = -1; 15838c2ecf20Sopenharmony_ci int winner = -1; 15848c2ecf20Sopenharmony_ci int queued; 15858c2ecf20Sopenharmony_ci int i; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci /* search for a winner using edca params */ 15888c2ecf20Sopenharmony_ci for (i = 0; i < 4; ++i) { 15898c2ecf20Sopenharmony_ci queued = cw1200_queue_get_num_queued(&priv->tx_queue[i], 15908c2ecf20Sopenharmony_ci link_id_map); 15918c2ecf20Sopenharmony_ci if (!queued) 15928c2ecf20Sopenharmony_ci continue; 15938c2ecf20Sopenharmony_ci *total += queued; 15948c2ecf20Sopenharmony_ci edca = &priv->edca.params[i]; 15958c2ecf20Sopenharmony_ci score = ((edca->aifns + edca->cwmin) << 16) + 15968c2ecf20Sopenharmony_ci ((edca->cwmax - edca->cwmin) * 15978c2ecf20Sopenharmony_ci (get_random_int() & 0xFFFF)); 15988c2ecf20Sopenharmony_ci if (score < best && (winner < 0 || i != 3)) { 15998c2ecf20Sopenharmony_ci best = score; 16008c2ecf20Sopenharmony_ci winner = i; 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci } 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci /* override winner if bursting */ 16058c2ecf20Sopenharmony_ci if (winner >= 0 && priv->tx_burst_idx >= 0 && 16068c2ecf20Sopenharmony_ci winner != priv->tx_burst_idx && 16078c2ecf20Sopenharmony_ci !cw1200_queue_get_num_queued( 16088c2ecf20Sopenharmony_ci &priv->tx_queue[winner], 16098c2ecf20Sopenharmony_ci link_id_map & urgent) && 16108c2ecf20Sopenharmony_ci cw1200_queue_get_num_queued( 16118c2ecf20Sopenharmony_ci &priv->tx_queue[priv->tx_burst_idx], 16128c2ecf20Sopenharmony_ci link_id_map)) 16138c2ecf20Sopenharmony_ci winner = priv->tx_burst_idx; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci return winner; 16168c2ecf20Sopenharmony_ci} 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_cistatic int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, 16198c2ecf20Sopenharmony_ci struct cw1200_queue **queue_p, 16208c2ecf20Sopenharmony_ci u32 *tx_allowed_mask_p, 16218c2ecf20Sopenharmony_ci bool *more) 16228c2ecf20Sopenharmony_ci{ 16238c2ecf20Sopenharmony_ci int idx; 16248c2ecf20Sopenharmony_ci u32 tx_allowed_mask; 16258c2ecf20Sopenharmony_ci int total = 0; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci /* Search for a queue with multicast frames buffered */ 16288c2ecf20Sopenharmony_ci if (priv->tx_multicast) { 16298c2ecf20Sopenharmony_ci tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM); 16308c2ecf20Sopenharmony_ci idx = cw1200_get_prio_queue(priv, 16318c2ecf20Sopenharmony_ci tx_allowed_mask, &total); 16328c2ecf20Sopenharmony_ci if (idx >= 0) { 16338c2ecf20Sopenharmony_ci *more = total > 1; 16348c2ecf20Sopenharmony_ci goto found; 16358c2ecf20Sopenharmony_ci } 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci /* Search for unicast traffic */ 16398c2ecf20Sopenharmony_ci tx_allowed_mask = ~priv->sta_asleep_mask; 16408c2ecf20Sopenharmony_ci tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD); 16418c2ecf20Sopenharmony_ci if (priv->sta_asleep_mask) { 16428c2ecf20Sopenharmony_ci tx_allowed_mask |= priv->pspoll_mask; 16438c2ecf20Sopenharmony_ci tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM); 16448c2ecf20Sopenharmony_ci } else { 16458c2ecf20Sopenharmony_ci tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM); 16468c2ecf20Sopenharmony_ci } 16478c2ecf20Sopenharmony_ci idx = cw1200_get_prio_queue(priv, 16488c2ecf20Sopenharmony_ci tx_allowed_mask, &total); 16498c2ecf20Sopenharmony_ci if (idx < 0) 16508c2ecf20Sopenharmony_ci return -ENOENT; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_cifound: 16538c2ecf20Sopenharmony_ci *queue_p = &priv->tx_queue[idx]; 16548c2ecf20Sopenharmony_ci *tx_allowed_mask_p = tx_allowed_mask; 16558c2ecf20Sopenharmony_ci return 0; 16568c2ecf20Sopenharmony_ci} 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ciint wsm_get_tx(struct cw1200_common *priv, u8 **data, 16598c2ecf20Sopenharmony_ci size_t *tx_len, int *burst) 16608c2ecf20Sopenharmony_ci{ 16618c2ecf20Sopenharmony_ci struct wsm_tx *wsm = NULL; 16628c2ecf20Sopenharmony_ci struct ieee80211_tx_info *tx_info; 16638c2ecf20Sopenharmony_ci struct cw1200_queue *queue = NULL; 16648c2ecf20Sopenharmony_ci int queue_num; 16658c2ecf20Sopenharmony_ci u32 tx_allowed_mask = 0; 16668c2ecf20Sopenharmony_ci const struct cw1200_txpriv *txpriv = NULL; 16678c2ecf20Sopenharmony_ci int count = 0; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci /* More is used only for broadcasts. */ 16708c2ecf20Sopenharmony_ci bool more = false; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci if (priv->wsm_cmd.ptr) { /* CMD request */ 16738c2ecf20Sopenharmony_ci ++count; 16748c2ecf20Sopenharmony_ci spin_lock(&priv->wsm_cmd.lock); 16758c2ecf20Sopenharmony_ci BUG_ON(!priv->wsm_cmd.ptr); 16768c2ecf20Sopenharmony_ci *data = priv->wsm_cmd.ptr; 16778c2ecf20Sopenharmony_ci *tx_len = priv->wsm_cmd.len; 16788c2ecf20Sopenharmony_ci *burst = 1; 16798c2ecf20Sopenharmony_ci spin_unlock(&priv->wsm_cmd.lock); 16808c2ecf20Sopenharmony_ci } else { 16818c2ecf20Sopenharmony_ci for (;;) { 16828c2ecf20Sopenharmony_ci int ret; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci if (atomic_add_return(0, &priv->tx_lock)) 16858c2ecf20Sopenharmony_ci break; 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci spin_lock_bh(&priv->ps_state_lock); 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci ret = wsm_get_tx_queue_and_mask(priv, &queue, 16908c2ecf20Sopenharmony_ci &tx_allowed_mask, &more); 16918c2ecf20Sopenharmony_ci queue_num = queue - priv->tx_queue; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci if (priv->buffered_multicasts && 16948c2ecf20Sopenharmony_ci (ret || !more) && 16958c2ecf20Sopenharmony_ci (priv->tx_multicast || !priv->sta_asleep_mask)) { 16968c2ecf20Sopenharmony_ci priv->buffered_multicasts = false; 16978c2ecf20Sopenharmony_ci if (priv->tx_multicast) { 16988c2ecf20Sopenharmony_ci priv->tx_multicast = false; 16998c2ecf20Sopenharmony_ci queue_work(priv->workqueue, 17008c2ecf20Sopenharmony_ci &priv->multicast_stop_work); 17018c2ecf20Sopenharmony_ci } 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci spin_unlock_bh(&priv->ps_state_lock); 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci if (ret) 17078c2ecf20Sopenharmony_ci break; 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci if (cw1200_queue_get(queue, 17108c2ecf20Sopenharmony_ci tx_allowed_mask, 17118c2ecf20Sopenharmony_ci &wsm, &tx_info, &txpriv)) 17128c2ecf20Sopenharmony_ci continue; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci if (wsm_handle_tx_data(priv, wsm, 17158c2ecf20Sopenharmony_ci tx_info, txpriv, queue)) 17168c2ecf20Sopenharmony_ci continue; /* Handled by WSM */ 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci wsm->hdr.id &= __cpu_to_le16( 17198c2ecf20Sopenharmony_ci ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX)); 17208c2ecf20Sopenharmony_ci wsm->hdr.id |= cpu_to_le16( 17218c2ecf20Sopenharmony_ci WSM_TX_LINK_ID(txpriv->raw_link_id)); 17228c2ecf20Sopenharmony_ci priv->pspoll_mask &= ~BIT(txpriv->raw_link_id); 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci *data = (u8 *)wsm; 17258c2ecf20Sopenharmony_ci *tx_len = __le16_to_cpu(wsm->hdr.len); 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci /* allow bursting if txop is set */ 17288c2ecf20Sopenharmony_ci if (priv->edca.params[queue_num].txop_limit) 17298c2ecf20Sopenharmony_ci *burst = min(*burst, 17308c2ecf20Sopenharmony_ci (int)cw1200_queue_get_num_queued(queue, tx_allowed_mask) + 1); 17318c2ecf20Sopenharmony_ci else 17328c2ecf20Sopenharmony_ci *burst = 1; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci /* store index of bursting queue */ 17358c2ecf20Sopenharmony_ci if (*burst > 1) 17368c2ecf20Sopenharmony_ci priv->tx_burst_idx = queue_num; 17378c2ecf20Sopenharmony_ci else 17388c2ecf20Sopenharmony_ci priv->tx_burst_idx = -1; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci if (more) { 17418c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = 17428c2ecf20Sopenharmony_ci (struct ieee80211_hdr *) 17438c2ecf20Sopenharmony_ci &((u8 *)wsm)[txpriv->offset]; 17448c2ecf20Sopenharmony_ci /* more buffered multicast/broadcast frames 17458c2ecf20Sopenharmony_ci * ==> set MoreData flag in IEEE 802.11 header 17468c2ecf20Sopenharmony_ci * to inform PS STAs 17478c2ecf20Sopenharmony_ci */ 17488c2ecf20Sopenharmony_ci hdr->frame_control |= 17498c2ecf20Sopenharmony_ci cpu_to_le16(IEEE80211_FCTL_MOREDATA); 17508c2ecf20Sopenharmony_ci } 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci pr_debug("[WSM] >>> 0x%.4X (%zu) %p %c\n", 17538c2ecf20Sopenharmony_ci 0x0004, *tx_len, *data, 17548c2ecf20Sopenharmony_ci wsm->more ? 'M' : ' '); 17558c2ecf20Sopenharmony_ci ++count; 17568c2ecf20Sopenharmony_ci break; 17578c2ecf20Sopenharmony_ci } 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci return count; 17618c2ecf20Sopenharmony_ci} 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_civoid wsm_txed(struct cw1200_common *priv, u8 *data) 17648c2ecf20Sopenharmony_ci{ 17658c2ecf20Sopenharmony_ci if (data == priv->wsm_cmd.ptr) { 17668c2ecf20Sopenharmony_ci spin_lock(&priv->wsm_cmd.lock); 17678c2ecf20Sopenharmony_ci priv->wsm_cmd.ptr = NULL; 17688c2ecf20Sopenharmony_ci spin_unlock(&priv->wsm_cmd.lock); 17698c2ecf20Sopenharmony_ci } 17708c2ecf20Sopenharmony_ci} 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci/* ******************************************************************** */ 17738c2ecf20Sopenharmony_ci/* WSM buffer */ 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_civoid wsm_buf_init(struct wsm_buf *buf) 17768c2ecf20Sopenharmony_ci{ 17778c2ecf20Sopenharmony_ci BUG_ON(buf->begin); 17788c2ecf20Sopenharmony_ci buf->begin = kmalloc(FWLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); 17798c2ecf20Sopenharmony_ci buf->end = buf->begin ? &buf->begin[FWLOAD_BLOCK_SIZE] : buf->begin; 17808c2ecf20Sopenharmony_ci wsm_buf_reset(buf); 17818c2ecf20Sopenharmony_ci} 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_civoid wsm_buf_deinit(struct wsm_buf *buf) 17848c2ecf20Sopenharmony_ci{ 17858c2ecf20Sopenharmony_ci kfree(buf->begin); 17868c2ecf20Sopenharmony_ci buf->begin = buf->data = buf->end = NULL; 17878c2ecf20Sopenharmony_ci} 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_cistatic void wsm_buf_reset(struct wsm_buf *buf) 17908c2ecf20Sopenharmony_ci{ 17918c2ecf20Sopenharmony_ci if (buf->begin) { 17928c2ecf20Sopenharmony_ci buf->data = &buf->begin[4]; 17938c2ecf20Sopenharmony_ci *(u32 *)buf->begin = 0; 17948c2ecf20Sopenharmony_ci } else { 17958c2ecf20Sopenharmony_ci buf->data = buf->begin; 17968c2ecf20Sopenharmony_ci } 17978c2ecf20Sopenharmony_ci} 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_cistatic int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size) 18008c2ecf20Sopenharmony_ci{ 18018c2ecf20Sopenharmony_ci size_t pos = buf->data - buf->begin; 18028c2ecf20Sopenharmony_ci size_t size = pos + extra_size; 18038c2ecf20Sopenharmony_ci u8 *tmp; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci size = round_up(size, FWLOAD_BLOCK_SIZE); 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci tmp = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA); 18088c2ecf20Sopenharmony_ci if (!tmp) { 18098c2ecf20Sopenharmony_ci wsm_buf_deinit(buf); 18108c2ecf20Sopenharmony_ci return -ENOMEM; 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci buf->begin = tmp; 18148c2ecf20Sopenharmony_ci buf->data = &buf->begin[pos]; 18158c2ecf20Sopenharmony_ci buf->end = &buf->begin[size]; 18168c2ecf20Sopenharmony_ci return 0; 18178c2ecf20Sopenharmony_ci} 1818