/* * WSM host interfaces for XRadio drivers * * Copyright (c) 2013 * Xradio Technology Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include "xradio.h" #include "wsm.h" #include "bh.h" #include "sbus.h" #include "itp.h" #ifdef ROAM_OFFLOAD #include "sta.h" #endif /*ROAM_OFFLOAD*/ #ifdef SUPPORT_FW_DBG_INF #include "fw_dbg_inf.h" #endif /*SUPPORT_FW_DBG_INF*/ /* With respect to interrupt loss, timeout in FW is 2s in some cases. */ #define WSM_CMD_TIMEOUT (3 * HZ) #define WSM_CMD_JOIN_TIMEOUT (7 * HZ) /* Join timeout is 5 sec. in FW */ #define WSM_CMD_START_TIMEOUT (7 * HZ) #define WSM_CMD_RESET_TIMEOUT (7 * HZ) /* 2 sec. timeout was observed. */ #define WSM_CMD_DEFAULT_TIMEOUT (3 * HZ) #define WSM_SKIP(buf, size) \ do { \ if (unlikely((buf)->data + size > (buf)->end)) \ goto underflow; \ (buf)->data += size; \ } while (0) #define WSM_GET(buf, ptr, size) \ do { \ if (unlikely((buf)->data + size > (buf)->end)) \ goto underflow; \ memcpy(ptr, (buf)->data, size); \ (buf)->data += size; \ } while (0) #define __WSM_GET(buf, type, cvt) \ ({ \ type val; \ if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \ goto underflow; \ val = cvt(*(type *)(buf)->data); \ (buf)->data += sizeof(type); \ val; \ }) #define WSM_GET8(buf) __WSM_GET(buf, u8, (u8)) #define WSM_GET16(buf) __WSM_GET(buf, u16, __le16_to_cpu) #define WSM_GET32(buf) __WSM_GET(buf, u32, __le32_to_cpu) #define WSM_PUT(buf, ptr, size) \ do { \ if (unlikely((buf)->data + size > (buf)->end)) \ if (unlikely(wsm_buf_reserve((buf), size))) \ goto nomem; \ memcpy((buf)->data, ptr, size); \ (buf)->data += size; \ } while (0) #define __WSM_PUT(buf, val, type, cvt) \ do { \ if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \ if (unlikely(wsm_buf_reserve((buf), sizeof(type)))) \ goto nomem; \ *(type *)(buf)->data = cvt(val); \ (buf)->data += sizeof(type); \ } while (0) #define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, (u8)) #define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __cpu_to_le16) #define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __cpu_to_le32) static void wsm_buf_reset(struct wsm_buf *buf); static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size); static int get_interface_id_scanning(struct xradio_common *hw_priv); static int wsm_cmd_send(struct xradio_common *hw_priv, struct wsm_buf *buf, void *arg, u16 cmd, long tmo, int if_id); static struct xradio_vif *wsm_get_interface_for_tx(struct xradio_common *hw_priv); static inline void wsm_cmd_lock(struct xradio_common *hw_priv) { down(&hw_priv->wsm_cmd_sema); } static inline void wsm_cmd_unlock(struct xradio_common *hw_priv) { up(&hw_priv->wsm_cmd_sema); } static inline void wsm_oper_lock(struct xradio_common *hw_priv) { down(&hw_priv->wsm_oper_lock); } static inline void wsm_oper_unlock(struct xradio_common *hw_priv) { up(&hw_priv->wsm_oper_lock); } /* ******************************************************************** */ /* WSM API implementation */ static int wsm_generic_confirm(struct xradio_common *hw_priv, void *arg, struct wsm_buf *buf) { u32 status = WSM_GET32(buf); if (status != WSM_STATUS_SUCCESS) return status; return 0; underflow: SYS_WARN(1); return -EINVAL; } #if (DGB_XRADIO_HWT) int wsm_hwt_cmd(struct xradio_common *hw_priv, void *arg, size_t arg_size) { int ret = 0; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); WSM_PUT(buf, arg, arg_size); ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0004, WSM_CMD_TIMEOUT, -1); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } #endif int wsm_fw_dbg(struct xradio_common *hw_priv, void *arg, size_t arg_size) { int ret = 0; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); WSM_PUT(buf, arg, arg_size); ret = wsm_cmd_send(hw_priv, buf, arg, 0x0025, WSM_CMD_TIMEOUT, -1); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } static int wsm_fw_dbg_confirm(struct xradio_common *hw_priv, void *arg, struct wsm_buf *buf) { #ifdef SUPPORT_FW_DBG_INF return xradio_fw_dbg_confirm((void *)(buf->data), arg); #else return 0; #endif } static int wsm_fw_dbg_indicate(struct xradio_common *hw_priv, struct wsm_buf *buf) { #ifdef SUPPORT_FW_DBG_INF return xradio_fw_dbg_indicate((void *)(buf->data)); #else return 0; #endif } #ifdef CONFIG_XRADIO_ETF int wsm_etf_cmd(struct xradio_common *hw_priv, struct wsm_hdr *arg) { int ret = 0; wsm_printk(XRADIO_DBG_MSG, "%s >>> 0x%.4X (%d)\n", __func__, arg->id, arg->len); #ifdef HW_RESTART if (unlikely(hw_priv->hw_restart)) { wsm_printk(XRADIO_DBG_ERROR, "%s hw reset!>>> 0x%.4X (%d)\n", __func__, arg->id, arg->len); return ETF_ERR_DRIVER_HANG; /*return success, don't process cmd in power off.*/ } #endif if (unlikely(hw_priv->bh_error)) { wsm_printk(XRADIO_DBG_ERROR, "%s bh error!>>> 0x%.4X (%d)\n", __func__, arg->id, arg->len); return ETF_ERR_DRIVER_HANG; } wsm_cmd_lock(hw_priv); spin_lock(&hw_priv->wsm_cmd.lock); SYS_BUG(hw_priv->wsm_cmd.ptr); hw_priv->wsm_cmd.done = 0; hw_priv->wsm_cmd.ptr = (u8 *)arg; hw_priv->wsm_cmd.len = arg->len; hw_priv->wsm_cmd.arg = NULL; hw_priv->wsm_cmd.cmd = arg->id; spin_unlock(&hw_priv->wsm_cmd.lock); xradio_bh_wakeup(hw_priv); if (unlikely(hw_priv->bh_error)) { /* Do not wait for timeout if BH is dead. Exit immediately. */ ret = ETF_ERR_DRIVER_HANG; } else { /*Set max timeout.*/ long tmo = WSM_CMD_TIMEOUT; unsigned long wsm_cmd_max_tmo = jiffies + tmo; /* Firmware prioritizes data traffic over control confirm. * Loop below checks if data was RXed and increases timeout * accordingly. */ do { /* It's safe to use unprotected access to wsm_cmd.done here */ ret = wait_event_timeout(hw_priv->wsm_cmd_wq, hw_priv->wsm_cmd.done, tmo); /* check time since last rxed and max timeout.*/ } while (!ret && time_before_eq(jiffies, hw_priv->rx_timestamp+tmo) && time_before(jiffies, wsm_cmd_max_tmo)); } spin_lock(&hw_priv->wsm_cmd.lock); if (unlikely(hw_priv->wsm_cmd.ptr != NULL)) { hw_priv->wsm_cmd.ptr = NULL; wsm_printk(XRADIO_DBG_ERROR, "%s cmd didn't send!>>> 0x%.4X (%d)\n", __func__, arg->id, arg->len); ret = ETF_ERR_DRIVER_HANG; } else { wsm_printk(XRADIO_DBG_NIY, "%s cmd send finish(%d)!>>> 0x%.4X (%d)\n", __func__, ret, arg->id, arg->len); ret = 0; } spin_unlock(&hw_priv->wsm_cmd.lock); wsm_cmd_unlock(hw_priv); return ret; } #endif #ifdef XR_RRM /*RadioResourceMeasurement*/ static int wsm_start_measure_requset(struct xradio_common *hw_priv, MEASUREMENT_PARAMETERS *arg, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); WSM_PUT(buf, arg, sizeof(*arg)); ret = wsm_cmd_send(hw_priv, buf, arg, 0x000E, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } int wsm_11k_measure_requset(struct xradio_common *hw_priv, u8 measure_type, u16 ChannelNum, u16 Duration) { int ret; u8 type, sub_type; MEASUREMENT_PARAMETERS rrm_paras; LMAC_MEAS_REQUEST *rrm_req = &rrm_paras.MeasurementRequest; /* LMAC_MEAS_CHANNEL_LOAD_PARAMS *rrm_req = &rrm_paras.MeasurementRequest; */ rrm_paras.TxPowerLevel = 0x11; rrm_paras.DurationMandatory = 0x22; rrm_paras.MeasurementRequestLength = 0x33; type = (measure_type&0xf0)>>4; sub_type = measure_type&0xf; rrm_paras.MeasurementType = type; /* if (measure_type == ChannelLoadMeasurement) { */ if (type == ChannelLoadMeasurement) { rrm_req->ChannelLoadParams.Reserved = 0; rrm_req->ChannelLoadParams.ChannelLoadCCA = sub_type; rrm_req->ChannelLoadParams.ChannelNum = ChannelNum; /*valid when channelload measure, interval bettween request&start */ rrm_req->ChannelLoadParams.RandomInterval = 0; /*unit:1TU=1024us */ rrm_req->ChannelLoadParams.MeasurementDuration = Duration; rrm_req->ChannelLoadParams.MeasurementStartTimel = 0; rrm_req->ChannelLoadParams.MeasurementStartTimeh = 0; } else if (type == NoiseHistrogramMeasurement) { rrm_req->NoisHistogramParams.Reserved = 0; rrm_req->NoisHistogramParams.IpiRpi = sub_type; rrm_req->NoisHistogramParams.ChannelNum = ChannelNum; rrm_req->NoisHistogramParams.RandomInterval = 0; rrm_req->NoisHistogramParams.MeasurementDuration = Duration; rrm_req->NoisHistogramParams.MeasurementStartTimel = 0; rrm_req->NoisHistogramParams.MeasurementStartTimeh = 0; } ret = wsm_start_measure_requset(hw_priv, &rrm_paras, 0); return ret; } #endif /*RadioResourceMeasurement */ int wsm_configuration(struct xradio_common *hw_priv, struct wsm_configuration *arg, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime); WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime); WSM_PUT32(buf, arg->dot11RtsThreshold); /* DPD block. */ WSM_PUT16(buf, arg->dpdData_size + 12); WSM_PUT16(buf, 1); /* DPD version */ WSM_PUT(buf, arg->dot11StationId, ETH_ALEN); WSM_PUT16(buf, 5); /* DPD flags */ WSM_PUT(buf, arg->dpdData, arg->dpdData_size); ret = wsm_cmd_send(hw_priv, buf, arg, 0x0009, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } static int wsm_configuration_confirm(struct xradio_common *hw_priv, struct wsm_configuration *arg, struct wsm_buf *buf) { int i; int status; status = WSM_GET32(buf); if (SYS_WARN(status != WSM_STATUS_SUCCESS)) return -EINVAL; WSM_GET(buf, arg->dot11StationId, ETH_ALEN); arg->dot11FrequencyBandsSupported = WSM_GET8(buf); WSM_SKIP(buf, 1); arg->supportedRateMask = WSM_GET32(buf); for (i = 0; i < 2; ++i) { arg->txPowerRange[i].min_power_level = WSM_GET32(buf); arg->txPowerRange[i].max_power_level = WSM_GET32(buf); arg->txPowerRange[i].stepping = WSM_GET32(buf); } return 0; underflow: SYS_WARN(1); return -EINVAL; } /* ******************************************************************** */ /*forcing upper layer to restart wifi.*/ void wsm_upper_restart(struct xradio_common *hw_priv) { wsm_printk(XRADIO_DBG_ERROR, "%s\n", __func__); #ifdef ERROR_HANG_DRIVER if (error_hang_driver) { wsm_printk(XRADIO_DBG_ERROR, "%s error_hang_driver\n", __func__); return ; /*do not restart for error debug.*/ } #endif #ifdef CONFIG_PM xradio_pm_stay_awake(&hw_priv->pm_state, 3*HZ); #endif #if defined(CONFIG_XRADIO_USE_EXTENSIONS) && 0 spin_lock(&hw_priv->vif_list_lock); xradio_for_each_vif(hw_priv, priv, i) { if (!priv) continue; /*ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); */ wsm_printk(XRADIO_DBG_WARN, "%s driver_hang_notify\n", __func__); } spin_unlock(&hw_priv->vif_list_lock); #elif defined(HW_RESTART) /* We shall not schedule restart_work to system kthread in such case: * 1. while there is a restart_work is pending * 2. while driver is not ready, for the case insmod drv failed, should not restart * 3. while restart_work is running */ if (work_pending(&hw_priv->hw_restart_work) || (!hw_priv->driver_ready) || hw_priv->hw_restart_work_running) { return; } else { wsm_cmd_lock(hw_priv); hw_priv->hw_restart = true; wsm_cmd_unlock(hw_priv); /* wait for scan complete.*/ wsm_printk(XRADIO_DBG_WARN, "Wait for scan complete!\n"); down(&hw_priv->scan.lock); down(&hw_priv->conf_lock); /* Unlock wsm_oper_lock since no confirms of wsm_oper_locks.*/ if (down_trylock(&hw_priv->wsm_oper_lock)) wsm_printk(XRADIO_DBG_WARN, "oper_lock may be locked!\n"); up(&hw_priv->wsm_oper_lock); up(&hw_priv->conf_lock); up(&hw_priv->scan.lock); msleep(200); wsm_cmd_lock(hw_priv); if (!hw_priv->exit_sync) { if (schedule_work(&hw_priv->hw_restart_work) > 0) wsm_printk(XRADIO_DBG_WARN, "%s schedule restart_work!\n", __func__); else wsm_printk(XRADIO_DBG_ERROR, "%s restart_work failed!\n", __func__); } else { wsm_printk(XRADIO_DBG_WARN, "%s Don't restart_work because driver exit!\n", __func__); } wsm_cmd_unlock(hw_priv); return; } #endif } void wsm_query_work(struct work_struct *work) { struct xradio_common *hw_priv = container_of(work, struct xradio_common, query_work); u8 ret[100] = {0}; wsm_printk(XRADIO_DBG_ALWY, "%s\n", __func__); *(u32 *)&ret[0] = hw_priv->query_packetID; wsm_read_mib(hw_priv, WSM_MIB_ID_REQ_PKT_STATUS, (void *)&ret[0], sizeof(ret), 4); if (!ret[4]) { wsm_printk(XRADIO_DBG_ALWY, "QuerypktID=0x%08x, status=0x%x, retry=%d, flags=0x%x, " \ "PktDebug=0x%x, pktqueue=0x%x, ext1=%d, ext2=%d, " \ "ext3=%d, ext4=0x%x, ext5=0x%x\n", *(u32 *)&ret[0], ret[6], ret[7], *(u32 *)&ret[8], *(u32 *)&ret[12], ret[44], ret[45], ret[46], ret[47], ret[48], ret[49]); wsm_printk(XRADIO_DBG_ALWY, "interdebug=0x%x, 0x%x, 0x%x, Soure=0x%x, 0x%x, 0x%x\n" \ "interuse=%d, external=%d, TxOutstanding=%d, " \ "QueueStatus=0x%x, BA0=0x%x, BA1=0x%x\n" \ "ScanStatus=0x%x, scanNULL=0x%x, " \ "wr_state=0x%x,0x%x,0x%x,0x%x," \ "wr_cnt=0x%04x, 0x%04x, 0x%04x, 0x%04x\n", *(u32 *)&ret[16], *(u32 *)&ret[20], *(u32 *)&ret[24], ret[28], ret[29], ret[30], ret[32], ret[33], ret[34], ret[35], *(u32 *)&ret[36], *(u32 *)&ret[40], ret[50], ret[51], ret[52], ret[53], ret[54], ret[55], *(u16 *)&ret[56], *(u16 *)&ret[58], *(u16 *)&ret[60], *(u16 *)&ret[62]); wsm_printk(XRADIO_DBG_ALWY, "FW time:request=%d, now=%d, queue=%d, complete=%d\n", *(u32 *)&ret[64], *(u32 *)&ret[68], *(u32 *)&ret[72], *(u32 *)&ret[76]); } else { ret[5] = 0; wsm_printk(XRADIO_DBG_ALWY, "No req packid=0x%08x!\n", *(u32 *)&ret[0]); } /*hardware error occurs, try to restart wifi.*/ if (ret[5] & 0x4) { wsm_printk(XRADIO_DBG_ERROR, "Hardware need to reset 0x%x.\n", ret[5]); hw_priv->bh_error = 1; #ifdef BH_USE_SEMAPHORE up(&hw_priv->bh_sem); #else wake_up(&hw_priv->bh_wq); #endif } hw_priv->query_packetID = 0; } /* ******************************************************************** */ int wsm_reset(struct xradio_common *hw_priv, const struct wsm_reset *arg, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; u16 cmd = 0x000A | WSM_TX_LINK_ID(arg->link_id); wsm_cmd_lock(hw_priv); WSM_PUT32(buf, arg->reset_statistics ? 0 : 1); ret = wsm_cmd_send(hw_priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } /* ******************************************************************** */ struct wsm_mib { u16 mibId; void *buf; size_t buf_size; }; int wsm_read_mib(struct xradio_common *hw_priv, u16 mibId, void *_buf, size_t buf_size, size_t arg_size) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; struct wsm_mib mib_buf = { .mibId = mibId, .buf = _buf, .buf_size = buf_size, }; wsm_cmd_lock(hw_priv); WSM_PUT16(buf, mibId); WSM_PUT16(buf, arg_size); WSM_PUT(buf, _buf, arg_size); ret = wsm_cmd_send(hw_priv, buf, &mib_buf, 0x0005, WSM_CMD_TIMEOUT, -1); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } static int wsm_read_mib_confirm(struct xradio_common *hw_priv, struct wsm_mib *arg, struct wsm_buf *buf) { u16 size; if (SYS_WARN(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) return -EINVAL; if (SYS_WARN(WSM_GET16(buf) != arg->mibId)) return -EINVAL; size = WSM_GET16(buf); if (size > arg->buf_size) size = arg->buf_size; WSM_GET(buf, arg->buf, size); arg->buf_size = size; return 0; underflow: SYS_WARN(1); return -EINVAL; } /* ******************************************************************** */ int wsm_write_mib(struct xradio_common *hw_priv, u16 mibId, void *_buf, size_t buf_size, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; struct wsm_mib mib_buf = { .mibId = mibId, .buf = _buf, .buf_size = buf_size, }; wsm_cmd_lock(hw_priv); WSM_PUT16(buf, mibId); WSM_PUT16(buf, buf_size); WSM_PUT(buf, _buf, buf_size); ret = wsm_cmd_send(hw_priv, buf, &mib_buf, 0x0006, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } static int wsm_write_mib_confirm(struct xradio_common *hw_priv, struct wsm_mib *arg, struct wsm_buf *buf, int interface_link_id) { int ret; int i; struct xradio_vif *priv; ret = wsm_generic_confirm(hw_priv, arg, buf); if (ret) return ret; /*wsm_set_operational_mode confirm.*/ if (arg->mibId == 0x1006) { const char *p = arg->buf; bool powersave_enabled = (p[0] & 0x0F) ? true : false; /* update vif PM status. */ priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id); if (priv) { xradio_enable_powersave(priv, powersave_enabled); spin_unlock(&priv->vif_lock); } /* HW powersave base on vif except for generic vif. */ spin_lock(&hw_priv->vif_list_lock); xradio_for_each_vif(hw_priv, priv, i) { #ifdef P2P_MULTIVIF if ((i == (XRWL_MAX_VIFS - 1)) || !priv) #else if (!priv) #endif continue; powersave_enabled &= !!priv->powersave_enabled; } hw_priv->powersave_enabled = powersave_enabled; spin_unlock(&hw_priv->vif_list_lock); } return 0; } /* ******************************************************************** */ int wsm_scan(struct xradio_common *hw_priv, const struct wsm_scan *arg, int if_id) { int i; int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; if (unlikely(arg->numOfChannels > 48)) return -EINVAL; if (unlikely(arg->numOfSSIDs > WSM_SCAN_MAX_NUM_OF_SSIDS)) return -EINVAL; if (unlikely(arg->band > 1)) return -EINVAL; wsm_oper_lock(hw_priv); wsm_cmd_lock(hw_priv); #ifdef SUPPORT_HT40 WSM_PUT8(buf, arg->band); WSM_PUT8(buf, arg->scanFlags); WSM_PUT16(buf, arg->TransmitRateEntry); WSM_PUT32(buf, arg->autoScanInterval); WSM_PUT8(buf, arg->numOfProbeRequests); WSM_PUT8(buf, arg->numOfChannels); WSM_PUT8(buf, arg->numOfSSIDs); WSM_PUT8(buf, arg->probeDelay); #else WSM_PUT8(buf, arg->band); WSM_PUT8(buf, arg->scanType); WSM_PUT8(buf, arg->scanFlags); WSM_PUT8(buf, arg->maxTransmitRate); WSM_PUT32(buf, arg->autoScanInterval); WSM_PUT8(buf, arg->numOfProbeRequests); WSM_PUT8(buf, arg->numOfChannels); WSM_PUT8(buf, arg->numOfSSIDs); WSM_PUT8(buf, arg->probeDelay); #endif for (i = 0; i < arg->numOfChannels; ++i) { WSM_PUT16(buf, arg->ch[i].number); WSM_PUT16(buf, 0); WSM_PUT32(buf, arg->ch[i].minChannelTime); WSM_PUT32(buf, arg->ch[i].maxChannelTime); WSM_PUT32(buf, 0); } for (i = 0; i < arg->numOfSSIDs; ++i) { WSM_PUT32(buf, arg->ssids[i].length); WSM_PUT(buf, &arg->ssids[i].ssid[0], sizeof(arg->ssids[i].ssid)); } ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0007, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); if (ret) wsm_oper_unlock(hw_priv); #ifdef HW_RESTART else if (hw_priv->hw_restart) wsm_oper_unlock(hw_priv); #endif return ret; nomem: wsm_cmd_unlock(hw_priv); wsm_oper_unlock(hw_priv); return -ENOMEM; } /* ******************************************************************** */ int wsm_stop_scan(struct xradio_common *hw_priv, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0008, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; } static int wsm_tx_confirm(struct xradio_common *hw_priv, struct wsm_buf *buf, int interface_link_id) { struct wsm_tx_confirm tx_confirm; #ifdef SUPPORT_HT40 tx_confirm.packetID = WSM_GET32(buf); tx_confirm.status = WSM_GET32(buf); tx_confirm.txedRateEntry = WSM_GET16(buf); tx_confirm.ackFailures = WSM_GET8(buf); tx_confirm.flags = WSM_GET8(buf); tx_confirm.RateTry[0] = WSM_GET16(buf); tx_confirm.RateTry[1] = WSM_GET16(buf); tx_confirm.RateTry[2] = WSM_GET16(buf); tx_confirm.RateTry[3] = WSM_GET16(buf); tx_confirm.RateTry[4] = WSM_GET16(buf); tx_confirm.RateTry[5] = WSM_GET16(buf); tx_confirm.mediaDelay = WSM_GET32(buf); tx_confirm.txQueueDelay = WSM_GET32(buf); #else tx_confirm.packetID = WSM_GET32(buf); tx_confirm.status = WSM_GET32(buf); tx_confirm.txedRate = WSM_GET8(buf); tx_confirm.ackFailures = WSM_GET8(buf); tx_confirm.flags = WSM_GET16(buf); tx_confirm.rate_try[0] = WSM_GET32(buf); tx_confirm.rate_try[1] = WSM_GET32(buf); tx_confirm.rate_try[2] = WSM_GET32(buf); tx_confirm.mediaDelay = WSM_GET32(buf); tx_confirm.txQueueDelay = WSM_GET32(buf); #endif wsm_printk(XRADIO_DBG_NIY, "mediaDelay=%d, QueueDelay=%d.\n", tx_confirm.mediaDelay, tx_confirm.txQueueDelay); xradio_debug_tx_delay(tx_confirm.mediaDelay, tx_confirm.txQueueDelay); if (is_hardware_xradio(hw_priv)) { /* TODO:COMBO:linkID will be stored in packetID*/ /* TODO:COMBO: Extract traffic resumption map */ tx_confirm.if_id = xradio_queue_get_if_id(tx_confirm.packetID); tx_confirm.link_id = xradio_queue_get_link_id( tx_confirm.packetID); } else { tx_confirm.link_id = interface_link_id; tx_confirm.if_id = 0; } #if 0 wsm_release_vif_tx_buffer(hw_priv, tx_confirm.if_id, 1); #endif if (hw_priv->wsm_cbc.tx_confirm) hw_priv->wsm_cbc.tx_confirm(hw_priv, &tx_confirm); return 0; underflow: SYS_WARN(1); return -EINVAL; } static int wsm_multi_tx_confirm(struct xradio_common *hw_priv, struct wsm_buf *buf, int interface_link_id) { struct xradio_vif *priv; int ret; int count; int i; count = WSM_GET32(buf); #if 0 if (SYS_WARN(count <= 0)) return -EINVAL; else if (count > 1) { ret = wsm_release_tx_buffer(hw_priv, count - 1); if (ret < 0) return ret; else if (ret > 0) xradio_bh_wakeup(hw_priv); } #endif DBG_ARRY_ADD(dbg_txconfirm, count-1); priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id); if (priv) { xradio_debug_txed_multi(priv, count); spin_unlock(&priv->vif_lock); } for (i = 0; i < count; ++i) { ret = wsm_tx_confirm(hw_priv, buf, interface_link_id); if (ret) return ret; } return ret; underflow: SYS_WARN(1); return -EINVAL; } /* ******************************************************************** */ static int wsm_join_confirm(struct xradio_common *hw_priv, struct wsm_join *arg, struct wsm_buf *buf) { if (WSM_GET32(buf) != WSM_STATUS_SUCCESS) return -EINVAL; arg->minPowerLevel = WSM_GET32(buf); arg->maxPowerLevel = WSM_GET32(buf); return 0; underflow: SYS_WARN(1); return -EINVAL; } int wsm_join(struct xradio_common *hw_priv, struct wsm_join *arg, int if_id) /*TODO: combo: make it work per vif.*/ { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_oper_lock(hw_priv); wsm_cmd_lock(hw_priv); #ifdef SUPPORT_HT40 WSM_PUT16(buf, *(u16 *)(&arg->PhyModeCfg)); WSM_PUT16(buf, arg->channelNumber); WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid)); WSM_PUT16(buf, arg->atimWindow); WSM_PUT8(buf, arg->mode); WSM_PUT8(buf, arg->probeForJoin); WSM_PUT8(buf, arg->dtimPeriod); WSM_PUT8(buf, arg->flags); WSM_PUT32(buf, arg->ssidLength); WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid)); WSM_PUT32(buf, arg->beaconInterval); WSM_PUT32(buf, arg->basicRateSet); #else WSM_PUT8(buf, arg->mode); WSM_PUT8(buf, arg->band); WSM_PUT16(buf, arg->channelNumber); WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid)); WSM_PUT16(buf, arg->atimWindow); WSM_PUT8(buf, arg->preambleType); WSM_PUT8(buf, arg->probeForJoin); WSM_PUT8(buf, arg->dtimPeriod); WSM_PUT8(buf, arg->flags); WSM_PUT32(buf, arg->ssidLength); WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid)); WSM_PUT32(buf, arg->beaconInterval); WSM_PUT32(buf, arg->basicRateSet); #endif hw_priv->tx_burst_idx = -1; ret = wsm_cmd_send(hw_priv, buf, arg, 0x000B, WSM_CMD_JOIN_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); wsm_oper_unlock(hw_priv); /*confirm, not indcation.*/ return ret; nomem: wsm_cmd_unlock(hw_priv); wsm_oper_unlock(hw_priv); return -ENOMEM; } /* ******************************************************************** */ int wsm_set_bss_params(struct xradio_common *hw_priv, const struct wsm_set_bss_params *arg, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); WSM_PUT8(buf, 0); WSM_PUT8(buf, arg->beaconLostCount); WSM_PUT16(buf, arg->aid); WSM_PUT32(buf, arg->operationalRateSet); ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0011, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } /* ******************************************************************** */ int wsm_add_key(struct xradio_common *hw_priv, const struct wsm_add_key *arg, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); WSM_PUT(buf, arg, sizeof(*arg)); ret = wsm_cmd_send(hw_priv, buf, NULL, 0x000C, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } /* ******************************************************************** */ int wsm_remove_key(struct xradio_common *hw_priv, const struct wsm_remove_key *arg, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); WSM_PUT8(buf, arg->entryIndex); WSM_PUT8(buf, 0); WSM_PUT16(buf, 0); ret = wsm_cmd_send(hw_priv, buf, NULL, 0x000D, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } /* ******************************************************************** */ int wsm_set_tx_queue_params(struct xradio_common *hw_priv, const struct wsm_set_tx_queue_params *arg, u8 id, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1}; wsm_cmd_lock(hw_priv); WSM_PUT8(buf, queue_id_to_wmm_aci[id]); WSM_PUT8(buf, 0); WSM_PUT8(buf, arg->ackPolicy); WSM_PUT8(buf, 0); WSM_PUT32(buf, arg->maxTransmitLifetime); WSM_PUT16(buf, arg->allowedMediumTime); WSM_PUT16(buf, 0); ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } /* ******************************************************************** */ int wsm_set_edca_params(struct xradio_common *hw_priv, const struct wsm_edca_params *arg, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); /* Implemented according to specification. */ WSM_PUT16(buf, arg->params[3].cwMin); WSM_PUT16(buf, arg->params[2].cwMin); WSM_PUT16(buf, arg->params[1].cwMin); WSM_PUT16(buf, arg->params[0].cwMin); WSM_PUT16(buf, arg->params[3].cwMax); WSM_PUT16(buf, arg->params[2].cwMax); WSM_PUT16(buf, arg->params[1].cwMax); WSM_PUT16(buf, arg->params[0].cwMax); WSM_PUT8(buf, arg->params[3].aifns); WSM_PUT8(buf, arg->params[2].aifns); WSM_PUT8(buf, arg->params[1].aifns); WSM_PUT8(buf, arg->params[0].aifns); WSM_PUT16(buf, arg->params[3].txOpLimit); WSM_PUT16(buf, arg->params[2].txOpLimit); WSM_PUT16(buf, arg->params[1].txOpLimit); WSM_PUT16(buf, arg->params[0].txOpLimit); WSM_PUT32(buf, arg->params[3].maxReceiveLifetime); WSM_PUT32(buf, arg->params[2].maxReceiveLifetime); WSM_PUT32(buf, arg->params[1].maxReceiveLifetime); WSM_PUT32(buf, arg->params[0].maxReceiveLifetime); ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0013, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } /* ******************************************************************** */ int wsm_switch_channel(struct xradio_common *hw_priv, const struct wsm_switch_channel *arg, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_lock_tx(hw_priv); wsm_cmd_lock(hw_priv); WSM_PUT8(buf, arg->channelMode); WSM_PUT8(buf, arg->channelSwitchCount); WSM_PUT16(buf, arg->newChannelNumber); hw_priv->channel_switch_in_progress = 1; ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0016, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); if (ret) { wsm_unlock_tx(hw_priv); hw_priv->channel_switch_in_progress = 0; } return ret; nomem: wsm_cmd_unlock(hw_priv); wsm_unlock_tx(hw_priv); return -ENOMEM; } /* ******************************************************************** */ int wsm_set_pm(struct xradio_common *hw_priv, const struct wsm_set_pm *arg, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_oper_lock(hw_priv); wsm_cmd_lock(hw_priv); WSM_PUT8(buf, arg->pmMode); WSM_PUT8(buf, arg->fastPsmIdlePeriod); WSM_PUT8(buf, arg->apPsmChangePeriod); WSM_PUT8(buf, arg->minAutoPsPollPeriod); ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0010, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); if (ret) wsm_oper_unlock(hw_priv); #ifdef HW_RESTART else if (hw_priv->hw_restart) wsm_oper_unlock(hw_priv); #endif return ret; nomem: wsm_cmd_unlock(hw_priv); wsm_oper_unlock(hw_priv); return -ENOMEM; } /* ******************************************************************** */ int wsm_start(struct xradio_common *hw_priv, const struct wsm_start *arg, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); #ifdef SUPPORT_HT40 WSM_PUT16(buf, *(u16 *)(&arg->PhyModeCfg)); WSM_PUT16(buf, arg->channelNumber); WSM_PUT32(buf, arg->CTWindow); WSM_PUT32(buf, arg->beaconInterval); WSM_PUT8(buf, arg->mode); WSM_PUT8(buf, arg->DTIMPeriod); WSM_PUT8(buf, arg->probeDelay); WSM_PUT8(buf, arg->ssidLength); WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); WSM_PUT32(buf, arg->basicRateSet); #else WSM_PUT8(buf, arg->mode); WSM_PUT8(buf, arg->band); WSM_PUT16(buf, arg->channelNumber); WSM_PUT32(buf, arg->CTWindow); WSM_PUT32(buf, arg->beaconInterval); WSM_PUT8(buf, arg->DTIMPeriod); WSM_PUT8(buf, arg->preambleType); WSM_PUT8(buf, arg->probeDelay); WSM_PUT8(buf, arg->ssidLength); WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); WSM_PUT32(buf, arg->basicRateSet); #endif hw_priv->tx_burst_idx = -1; ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0017, WSM_CMD_START_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } #if 0 /* This API is no longer present in WSC */ /* ******************************************************************** */ int wsm_beacon_transmit(struct xradio_common *hw_priv, const struct wsm_beacon_transmit *arg, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); WSM_PUT32(buf, arg->enableBeaconing ? 1 : 0); ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0018, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } #endif /* ******************************************************************** */ int wsm_start_find(struct xradio_common *hw_priv, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; } /* ******************************************************************** */ int wsm_stop_find(struct xradio_common *hw_priv, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); ret = wsm_cmd_send(hw_priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; } /* ******************************************************************** */ int wsm_map_link(struct xradio_common *hw_priv, const struct wsm_map_link *arg, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; u16 cmd = 0x001C; wsm_cmd_lock(hw_priv); WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr)); if (is_hardware_xradio(hw_priv)) { WSM_PUT8(buf, arg->unmap); WSM_PUT8(buf, arg->link_id); } else { cmd |= WSM_TX_LINK_ID(arg->link_id); WSM_PUT16(buf, 0); } ret = wsm_cmd_send(hw_priv, buf, NULL, cmd, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } /* ******************************************************************** */ int wsm_update_ie(struct xradio_common *hw_priv, const struct wsm_update_ie *arg, int if_id) { int ret; struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; wsm_cmd_lock(hw_priv); WSM_PUT16(buf, arg->what); WSM_PUT16(buf, arg->count); WSM_PUT(buf, arg->ies, arg->length); ret = wsm_cmd_send(hw_priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT, if_id); wsm_cmd_unlock(hw_priv); return ret; nomem: wsm_cmd_unlock(hw_priv); return -ENOMEM; } /* ******************************************************************** */ #ifdef MCAST_FWDING /* 3.66 */ static int wsm_give_buffer_confirm(struct xradio_common *hw_priv, struct wsm_buf *buf) { wsm_printk(XRADIO_DBG_MSG, "HW Buf count %d\n", hw_priv->hw_bufs_used); if (!hw_priv->hw_bufs_used) wake_up(&hw_priv->bh_evt_wq); return 0; } /* 3.65 */ int wsm_init_release_buffer_request(struct xradio_common *hw_priv) { struct wsm_buf *buf = &hw_priv->wsm_release_buf; u16 cmd = 0x0022; /* Buffer Request */ size_t buf_len = sizeof(struct wsm_hdr) + 8; wsm_buf_init(buf, buf_len); WSM_PUT8(buf, 0); WSM_PUT8(buf, 0); WSM_PUT16(buf, 0); buf_len = buf->data - buf->begin; /* Fill HI message header */ ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); return 0; nomem: return -ENOMEM; } void wsm_deinit_release_buffer(struct xradio_common *hw_priv) { wsm_buf_deinit(&hw_priv->wsm_release_buf); } /* 3.68 */ static int wsm_request_buffer_confirm(struct xradio_vif *priv, u8 *arg, struct wsm_buf *buf) { u8 count; u32 sta_asleep_mask = 0; int i; u32 mask = 0; u32 change_mask = 0; struct xradio_common *hw_priv = priv->hw_priv; /* There is no status field in this message */ sta_asleep_mask = WSM_GET32(buf); count = WSM_GET8(buf); count -= 1; /* Current workaround for FW issue */ spin_lock_bh(&priv->ps_state_lock); change_mask = (priv->sta_asleep_mask ^ sta_asleep_mask); wsm_printk(XRADIO_DBG_MSG, "CM %x, HM %x, FWM %x\n", change_mask, priv->sta_asleep_mask, sta_asleep_mask); spin_unlock_bh(&priv->ps_state_lock); if (change_mask) { struct ieee80211_sta *sta; int ret = 0; for (i = 0; i < MAX_STA_IN_AP_MODE ; ++i) { if (XRADIO_LINK_HARD != priv->link_id_db[i].status) continue; mask = BIT(i + 1); /* If FW state and host state for * this link are different then notify OMAC */ if (change_mask & mask) { wsm_printk(XRADIO_DBG_MSG, "PS State Changed %d for sta %pM\n", (sta_asleep_mask & mask) ? 1 : 0, priv->link_id_db[i].mac); rcu_read_lock(); sta = mac80211_find_sta(priv->vif, priv->link_id_db[i].mac); if (!sta) { wsm_printk(XRADIO_DBG_MSG, "WRBC - could not find sta %pM\n", priv->link_id_db[i].mac); } else { ret = mac80211_sta_ps_transition_ni(sta, (sta_asleep_mask & mask) ? true : false); wsm_printk(XRADIO_DBG_MSG, "PS State NOTIFIED %d\n", ret); SYS_WARN(ret); } rcu_read_unlock(); } } /* Replace STA mask with one reported by FW */ spin_lock_bh(&priv->ps_state_lock); priv->sta_asleep_mask = sta_asleep_mask; spin_unlock_bh(&priv->ps_state_lock); } wsm_printk(XRADIO_DBG_MSG, "WRBC - HW Buf count %d SleepMask %d\n", hw_priv->hw_bufs_used, sta_asleep_mask); hw_priv->buf_released = 0; SYS_WARN(count != (hw_priv->wsm_caps.numInpChBufs - 1)); return 0; underflow: SYS_WARN(1); return -EINVAL; } /* 3.67 */ int wsm_request_buffer_request(struct xradio_vif *priv, u8 *arg) { int ret; struct wsm_buf *buf = &priv->hw_priv->wsm_cmd_buf; wsm_cmd_lock(priv->hw_priv); WSM_PUT8(buf, (*arg)); WSM_PUT8(buf, 0); WSM_PUT16(buf, 0); ret = wsm_cmd_send(priv->hw_priv, buf, arg, 0x0023, WSM_CMD_JOIN_TIMEOUT, priv->if_id); wsm_cmd_unlock(priv->hw_priv); return ret; nomem: wsm_cmd_unlock(priv->hw_priv); return -ENOMEM; } #endif int wsm_set_keepalive_filter(struct xradio_vif *priv, bool enable) { struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); priv->rx_filter.keepalive = enable; return wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id); } int wsm_set_probe_responder(struct xradio_vif *priv, bool enable) { struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); priv->rx_filter.probeResponder = enable; return wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id); } /* ******************************************************************** */ /* WSM indication events implementation */ static int wsm_startup_indication(struct xradio_common *hw_priv, struct wsm_buf *buf) { u16 status; #ifdef CONFIG_XRADIO_DEBUG static const char * const fw_types[] = { "ETF", "WFM", "WSM", "HI test", "Platform test" }; #endif hw_priv->wsm_caps.numInpChBufs = WSM_GET16(buf); hw_priv->wsm_caps.sizeInpChBuf = WSM_GET16(buf); hw_priv->wsm_caps.hardwareId = WSM_GET16(buf); hw_priv->wsm_caps.hardwareSubId = WSM_GET16(buf); status = WSM_GET16(buf); hw_priv->wsm_caps.firmwareCap = WSM_GET16(buf); hw_priv->wsm_caps.firmwareType = WSM_GET16(buf); hw_priv->wsm_caps.firmwareApiVer = WSM_GET16(buf); hw_priv->wsm_caps.firmwareBuildNumber = WSM_GET16(buf); hw_priv->wsm_caps.firmwareVersion = WSM_GET16(buf); WSM_GET(buf, &hw_priv->wsm_caps.fw_label[0], WSM_FW_LABEL); /* Do not trust FW too much. */ hw_priv->wsm_caps.fw_label[WSM_FW_LABEL+1] = 0; hw_priv->wsm_caps.firmwareConfig[0] = WSM_GET32(buf); hw_priv->wsm_caps.firmwareConfig[1] = WSM_GET32(buf); hw_priv->wsm_caps.firmwareConfig[2] = WSM_GET32(buf); if (SYS_WARN(status)) return -EINVAL; if (SYS_WARN(hw_priv->wsm_caps.firmwareType > 4)) return -EINVAL; wsm_printk(XRADIO_DBG_NIY, "%s\n" " Input buffers: %d x %d bytes\n" " Hardware: %d.%d\n" " %s firmware ver: %d, build: %d," " api: %d, cap: 0x%.4X\n", __func__, hw_priv->wsm_caps.numInpChBufs, hw_priv->wsm_caps.sizeInpChBuf, hw_priv->wsm_caps.hardwareId, hw_priv->wsm_caps.hardwareSubId, fw_types[hw_priv->wsm_caps.firmwareType], hw_priv->wsm_caps.firmwareVersion, hw_priv->wsm_caps.firmwareBuildNumber, hw_priv->wsm_caps.firmwareApiVer, hw_priv->wsm_caps.firmwareCap); wsm_printk(XRADIO_DBG_ALWY, "Firmware Label:%s\n", &hw_priv->wsm_caps.fw_label[0]); hw_priv->wsm_caps.firmwareReady = 1; wake_up(&hw_priv->wsm_startup_done); return 0; underflow: SYS_WARN(1); return -EINVAL; } void wsm_send_deauth_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv) { struct sk_buff *skb = NULL; struct ieee80211_mgmt *deauth = NULL; if (priv->join_status == XRADIO_JOIN_STATUS_AP) { int i = 0; wsm_printk(XRADIO_DBG_WARN, "AP mode, send_deauth_to_self\n"); for (i = 0; i < MAX_STA_IN_AP_MODE; i++) { if (priv->link_id_db[i].status == XRADIO_LINK_HARD) { skb = xr_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); if (!skb) return; skb_reserve(skb, 64); deauth = (struct ieee80211_mgmt *) \ skb_put(skb, sizeof(struct ieee80211_mgmt)); if (!deauth) { SYS_WARN(1); return; } deauth->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); deauth->duration = 0; memcpy(deauth->da, priv->vif->addr, ETH_ALEN); memcpy(deauth->sa, priv->link_id_db[i].mac, ETH_ALEN); memcpy(deauth->bssid, priv->vif->addr, ETH_ALEN); deauth->seq_ctrl = 0; deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING; mac80211_rx_irqsafe(priv->hw, skb); } } } else if (priv->join_status == XRADIO_JOIN_STATUS_STA) { wsm_printk(XRADIO_DBG_WARN, "STA mode, send_deauth_to_self\n"); skb = xr_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); if (!skb) return; skb_reserve(skb, 64); deauth = (struct ieee80211_mgmt *) \ skb_put(skb, sizeof(struct ieee80211_mgmt)); if (!deauth) { SYS_WARN(1); return; } deauth->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); deauth->duration = 0; memcpy(deauth->da, priv->vif->addr, ETH_ALEN); memcpy(deauth->sa, priv->join_bssid, ETH_ALEN); memcpy(deauth->bssid, priv->join_bssid, ETH_ALEN); deauth->seq_ctrl = 0; deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING; mac80211_rx_irqsafe(priv->hw, skb); } } void wsm_send_disassoc_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv) { struct sk_buff *skb = NULL; struct ieee80211_mgmt *disassoc = NULL; if (priv->join_status == XRADIO_JOIN_STATUS_AP) { int i = 0; wsm_printk(XRADIO_DBG_WARN, "AP mode, wsm_send_disassoc_to_self\n"); for (i = 0; i < MAX_STA_IN_AP_MODE; i++) { if (priv->link_id_db[i].status == XRADIO_LINK_HARD) { skb = xr_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); if (!skb) return; skb_reserve(skb, 64); disassoc = (struct ieee80211_mgmt *) \ skb_put(skb, sizeof(struct ieee80211_mgmt)); if (!disassoc) { SYS_WARN(1); return; } disassoc->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC); disassoc->duration = 0; memcpy(disassoc->da, priv->vif->addr, ETH_ALEN); memcpy(disassoc->sa, priv->link_id_db[i].mac, ETH_ALEN); memcpy(disassoc->bssid, priv->vif->addr, ETH_ALEN); disassoc->seq_ctrl = 0; disassoc->u.disassoc.reason_code = WLAN_REASON_DISASSOC_STA_HAS_LEFT; mac80211_rx_irqsafe(priv->hw, skb); } } } else if (priv->join_status == XRADIO_JOIN_STATUS_STA) { wsm_printk(XRADIO_DBG_WARN, "STA mode, wsm_send_disassoc_to_self\n"); skb = xr_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); if (!skb) return; skb_reserve(skb, 64); disassoc = (struct ieee80211_mgmt *) \ skb_put(skb, sizeof(struct ieee80211_mgmt)); if (!disassoc) { SYS_WARN(1); return; } disassoc->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC); disassoc->duration = 0; memcpy(disassoc->da, priv->vif->addr, ETH_ALEN); memcpy(disassoc->sa, priv->join_bssid, ETH_ALEN); memcpy(disassoc->bssid, priv->join_bssid, ETH_ALEN); disassoc->seq_ctrl = 0; disassoc->u.disassoc.reason_code = WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY; mac80211_rx_irqsafe(priv->hw, skb); } } static int wsm_receive_indication(struct xradio_common *hw_priv, int interface_link_id, struct wsm_buf *buf, struct sk_buff **skb_p) { struct xradio_vif *priv; int if_id = interface_link_id; hw_priv->rx_timestamp = jiffies; if (hw_priv->wsm_cbc.rx) { struct wsm_rx rx; struct ieee80211_hdr *hdr; size_t hdr_len; #ifdef SUPPORT_HT40 rx.status = WSM_GET32(buf); rx.channelNumber = WSM_GET16(buf); rx.rxedRateEntry = WSM_GET16(buf); rx.Reserved[0] = WSM_GET8(buf); rx.Reserved[1] = WSM_GET8(buf); rx.Reserved[2] = WSM_GET8(buf); rx.rcpiRssi = WSM_GET8(buf); rx.flags = WSM_GET32(buf); #else rx.status = WSM_GET32(buf); rx.channelNumber = WSM_GET16(buf); rx.rxedRate = WSM_GET8(buf); rx.rcpiRssi = WSM_GET8(buf); rx.flags = WSM_GET32(buf); #endif /* TODO:COMBO: Frames received from scanning are received * with interface ID == 2 */ if (is_hardware_xradio(hw_priv)) { if (if_id == XRWL_GENERIC_IF_ID) { /* Frames received in response to SCAN * Request */ if_id = get_interface_id_scanning(hw_priv); if (if_id == -1) { if_id = hw_priv->roc_if_id; } #ifdef ROAM_OFFLOAD if (hw_priv->auto_scanning) { if_id = hw_priv->scan.if_id; } #endif/*ROAM_OFFLOAD*/ } /* linkid (peer sta id is encoded in bit 25-28 of flags field */ #ifdef SUPPORT_HT40 rx.link_id = WSM_RX_LINK_ID_GET(rx.flags); #else rx.link_id = ((rx.flags & (0xf << 25)) >> 25); #endif rx.if_id = if_id; } else { rx.link_id = if_id; rx.if_id = 0; } #ifdef MONITOR_MODE if (hw_priv->monitor_if_id != -1) priv = xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->monitor_if_id); else #endif priv = xrwl_hwpriv_to_vifpriv(hw_priv, rx.if_id); if (!priv) { wsm_printk(XRADIO_DBG_WARN, "%s: NULL priv(if=%d) drop frame, link_id=%d," "scan_id=%d, roc_id=%d, scan_req=%p, direct_probe=%d\n", __func__, if_id, interface_link_id, hw_priv->scan.if_id, hw_priv->roc_if_id, hw_priv->scan.req, hw_priv->scan.direct_probe); return 0; } /*remove wsm hdr of skb*/ hdr_len = buf->data - buf->begin; skb_pull(*skb_p, hdr_len); /* FW Workaround: Drop probe resp or beacon when RSSI is 0 */ hdr = (struct ieee80211_hdr *) (*skb_p)->data; if (!rx.rcpiRssi && (ieee80211_is_probe_resp(hdr->frame_control) || ieee80211_is_beacon(hdr->frame_control))) { spin_unlock(&priv->vif_lock); return 0; } /* If no RSSI subscription has been made, * convert RCPI to RSSI here */ if (!priv->cqm_use_rssi) rx.rcpiRssi = rx.rcpiRssi / 2 - 110; #ifdef USE_RSSI_OFFSET rx.rcpiRssi = (s8)rx.rcpiRssi - WSM_RSSI_OFFSET; /* rssi offset.*/ #endif if ((s8)rx.rcpiRssi > 0) rx.rcpiRssi = 0; if (!rx.status && unlikely(ieee80211_is_deauth(hdr->frame_control))) { if (priv->join_status == XRADIO_JOIN_STATUS_STA) { /* Shedule unjoin work */ wsm_printk(XRADIO_DBG_WARN, \ "Issue unjoin command (RX).\n"); wsm_lock_tx_async(hw_priv); if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0) wsm_unlock_tx(hw_priv); } } hw_priv->wsm_cbc.rx(priv, &rx, skb_p); if (*skb_p) skb_push(*skb_p, hdr_len); spin_unlock(&priv->vif_lock); } return 0; underflow: return -EINVAL; } static int wsm_multi_receive_indication(struct xradio_common *hw_priv, int multirxlen, struct wsm_buf *buf, struct sk_buff **skb_p) { size_t wsm_len = 0; int wsm_id; size_t len; struct wsm_hdr *wsm; u8 *data = buf->begin; size_t packet_len = 0; int interface_link_id = 0; int cloned = 0; wsm_printk(XRADIO_DBG_NIY, "%s Total=%d\n", __func__, multirxlen); for (len = 0; len < multirxlen; len += packet_len) { wsm = (struct wsm_hdr *)(data + len); wsm_len = __le32_to_cpu(wsm->len); packet_len = ROUND4(wsm_len); wsm_id = __le32_to_cpu(wsm->id) & 0xFFF; interface_link_id = (wsm_id >> 6) & 0x0F; wsm_printk(XRADIO_DBG_MSG, "if%d-multi-rx 0x%.4X (%zu)\n", interface_link_id, wsm_id, wsm_len); wsm_id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); if (wsm_id == 0x0804) { struct wsm_buf wsm_buffer; struct sk_buff *skb = NULL; wsm_buffer.begin = (u8 *)&wsm[0]; wsm_buffer.data = (u8 *)&wsm[1]; wsm_buffer.end = &wsm_buffer.begin[__le32_to_cpu(wsm->len)]; if ((len + packet_len) < multirxlen) { skb = skb_clone((*skb_p), GFP_ATOMIC); if (WARN_ON(!skb)) break; skb_trim(skb, 0); skb_put(skb, len + wsm_len); /* set data end.*/ skb_pull(skb, len); /* set data begin.*/ skb_trim(skb, wsm_len); /* set data length.*/ wsm_receive_indication(hw_priv, interface_link_id, &wsm_buffer, &skb); if (skb) { dev_kfree_skb(skb); skb = NULL; } else { ++cloned; } } else { /*last packet*/ skb_trim(*skb_p, 0); skb_put(*skb_p, len + wsm_len); /* set data end.*/ skb_pull(*skb_p, len); /* set data begin.*/ skb_trim(*skb_p, wsm_len); /* set data length.*/ wsm_receive_indication(hw_priv, interface_link_id, &wsm_buffer, skb_p); if (*skb_p) { /* the skb cannot be reclaim by xradio_put_skb or * xradio_put_resv_skb if it is cloned. */ if (cloned) { dev_kfree_skb(*skb_p); *skb_p = NULL; } else { /* reset the data begin.*/ skb_push(*skb_p, len); } } } } } return 0; } static int wsm_event_indication(struct xradio_common *hw_priv, struct wsm_buf *buf, int interface_link_id) { int first; struct xradio_wsm_event *event = NULL; struct xradio_vif *priv; if (!is_hardware_xradio(hw_priv)) interface_link_id = 0; priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id); if (unlikely(!priv)) { wsm_printk(XRADIO_DBG_WARN, "Event: %d(%d) for removed " "interface, ignoring\n", __le32_to_cpu(WSM_GET32(buf)), __le32_to_cpu(WSM_GET32(buf))); return 0; } if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { /* STA is stopped. */ return 0; } spin_unlock(&priv->vif_lock); event = xr_kzalloc(sizeof(struct xradio_wsm_event), false); if (event == NULL) { wsm_printk(XRADIO_DBG_ERROR, "%s:xr_kzalloc failed!", __func__); return -EINVAL; } event->evt.eventId = __le32_to_cpu(WSM_GET32(buf)); event->evt.eventData = __le32_to_cpu(WSM_GET32(buf)); event->if_id = interface_link_id; wsm_printk(XRADIO_DBG_MSG, "Event: %d(%d)\n", event->evt.eventId, event->evt.eventData); if (event->evt.eventId == WSM_EVENT_ERROR) { int len = (int)(buf->end - buf->data); int i = 0; wsm_printk(XRADIO_DBG_ALWY, "FW TXERR: 0x%08x(%d)\n", event->evt.eventData, len); for (i = 0; i < (len>>2); i++) wsm_printk(XRADIO_DBG_ALWY, "0x%08x\n", __le32_to_cpu(WSM_GET32(buf))); for (len &= 0x3; len > 0; len--) wsm_printk(XRADIO_DBG_ALWY, "0x%x\n", __le32_to_cpu(WSM_GET8(buf))); wsm_printk(XRADIO_DBG_ALWY, "FW TXERR END\n"); } spin_lock(&hw_priv->event_queue_lock); first = list_empty(&hw_priv->event_queue); list_add_tail(&event->link, &hw_priv->event_queue); spin_unlock(&hw_priv->event_queue_lock); if (first) queue_work(hw_priv->workqueue, &hw_priv->event_handler); return 0; underflow: kfree(event); return -EINVAL; } #define PRINT_11K_MEASRURE 1 static int wsm_measure_cmpl_indication(struct xradio_common *hw_priv, struct wsm_buf *buf) { MEASUREMENT_COMPLETE measure_cmpl; u8 cca_chanload; u32 buf_len = 0; u32 *data; LMAC_MEAS_CHANNEL_LOAD_RESULTS *chanload_res; LMAC_MEAS_NOISE_HISTOGRAM_RESULTS *noise_res; WSM_GET(buf, &measure_cmpl, 12); switch (measure_cmpl.MeasurementType) { case ChannelLoadMeasurement: buf_len = sizeof(LMAC_MEAS_CHANNEL_LOAD_RESULTS); break; case NoiseHistrogramMeasurement: buf_len = sizeof(LMAC_MEAS_NOISE_HISTOGRAM_RESULTS); break; case BeaconReport: buf_len = sizeof(LMAC_MEAS_BEACON_RESULTS); break; case STAstatisticsReport: buf_len = sizeof(LMAC_MEAS_STA_STATS_RESULTS); break; case LinkMeasurement: buf_len = sizeof(LMAC_MEAS_LINK_MEASUREMENT_RESULTS); break; } wsm_printk(XRADIO_DBG_ERROR, "[11K]buf_len = %d\n", buf_len); WSM_GET(buf, &measure_cmpl.MeasurementReport, buf_len); data = (u32 *)(&measure_cmpl); /* wsm_printk(XRADIO_DBG_ERROR, "[***11K***]data[0]=%08x\n", data[0]); wsm_printk(XRADIO_DBG_ERROR, "[***11K***]data[1]=%08x\n", data[1]); wsm_printk(XRADIO_DBG_ERROR, "[***11K***]data[2]=%08x\n", data[2]); wsm_printk(XRADIO_DBG_ERROR, "[***11K***]data[3]=%08x\n", data[3]); wsm_printk(XRADIO_DBG_ERROR, "[***11K***]data[4]=%08x\n", data[4]); wsm_printk(XRADIO_DBG_ERROR, "[***11K***]data[5]=%08x\n", data[5]); wsm_printk(XRADIO_DBG_ERROR, "[***11K***]data[6]=%08x\n", data[6]); */ wsm_printk(XRADIO_DBG_ERROR, "[***11K***]MeasurementType=%0d\n", measure_cmpl.MeasurementType); if (measure_cmpl.Status == WSM_STATUS_SUCCESS) { switch (measure_cmpl.MeasurementType) { case ChannelLoadMeasurement: chanload_res = &measure_cmpl.MeasurementReport.ChannelLoadResults; cca_chanload = (chanload_res->ChannelLoadCCA == MEAS_CCA) ? chanload_res->CCAbusyFraction : chanload_res->ChannelLoad; #ifdef PRINT_11K_MEASRURE wsm_printk(XRADIO_DBG_ERROR, "[11K] ChannelLoadMeasurement Result:\n"\ "ChannelLoadCCA = %d\n"\ "ChannelNum = %d\n"\ "Duration = %d\n"\ "Fraction = %d\n", \ chanload_res->ChannelLoadCCA,\ chanload_res->ChannelNum,\ chanload_res->MeasurementDuration,\ cca_chanload); #endif break; case NoiseHistrogramMeasurement: noise_res = &measure_cmpl.MeasurementReport.NoiseHistogramResults; /* IpiRpi = (noise_res->IpiRpi == MEAS_RPI) ? chanload_res->CCAbusyFraction : chanload_res->ChannelLoad; */ #ifdef PRINT_11K_MEASRURE wsm_printk(XRADIO_DBG_ERROR, "[11K] NoiseHistogramResults:\n"\ "IpiRpi = %d\n"\ "ChannelNum = %d\n"\ "PI_0__Density = %d\n"\ "PI_1__Density = %d\n"\ "PI_2__Density = %d\n"\ "PI_3__Density = %d\n"\ "PI_4__Density = %d\n"\ "PI_5__Density = %d\n"\ "PI_6__Density = %d\n"\ "PI_7__Density = %d\n"\ "PI_8__Density = %d\n"\ "PI_9__Density = %d\n"\ "PI_10_Density = %d\n", \ noise_res->IpiRpi,\ noise_res->ChannelNum,\ noise_res->PI_0_Density,\ noise_res->PI_1_Density,\ noise_res->PI_2_Density,\ noise_res->PI_3_Density,\ noise_res->PI_4_Density,\ noise_res->PI_5_Density,\ noise_res->PI_6_Density,\ noise_res->PI_7_Density,\ noise_res->PI_8_Density,\ noise_res->PI_9_Density,\ noise_res->PI_10_Density \ ); #endif break; case BeaconReport: break; case STAstatisticsReport: break; case LinkMeasurement: break; } } else { wsm_printk(XRADIO_DBG_ERROR, "11K Measure(type=%d) Fail\n", measure_cmpl.MeasurementType); } return 0; underflow: return -EINVAL; } /* TODO:COMBO:Make this perVIFF once mac80211 support is available */ static int wsm_channel_switch_indication(struct xradio_common *hw_priv, struct wsm_buf *buf) { wsm_unlock_tx(hw_priv); /* Re-enable datapath */ SYS_WARN(WSM_GET32(buf)); hw_priv->channel_switch_in_progress = 0; wake_up(&hw_priv->channel_switch_done); if (hw_priv->wsm_cbc.channel_switch) hw_priv->wsm_cbc.channel_switch(hw_priv); return 0; underflow: return -EINVAL; } static int wsm_set_pm_indication(struct xradio_common *hw_priv, struct wsm_buf *buf) { wsm_oper_unlock(hw_priv); return 0; } static int wsm_scan_complete_indication(struct xradio_common *hw_priv, struct wsm_buf *buf) { #ifdef ROAM_OFFLOAD if (hw_priv->auto_scanning == 0) wsm_oper_unlock(hw_priv); #else wsm_oper_unlock(hw_priv); #endif /*ROAM_OFFLOAD*/ if (hw_priv->wsm_cbc.scan_complete) { struct wsm_scan_complete arg; arg.status = WSM_GET32(buf); arg.psm = WSM_GET8(buf); arg.numChannels = WSM_GET8(buf); hw_priv->wsm_cbc.scan_complete(hw_priv, &arg); } return 0; underflow: return -EINVAL; } static int wsm_find_complete_indication(struct xradio_common *hw_priv, struct wsm_buf *buf) { /* TODO: Implement me. */ /*STUB();*/ return 0; } static int wsm_suspend_resume_indication(struct xradio_common *hw_priv, int interface_link_id, struct wsm_buf *buf) { if (hw_priv->wsm_cbc.suspend_resume) { u32 flags; struct wsm_suspend_resume arg; struct xradio_vif *priv; if (is_hardware_xradio(hw_priv)) { int i; arg.if_id = interface_link_id; /* TODO:COMBO: Extract bitmap from suspend-resume * TX indication */ xradio_for_each_vif(hw_priv, priv, i) { if (!priv) continue; if (priv->join_status == XRADIO_JOIN_STATUS_AP) { arg.if_id = priv->if_id; break; } arg.link_id = 0; } } else { arg.if_id = 0; arg.link_id = interface_link_id; } flags = WSM_GET32(buf); arg.stop = !(flags & 1); arg.multicast = !!(flags & 8); arg.queue = (flags >> 1) & 3; priv = xrwl_hwpriv_to_vifpriv(hw_priv, arg.if_id); if (unlikely(!priv)) { wsm_printk(XRADIO_DBG_MSG, "suspend-resume indication" " for removed interface!\n"); return 0; } hw_priv->wsm_cbc.suspend_resume(priv, &arg); spin_unlock(&priv->vif_lock); } return 0; underflow: return -EINVAL; } /* ******************************************************************** */ /* WSM TX */ int wsm_cmd_send(struct xradio_common *hw_priv, struct wsm_buf *buf, void *arg, u16 cmd, long tmo, int if_id) { size_t buf_len = buf->data - buf->begin; int ret; u16 mib_id = 0; unsigned long wsm_cmd_max_tmo = 0x0; if (cmd == 0x0006 || cmd == 0x0005) {/* Write/Read MIB */ mib_id = __le16_to_cpu(((__le16 *)buf->begin)[2]); wsm_printk(XRADIO_DBG_MSG, ">>> 0x%.4X [MIB: 0x%.4X] (%zu)\n", cmd, mib_id, buf_len); } else { wsm_printk(XRADIO_DBG_MSG, ">>> 0x%.4X (%zu)\n", cmd, buf_len); } #ifdef HW_RESTART if (hw_priv->hw_restart) { wsm_printk(XRADIO_DBG_NIY, "hw reset!>>> 0x%.4X (%zu)\n", cmd, buf_len); wsm_buf_reset(buf); return 0; /*return success, don't process cmd in power off.*/ } #endif if (unlikely(hw_priv->bh_error)) { wsm_buf_reset(buf); wsm_printk(XRADIO_DBG_ERROR, "bh error!>>> 0x%.4X (%zu)\n", cmd, buf_len); return -ETIMEDOUT; } /* Fill HI message header */ /* BH will add sequence number */ /* TODO:COMBO: Add if_id from to the WSM header */ /* if_id == -1 indicates that command is HW specific, * eg. wsm_configuration which is called during driver initialzation * (mac80211 .start callback called when first ifce is created.) */ /* send hw specific commands on if 0 */ if (if_id == -1) if_id = 0; ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd | ((is_hardware_xradio(hw_priv)) ? (if_id << 6) : 0)); spin_lock(&hw_priv->wsm_cmd.lock); SYS_BUG(hw_priv->wsm_cmd.ptr); hw_priv->wsm_cmd.done = 0; hw_priv->wsm_cmd.ptr = buf->begin; hw_priv->wsm_cmd.len = buf_len; hw_priv->wsm_cmd.arg = arg; hw_priv->wsm_cmd.cmd = cmd; spin_unlock(&hw_priv->wsm_cmd.lock); xradio_bh_wakeup(hw_priv); if (unlikely(hw_priv->bh_error)) { /* Do not wait for timeout if BH is dead. Exit immediately. */ ret = 0; } else { /* Give start cmd a little more time */ if (unlikely(tmo == WSM_CMD_START_TIMEOUT)) wsm_cmd_max_tmo = WSM_CMD_START_TIMEOUT; else wsm_cmd_max_tmo = WSM_CMD_DEFAULT_TIMEOUT; /*Set max timeout.*/ wsm_cmd_max_tmo = jiffies + wsm_cmd_max_tmo; /* Firmware prioritizes data traffic over control confirm. * Loop below checks if data was RXed and increases timeout * accordingly. */ do { /* It's safe to use unprotected access to wsm_cmd.done here */ ret = wait_event_timeout(hw_priv->wsm_cmd_wq, hw_priv->wsm_cmd.done, tmo); /* check time since last rxed and max timeout.*/ } while (!ret && time_before_eq(jiffies, hw_priv->rx_timestamp+tmo) && time_before(jiffies, wsm_cmd_max_tmo)); } if (unlikely(ret == 0 && !hw_priv->wsm_cmd.done)) { u16 raceCheck; wsm_printk(XRADIO_DBG_ERROR, "***CMD timeout(%ld, %lu, %lu)!>>> 0x%.4X [0x%.4X](%zu)," "cmd_ptr=%p, buf_use=%d, bh_state=%d\n", tmo, wsm_cmd_max_tmo, jiffies, cmd | hw_priv->wsm_cmd.seq, mib_id, buf_len, hw_priv->wsm_cmd.ptr, hw_priv->hw_bufs_used, hw_priv->bh_error); spin_lock(&hw_priv->wsm_cmd.lock); raceCheck = hw_priv->wsm_cmd.cmd; hw_priv->wsm_cmd.arg = NULL; hw_priv->wsm_cmd.ptr = NULL; spin_unlock(&hw_priv->wsm_cmd.lock); /* Race condition check to make sure _confirm is not called * after exit of _send */ if (raceCheck == 0xFFFF) { /* If wsm_handle_rx got stuck in _confirm we will hang * system there. It's better than silently currupt * stack or heap, isn't it? */ SYS_BUG(wait_event_timeout( hw_priv->wsm_cmd_wq, hw_priv->wsm_cmd.done, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); } /* Kill BH thread to report the error to the top layer. */ hw_priv->bh_error = 1; #ifdef BH_USE_SEMAPHORE up(&hw_priv->bh_sem); #else wake_up(&hw_priv->bh_wq); #endif ret = -ETIMEDOUT; } else { spin_lock(&hw_priv->wsm_cmd.lock); SYS_BUG(!hw_priv->wsm_cmd.done); ret = hw_priv->wsm_cmd.ret; spin_unlock(&hw_priv->wsm_cmd.lock); } wsm_buf_reset(buf); return ret; } /* ******************************************************************** */ /* WSM TX port control */ void wsm_lock_tx(struct xradio_common *hw_priv) { down(&hw_priv->tx_lock_sem); atomic_add(1, &hw_priv->tx_lock); /* always check event if wsm_vif_lock_tx.*/ if (wsm_flush_tx(hw_priv)) wsm_printk(XRADIO_DBG_MSG, "TX is locked.\n"); up(&hw_priv->tx_lock_sem); } void wsm_vif_lock_tx(struct xradio_vif *priv) { struct xradio_common *hw_priv = priv->hw_priv; down(&hw_priv->tx_lock_sem); if (atomic_add_return(1, &hw_priv->tx_lock) == 1) { if (wsm_vif_flush_tx(priv)) wsm_printk(XRADIO_DBG_MSG, "TX is locked for" " if_id %d.\n", priv->if_id); } up(&hw_priv->tx_lock_sem); } void wsm_lock_tx_async(struct xradio_common *hw_priv) { if (atomic_add_return(1, &hw_priv->tx_lock) == 1) wsm_printk(XRADIO_DBG_MSG, "TX is locked (async).\n"); } bool wsm_flush_tx(struct xradio_common *hw_priv) { long timeout = WSM_CMD_LAST_CHANCE_TIMEOUT; /* Flush must be called with TX lock held. */ SYS_BUG(!atomic_read(&hw_priv->tx_lock)); /* First check if we really need to do something. * It is safe to use unprotected access, as hw_bufs_used * can only decrements. */ if (!hw_priv->hw_bufs_used) return true; if (hw_priv->bh_error) { /* In case of failure do not wait for magic. */ wsm_printk(XRADIO_DBG_ERROR, "Fatal error occured, " "will not flush TX.\n"); return false; } else { /* Get "oldest" frame, if any frames stuck in firmware, query all of them until max timeout. */ int num = hw_priv->hw_bufs_used + 1; while (xradio_query_txpkt_timeout(hw_priv, XRWL_ALL_IFS, 0xffffffff, &timeout)) { if (timeout < 0 || !num) { /* Hmmm... Not good. Frame had stuck in firmware. */ wsm_printk(XRADIO_DBG_ERROR, "%s:timeout=%ld, hw_bufs_used=%d, num=%d\n", __func__, timeout, hw_priv->hw_bufs_used, num); hw_priv->bh_error = 1; #ifdef BH_USE_SEMAPHORE up(&hw_priv->bh_sem); #else wake_up(&hw_priv->bh_wq); #endif return false; } else if (wait_event_timeout(hw_priv->bh_evt_wq, !hw_priv->hw_bufs_used, timeout) > 0) { return true; } --num; } if (hw_priv->hw_bufs_used) wsm_printk(XRADIO_DBG_WARN, "%s:No pengding, but hw_bufs_used=%d\n", __func__, hw_priv->hw_bufs_used); /* Ok, everything is flushed. */ return true; } } bool wsm_vif_flush_tx(struct xradio_vif *priv) { struct xradio_common *hw_priv = priv->hw_priv; long timeout = WSM_CMD_LAST_CHANCE_TIMEOUT; int if_id = priv->if_id; /* Flush must be called with TX lock held. */ SYS_BUG(!atomic_read(&hw_priv->tx_lock)); /* First check if we really need to do something. * It is safe to use unprotected access, as hw_bufs_used * can only decrements. */ if (!hw_priv->hw_bufs_used_vif[if_id]) return true; if (hw_priv->bh_error) { /* In case of failure do not wait for magic. */ wsm_printk(XRADIO_DBG_ERROR, "Fatal error occured, " "will not flush TX.\n"); return false; } else { /* Get "oldest" frame, if any frames stuck in firmware, query all of them until max timeout. */ int num = hw_priv->hw_bufs_used_vif[if_id] + 1; while (xradio_query_txpkt_timeout(hw_priv, if_id, 0xffffffff, &timeout)) { if (timeout < 0 || !num) { /* Hmmm... Not good. Frame had stuck in firmware. */ wsm_printk(XRADIO_DBG_ERROR, "%s: if_id=%d, hw_bufs_used_vif=%d, num=%d\n", __func__, if_id, hw_priv->hw_bufs_used_vif[priv->if_id], num); hw_priv->bh_error = 1; #ifdef BH_USE_SEMAPHORE up(&hw_priv->bh_sem); #else wake_up(&hw_priv->bh_wq); #endif return false; } else if (wait_event_timeout(hw_priv->bh_evt_wq, !hw_priv->hw_bufs_used_vif[if_id], timeout) > 0) { return true; } --num; } if (hw_priv->hw_bufs_used_vif[if_id]) wsm_printk(XRADIO_DBG_WARN, "%s:No pengding, but hw_bufs_used_vif=%d\n", __func__, hw_priv->hw_bufs_used_vif[priv->if_id]); /* Ok, everything is flushed. */ return true; } } void wsm_unlock_tx(struct xradio_common *hw_priv) { int tx_lock; if (hw_priv->bh_error) wsm_printk(XRADIO_DBG_ERROR, "bh_error=%d, wsm_unlock_tx is unsafe\n", hw_priv->bh_error); else { tx_lock = atomic_sub_return(1, &hw_priv->tx_lock); if (tx_lock < 0) { SYS_BUG(1); } else if (tx_lock == 0) { #if BH_PROC_TX xradio_proc_wakeup(hw_priv); #endif xradio_bh_wakeup(hw_priv); wsm_printk(XRADIO_DBG_MSG, "TX is unlocked.\n"); } } } /* ******************************************************************** */ /* WSM RX */ int wsm_handle_exception(struct xradio_common *hw_priv, u8 *data, size_t len) { struct wsm_buf buf; u32 reason; u32 reg[18]; char fname[48]; int i = 0; #ifdef CONFIG_XRADIO_DEBUG static const char * const reason_str[] = { "undefined instruction", "prefetch abort", "data abort", "unknown error", }; #endif buf.begin = buf.data = data; buf.end = &buf.begin[len]; reason = WSM_GET32(&buf); for (i = 0; i < ARRAY_SIZE(reg); ++i) reg[i] = WSM_GET32(&buf); WSM_GET(&buf, fname, sizeof(fname)); if (reason < 4) { #ifdef SUPPORT_FW_DBG_INF xradio_fw_dbg_set_dump_flag_on_fw_exception(); #endif wsm_printk(XRADIO_DBG_ERROR, "Firmware exception: %s.\n", reason_str[reason]); } else { wsm_printk(XRADIO_DBG_ERROR, "Firmware assert at %.*s, line %d, reason=0x%x\n", (int)sizeof(fname), fname, reg[1], reg[2]); } for (i = 0; i < 12; i += 4) { wsm_printk(XRADIO_DBG_ERROR, "Firmware:" \ "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n", i + 0, reg[i + 0], i + 1, reg[i + 1], i + 2, reg[i + 2], i + 3, reg[i + 3]); } wsm_printk(XRADIO_DBG_ERROR, "Firmware:" \ "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n", reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]); i += 4; wsm_printk(XRADIO_DBG_ERROR, "Firmware:CPSR: 0x%.8X, SPSR: 0x%.8X\n", reg[i + 0], reg[i + 1]); return 0; underflow: wiphy_err(hw_priv->hw->wiphy, "Firmware exception.\n"); print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, data, len); return -EINVAL; } static int wsm_debug_indication(struct xradio_common *hw_priv, struct wsm_buf *buf) { /*for only one debug item.*/ u32 buf_data = 0; u32 dbg_id; u16 dbg_buf_len; u8 dbg_len; u8 *dbg_buf; dbg_id = WSM_GET32(buf); dbg_buf_len = buf->end - buf->data; if (dbg_id == 5) { do { dbg_buf_len = buf->end - buf->data; dbg_len = WSM_GET8(buf); if (dbg_len > dbg_buf_len - sizeof(dbg_len)) { wsm_printk(XRADIO_DBG_ERROR, "[FW]dbg_len = %d\n", dbg_len); wsm_printk(XRADIO_DBG_ERROR, "[FW]dbg_buf_len = %d\n", dbg_buf_len); wsm_printk(XRADIO_DBG_ERROR, "[FW]debug ind err\n"); break; } dbg_buf = buf->data; /*print it;*/ wsm_printk(XRADIO_DBG_ALWY, "[FW-LOG] %s", dbg_buf); buf->data += dbg_len; } while (buf->data < buf->end); } else { wsm_printk(XRADIO_DBG_ERROR, "[FW-DEBUG] DbgId = %d\n", dbg_id); while (buf->end - buf->data >= 4) { buf_data = WSM_GET32(buf); wsm_printk(XRADIO_DBG_ERROR, "[FW-DEBUG] 0x%08X\n", buf_data); } } return 0; underflow: SYS_WARN(1); return -EINVAL; } #if (DGB_XRADIO_HWT) extern u8 hwt_testing; extern u16 hwt_tx_len; extern u16 hwt_tx_num; extern int sent_num; extern struct timeval hwt_start_time; extern struct timeval hwt_end_time; int wsm_hwt_tx_confirm(struct xradio_common *hw_priv, struct wsm_buf *buf) { u8 num = *(buf->data + 6); u16 *through_put = (u16 *)(buf->data) + 3; wsm_printk(XRADIO_DBG_NIY, "%s, num=%d, hw_bufs_used=%d, confirm[7]=%d\n", __func__, num, hw_priv->hw_bufs_used, *(buf->data+7)); /*one release is in bh.*/ wsm_release_vif_tx_buffer(hw_priv, 0, num - 1); wsm_release_tx_buffer(hw_priv, num - 1); /*confirm of last packet, so report the test results.*/ if (*(buf->data+7) & 0x01) { /*last packet*/ u32 time_int = 0; u32 total = hwt_tx_num*hwt_tx_len*8; do_gettimeofday(&hwt_end_time); time_int = (hwt_end_time.tv_sec-hwt_start_time.tv_sec)*1000000 + \ (hwt_end_time.tv_usec-hwt_start_time.tv_usec); wsm_printk(XRADIO_DBG_ALWY, "%s, HIF TX: time=%dms, throughput=%d.%dMbps\n", __func__, time_int/1000, total/time_int, (total%time_int)*10/time_int); *through_put = (u16)((total*10)/time_int); hwt_tx_len = 0; hwt_tx_num = 0; sent_num = 0; /*reset the sent_num*/ hwt_testing = 0; return 1; } return 0; } u16 recv_num; extern u8 hwt_rx_en; extern u16 hwt_rx_len; extern u16 hwt_rx_num; int wsm_hwt_rx_frames(struct xradio_common *hw_priv, struct wsm_buf *buf) { wsm_printk(XRADIO_DBG_NIY, "%s, status=%d, len=%d\n", __func__, *(u16 *)(buf->data+2), *(u16 *)(buf->data+4)); recv_num++; if (recv_num >= hwt_rx_num) { /*last packet*/ u32 time_int = 0; u32 total = recv_num*hwt_rx_len*8; do_gettimeofday(&hwt_end_time); time_int = (hwt_end_time.tv_sec-hwt_start_time.tv_sec)*1000000 + \ (hwt_end_time.tv_usec-hwt_start_time.tv_usec); // wsm_printk(XRADIO_DBG_ALWY, // "%s, HIF RX: time=%dms, throughput=%d.%dMbps\n", // __func__, time_int/1000, total/time_int, // (total%time_int)*10/time_int); hwt_rx_en = 0; hwt_rx_num = 0; recv_num = 0; /*reset the recv_num*/ // hwt_testing = 0; //set to 0 when 0x404 received } return 0; } int wsm_hwt_enc_results(struct xradio_common *hw_priv, struct wsm_buf *buf) { wsm_printk(XRADIO_DBG_ALWY, "%s, status=%d, enc throughput=%d.%02dMbps\n", __func__, *(u16 *)(buf->data+2), *(u32 *)(buf->data+8), *(u32 *)(buf->data+12)); hwt_testing = 0; return 0; } int wsm_hwt_mic_results(struct xradio_common *hw_priv, struct wsm_buf *buf) { wsm_printk(XRADIO_DBG_ALWY, "%s, status=%d, mic throughput=%d.%02dMbps\n", __func__, *(u16 *)(buf->data+2), *(u32 *)(buf->data+8), *(u32 *)(buf->data+12)); hwt_testing = 0; return 0; } #endif /*DGB_XRADIO_HWT*/ #if PERF_INFO_TEST struct timeval ind_rx_time; #endif int wsm_handle_rx(struct xradio_common *hw_priv, u8 flags, struct sk_buff **skb_p) { int ret = 0; struct xradio_vif *priv = NULL; int i = 0; struct wsm_buf wsm_buf; size_t total_len = (*skb_p)->len; struct wsm_hdr *wsm = (struct wsm_hdr *)((*skb_p)->data); int id = __le32_to_cpu(wsm->id) & 0xFFF; int interface_link_id = (id >> 6) & 0x0F; #ifdef ROAM_OFFLOAD #if 0 struct xradio_vif *priv; priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id); if (unlikely(!priv)) { SYS_WARN(1); return 0; } spin_unlock(&priv->vif_lock); #endif #endif/*ROAM_OFFLOAD*/ /* Strip link id. */ id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); wsm_buf.begin = (u8 *)&wsm[0]; wsm_buf.data = (u8 *)&wsm[1]; wsm_buf.end = &wsm_buf.begin[__le32_to_cpu(wsm->len)]; wsm_printk(XRADIO_DBG_MSG, "<<< 0x%.4X (%zu)\n", id, (size_t)(wsm_buf.end - wsm_buf.begin)); #ifdef CONFIG_XRADIO_ETF if (etf_is_connect()) { if (id == 0x0801) { /* ETF_CONTEXT_OFFSET need to consist with etf fw.*/ u8 *pContext = wsm_buf.data + ETF_CONTEXT_OFFSET; xradio_etf_save_context(pContext, (int)(wsm_buf.end - pContext)); wsm_startup_indication(hw_priv, &wsm_buf); } #if (DGB_XRADIO_HWT) /***************************for HWT ********************************/ else if (id == 0x0404) { u16 TestID = *(u16 *)(wsm_buf.data); if (TestID == 1) { /*test frame confirm.*/ if (wsm_hwt_tx_confirm(hw_priv, &wsm_buf)) { spin_lock(&hw_priv->wsm_cmd.lock); hw_priv->wsm_cmd.ret = *((u16 *)(wsm_buf.data) + 3); hw_priv->wsm_cmd.done = 1; spin_unlock(&hw_priv->wsm_cmd.lock); wake_up(&hw_priv->wsm_cmd_wq); wsm_printk(XRADIO_DBG_ALWY, "%s:HWT TestID=0x%x Confirm ret=%d\n", __func__, *(u16 *)(wsm_buf.data), hw_priv->wsm_cmd.ret); return xradio_etf_from_device(skb_p); } } else { spin_lock(&hw_priv->wsm_cmd.lock); hw_priv->wsm_cmd.ret = *((u16 *)(wsm_buf.data) + 1); hw_priv->wsm_cmd.done = 1; spin_unlock(&hw_priv->wsm_cmd.lock); wake_up(&hw_priv->wsm_cmd_wq); hwt_testing = 0; wsm_printk(XRADIO_DBG_ALWY, "%s:HWT TestID=0x%x Confirm ret=%d\n", __func__, *(u16 *)(wsm_buf.data), hw_priv->wsm_cmd.ret); return xradio_etf_from_device(skb_p); } return 0; } else if (id == 0x0804) { u16 TestID = *(u16 *)(wsm_buf.data); switch (TestID) { case 2: /*recieve a test frame.*/ wsm_hwt_rx_frames(hw_priv, &wsm_buf); break; case 3: /*enc test result.*/ wsm_hwt_enc_results(hw_priv, &wsm_buf); break; case 4: /*mic test result.*/ wsm_hwt_mic_results(hw_priv, &wsm_buf); break; case 5: break; default: wsm_printk(XRADIO_DBG_ERROR, "HWT ERROR Indication TestID=0x%x\n", TestID); break; } return 0; } /***************************for HWT ********************************/ #endif /*DGB_XRADIO_HWT*/ else { spin_lock(&hw_priv->wsm_cmd.lock); hw_priv->wsm_cmd.ret = 0; hw_priv->wsm_cmd.done = 1; spin_unlock(&hw_priv->wsm_cmd.lock); wake_up(&hw_priv->wsm_cmd_wq); } return xradio_etf_from_device(skb_p); } #endif #if (DGB_XRADIO_HWT) /***************************for HWT ********************************/ if (id == 0x0424) { u16 TestID = *(u16 *)(wsm_buf.data); if (TestID == 1) /*test frame confirm.*/ wsm_hwt_tx_confirm(hw_priv, &wsm_buf); else { spin_lock(&hw_priv->wsm_cmd.lock); hw_priv->wsm_cmd.ret = *((u16 *)(wsm_buf.data) + 1); hw_priv->wsm_cmd.done = 1; spin_unlock(&hw_priv->wsm_cmd.lock); wake_up(&hw_priv->wsm_cmd_wq); wsm_printk(XRADIO_DBG_ALWY, "HWT TestID=0x%x Confirm ret=%d\n", *(u16 *)(wsm_buf.data), hw_priv->wsm_cmd.ret); } return 0; } else if (id == 0x0824) { u16 TestID = *(u16 *)(wsm_buf.data); switch (TestID) { case 2: /*recieve a test frame.*/ wsm_hwt_rx_frames(hw_priv, &wsm_buf); break; case 3: /*enc test result.*/ wsm_hwt_enc_results(hw_priv, &wsm_buf); break; case 4: /*mic test result.*/ wsm_hwt_mic_results(hw_priv, &wsm_buf); break; case 5: break; default: wsm_printk(XRADIO_DBG_ERROR, "HWT ERROR Indication TestID=0x%x\n", TestID); break; } return 0; } /***************************for HWT ********************************/ #endif /*DGB_XRADIO_HWT*/ if (id == 0x404) { DBG_ARRY_ADD(dbg_txconfirm, 0); ret = wsm_tx_confirm(hw_priv, &wsm_buf, interface_link_id); #ifdef MCAST_FWDING #if 1 } else if (id == 0x422) { ret = wsm_give_buffer_confirm(hw_priv, &wsm_buf); #endif #endif } else if (id == 0x41E) { ret = wsm_multi_tx_confirm(hw_priv, &wsm_buf, interface_link_id); } else if (id & 0x0400) { void *wsm_arg; u16 wsm_cmd; /* Do not trust FW too much. Protection against repeated * response and race condition removal (see above). */ spin_lock(&hw_priv->wsm_cmd.lock); wsm_arg = hw_priv->wsm_cmd.arg; wsm_cmd = hw_priv->wsm_cmd.cmd & ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); hw_priv->wsm_cmd.cmd = 0xFFFF; spin_unlock(&hw_priv->wsm_cmd.lock); if (SYS_WARN((id & ~0x0400) != wsm_cmd)) { /* Note that any non-zero is a fatal retcode. */ ret = -EINVAL; goto out; } switch (id) { case 0x0409: /* Note that wsm_arg can be NULL in case of timeout in * wsm_cmd_send(). */ if (likely(wsm_arg)) ret = wsm_configuration_confirm(hw_priv, wsm_arg, &wsm_buf); break; case 0x0405: if (likely(wsm_arg)) ret = wsm_read_mib_confirm(hw_priv, wsm_arg, &wsm_buf); break; case 0x0406: if (likely(wsm_arg)) ret = wsm_write_mib_confirm(hw_priv, wsm_arg, &wsm_buf, interface_link_id); break; case 0x040B: if (likely(wsm_arg)) ret = wsm_join_confirm(hw_priv, wsm_arg, &wsm_buf); if (ret) wsm_printk(XRADIO_DBG_WARN, "Join confirm Failed!\n"); break; case 0x040E: /* 11K measure*/ if (likely(wsm_arg)) ret = wsm_generic_confirm(hw_priv, wsm_arg, &wsm_buf); if (ret) wsm_printk(XRADIO_DBG_ERROR, "[***11K***] Confirm Error\n"); break; #ifdef MCAST_FWDING case 0x0423: /* req buffer cfm*/ if (likely(wsm_arg)) { xradio_for_each_vif(hw_priv, priv, i) { if (priv && (priv->join_status == XRADIO_JOIN_STATUS_AP)) ret = wsm_request_buffer_confirm(priv, wsm_arg, &wsm_buf); } } break; #endif case 0x0425: ret = wsm_fw_dbg_confirm(hw_priv, wsm_arg, &wsm_buf); if (ret) wsm_printk(XRADIO_DBG_ERROR, "[0x%04x] ret(%d): Confirm Error, msg_len:%d\n", id, ret, wsm->len); break; case 0x0407: /* start-scan */ #ifdef ROAM_OFFLOAD if (hw_priv->auto_scanning) { if (atomic_read(&hw_priv->scan.in_progress)) { hw_priv->auto_scanning = 0; } else { wsm_oper_unlock(hw_priv); up(&hw_priv->scan.lock); } } #endif /*ROAM_OFFLOAD*/ case 0x0408: /* stop-scan */ case 0x040A: /* wsm_reset */ case 0x040C: /* add_key */ case 0x040D: /* remove_key */ case 0x0410: /* wsm_set_pm */ case 0x0411: /* set_bss_params */ case 0x0412: /* set_tx_queue_params */ case 0x0413: /* set_edca_params */ case 0x0416: /* switch_channel */ case 0x0417: /* start */ case 0x0418: /* beacon_transmit */ case 0x0419: /* start_find */ case 0x041A: /* stop_find */ case 0x041B: /* update_ie */ case 0x041C: /* map_link */ SYS_WARN(wsm_arg != NULL); ret = wsm_generic_confirm(hw_priv, wsm_arg, &wsm_buf); if (ret) wsm_printk(XRADIO_DBG_ERROR, "wsm_generic_confirm " "failed for request 0x%.4X ret=%d.\n", id & ~0x0400, ret); break; default: SYS_BUG(1); } spin_lock(&hw_priv->wsm_cmd.lock); hw_priv->wsm_cmd.ret = ret; hw_priv->wsm_cmd.done = 1; spin_unlock(&hw_priv->wsm_cmd.lock); ret = 0; /* Error response from device should ne stop BH. */ wake_up(&hw_priv->wsm_cmd_wq); } else if (id & 0x0800) { switch (id) { case 0x0801: ret = wsm_startup_indication(hw_priv, &wsm_buf); break; case 0x0804: if (xradio_realloc_resv_skb(hw_priv, *skb_p, flags)) { /* we reuse this skb, because fail to realloc.*/ wsm_printk(XRADIO_DBG_WARN, "xradio_realloc_resv_skb drop frames,len=%d.\n", (*skb_p)->len); *skb_p = NULL; ret = 0; goto out; } /* * if packet len < total length - piggy back length * then multi receive indication. */ PERF_INFO_GETTIME(&ind_rx_time); if (ROUND4(__le32_to_cpu(wsm->len)) < total_len) { ret = wsm_multi_receive_indication(hw_priv, total_len, &wsm_buf, skb_p); } else { if (__le32_to_cpu(wsm->len) != total_len) wsm_printk(XRADIO_DBG_WARN, "wsm->len=%u, total_len=%zu", __le32_to_cpu(wsm->len), total_len); ret = wsm_receive_indication(hw_priv, interface_link_id, &wsm_buf, skb_p); } PERF_INFO_STAMP(&ind_rx_time, &ind_rx, total_len); break; case 0x0805: ret = wsm_event_indication(hw_priv, &wsm_buf, interface_link_id); break; case 0x0807: wsm_printk(XRADIO_DBG_ERROR, "[11K]wsm_measure_cmpl_indication\n"); ret = wsm_measure_cmpl_indication(hw_priv, &wsm_buf); break; case 0x080A: ret = wsm_channel_switch_indication(hw_priv, &wsm_buf); break; case 0x0809: ret = wsm_set_pm_indication(hw_priv, &wsm_buf); break; case 0x0806: #ifdef ROAM_OFFLOAD if (hw_priv->auto_scanning && hw_priv->frame_rcvd) { struct xradio_vif *priv; hw_priv->frame_rcvd = 0; priv = xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id); if (unlikely(!priv)) { SYS_WARN(1); return 0; } spin_unlock(&priv->vif_lock); if (hw_priv->beacon) { struct wsm_scan_complete *scan_cmpl = \ (struct wsm_scan_complete *) \ ((u8 *)wsm + sizeof(struct wsm_hdr)); struct ieee80211_rx_status *rhdr = \ IEEE80211_SKB_RXCB(hw_priv->beacon); rhdr->signal = (s8)scan_cmpl->reserved; if (!priv->cqm_use_rssi) { rhdr->signal = rhdr->signal / 2 - 110; } if (!hw_priv->beacon_bkp) hw_priv->beacon_bkp = \ skb_copy(hw_priv->beacon, GFP_ATOMIC); mac80211_rx_irqsafe(hw_priv->hw, hw_priv->beacon); hw_priv->beacon = hw_priv->beacon_bkp; hw_priv->beacon_bkp = NULL; } wsm_printk(XRADIO_DBG_MSG, \ "Send Testmode Event.\n"); xradio_testmode_event(priv->hw->wiphy, NL80211_CMD_NEW_SCAN_RESULTS, 0, 0, GFP_KERNEL); } #endif /*ROAM_OFFLOAD*/ ret = wsm_scan_complete_indication(hw_priv, &wsm_buf); break; case 0x080B: ret = wsm_find_complete_indication(hw_priv, &wsm_buf); break; case 0x080C: ret = wsm_suspend_resume_indication(hw_priv, interface_link_id, &wsm_buf); break; case 0x080E: wsm_printk(XRADIO_DBG_MSG, "wsm_debug_indication"); ret = wsm_debug_indication(hw_priv, &wsm_buf); break; case 0x0825: ret = wsm_fw_dbg_indicate(hw_priv, &wsm_buf); if (ret) wsm_printk(XRADIO_DBG_ERROR, "[0x%04x] ret(%d): indicate Error, msg_len:%d\n", id, ret, wsm->len); break; default: wsm_printk(XRADIO_DBG_ERROR, "unknown Indmsg ID=0x%04x,len=%d\n", wsm->id, wsm->len); break; } } else { SYS_WARN(1); ret = -EINVAL; } out: return ret; } static bool wsm_handle_tx_data(struct xradio_vif *priv, const struct wsm_tx *wsm, const struct ieee80211_tx_info *tx_info, struct xradio_txpriv *txpriv, struct xradio_queue *queue) { struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); #ifdef P2P_MULTIVIF struct xradio_vif *p2p_if_vif = NULL; #endif bool handled = false; const struct ieee80211_hdr *frame = (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; __le16 fctl = frame->frame_control; enum { doProbe, doDrop, doJoin, doOffchannel, doWep, doTx, } action = doTx; hw_priv = xrwl_vifpriv_to_hwpriv(priv); #ifdef P2P_MULTIVIF if (priv->if_id == XRWL_GENERIC_IF_ID) p2p_if_vif = __xrwl_hwpriv_to_vifpriv(hw_priv, 1); #endif frame = (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; fctl = frame->frame_control; switch (priv->mode) { case NL80211_IFTYPE_STATION: if (unlikely(priv->bss_loss_status == XRADIO_BSS_LOSS_CHECKING && priv->join_status == XRADIO_JOIN_STATUS_STA) && ieee80211_is_data(fctl)) { spin_lock(&priv->bss_loss_lock); priv->bss_loss_confirm_id = wsm->packetID; priv->bss_loss_status = XRADIO_BSS_LOSS_CONFIRMING; spin_unlock(&priv->bss_loss_lock); } else if (unlikely((priv->join_status <= XRADIO_JOIN_STATUS_MONITOR) || memcmp(frame->addr1, priv->join_bssid, sizeof(priv->join_bssid)))) { #ifdef P2P_MULTIVIF if (p2p_if_vif && (p2p_if_vif->join_status > XRADIO_JOIN_STATUS_MONITOR) && (priv->join_status < XRADIO_JOIN_STATUS_MONITOR)) { /* Post group formation, frame transmission on p2p0 * interafce should not use offchannel/generic channel. * Instead, the frame should be transmitted on interafce * 1. This is needed by wsc fw. */ action = doTx; txpriv->raw_if_id = 1; } else #endif if (ieee80211_is_auth(fctl)) action = doJoin; else if ((ieee80211_is_deauth(fctl) || ieee80211_is_disassoc(fctl)) && priv->join_status < XRADIO_JOIN_STATUS_MONITOR) /* no need to send deauth when STA-unjoined.*/ action = doDrop; else if (ieee80211_is_probe_req(fctl)) action = doTx; else if (memcmp(frame->addr1, priv->join_bssid, sizeof(priv->join_bssid)) && (priv->join_status == XRADIO_JOIN_STATUS_STA) && (ieee80211_is_data(fctl))) { action = doDrop; } else if (priv->join_status >= XRADIO_JOIN_STATUS_MONITOR) action = doTx; else if (get_interface_id_scanning(hw_priv) != -1) { wsm_printk(XRADIO_DBG_WARN, "Scan ONGOING dropping" " offchannel eligible frame.\n"); action = doDrop; } else { action = doOffchannel; wsm_printk(XRADIO_DBG_WARN, "Offchannel fctl=0x%04x", fctl); } } break; case NL80211_IFTYPE_AP: if (unlikely(!priv->join_status)) action = doDrop; else if (unlikely(!(BIT(txpriv->raw_link_id) & (BIT(0) | priv->link_id_map)))) { wsm_printk(XRADIO_DBG_WARN, "A frame with expired link id " "is dropped.\n"); action = doDrop; } if (xradio_queue_get_generation(wsm->packetID) > XRADIO_MAX_REQUEUE_ATTEMPTS) { /* HACK!!! WSM324 firmware has tendency to requeue * multicast frames in a loop, causing performance * drop and high power consumption of the driver. * In this situation it is better just to drop * the problematic frame. */ wsm_printk(XRADIO_DBG_WARN, "Too many attempts " "to requeue a frame. " "Frame is dropped, fctl=0x%04x.\n", fctl); action = doDrop; } break; case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: /*STUB();*/ case NL80211_IFTYPE_MONITOR: default: action = doDrop; break; } if (action == doTx) { if (unlikely(ieee80211_is_probe_req(fctl))) { #ifdef CONFIG_XRADIO_TESTMODE if (hw_priv->enable_advance_scan && (priv->join_status == XRADIO_JOIN_STATUS_STA) && (hw_priv->advanceScanElems.scanMode == XRADIO_SCAN_MEASUREMENT_ACTIVE)) /* If Advance Scan is Requested on Active Scan * then transmit the Probe Request */ action = doTx; else #endif action = doProbe; } else if ((fctl & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) && tx_info->control.hw_key && unlikely(tx_info->control.hw_key->keyidx != priv->wep_default_key_id) && (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 || tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) { action = doWep; } } switch (action) { case doProbe: { /* An interesting FW "feature". Device filters * probe responses. * The easiest way to get it back is to convert * probe request into WSM start_scan command. */ wsm_printk(XRADIO_DBG_MSG, \ "Convert probe request to scan.\n"); wsm_lock_tx_async(hw_priv); hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.probe_work, 0); handled = true; } break; case doDrop: { /* See detailed description of "join" below. * We are dropping everything except AUTH in non-joined mode. */ wsm_printk(XRADIO_DBG_MSG, "Drop frame (0x%.4X).\n", fctl); #ifdef CONFIG_XRADIO_TESTMODE SYS_BUG(xradio_queue_remove(hw_priv, queue, __le32_to_cpu(wsm->packetID))); #else SYS_BUG(xradio_queue_remove(queue, __le32_to_cpu(wsm->packetID))); #endif /*CONFIG_XRADIO_TESTMODE*/ handled = true; } break; case doJoin: { /* p2p should disconnect when sta try to join a different channel AP, * because no good performance in this case. */ struct xradio_vif *p2p_tmp_vif = __xrwl_hwpriv_to_vifpriv(hw_priv, 1); if (priv->if_id == 0 && p2p_tmp_vif) { if (p2p_tmp_vif->join_status >= XRADIO_JOIN_STATUS_STA && hw_priv->channel_changed) { wsm_printk(XRADIO_DBG_WARN, "combo with different channels, p2p disconnect.\n"); wsm_send_disassoc_to_self(hw_priv, p2p_tmp_vif); } } /* There is one more interesting "feature" * in FW: it can't do RX/TX before "join". * "Join" here is not an association, * but just a syncronization between AP and STA. * priv->join_status is used only in bh thread and does * not require protection */ wsm_printk(XRADIO_DBG_NIY, "Issue join command.\n"); wsm_lock_tx_async(hw_priv); hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); if (queue_work(hw_priv->workqueue, &priv->join_work) <= 0) wsm_unlock_tx(hw_priv); handled = true; } break; case doOffchannel: { wsm_printk(XRADIO_DBG_MSG, "Offchannel TX request.\n"); wsm_lock_tx_async(hw_priv); hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); if (queue_work(hw_priv->workqueue, &priv->offchannel_work) <= 0) wsm_unlock_tx(hw_priv); handled = true; } break; case doWep: { wsm_printk(XRADIO_DBG_MSG, "Issue set_default_wep_key.\n"); wsm_lock_tx_async(hw_priv); priv->wep_default_key_id = tx_info->control.hw_key->keyidx; hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); if (queue_work(hw_priv->workqueue, &priv->wep_key_work) <= 0) wsm_unlock_tx(hw_priv); handled = true; } break; case doTx: { #if 0 /* Kept for history. If you want to implement wsm->more, * make sure you are able to send a frame after that. */ wsm->more = (count > 1) ? 1 : 0; if (wsm->more) { /* HACK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * It's undocumented in WSM spec, but XRADIO hangs * if 'more' is set and no TX is performed due to TX * buffers limitation. */ if (priv->hw_bufs_used + 1 == priv->wsm_caps.numInpChBufs) wsm->more = 0; } /* BUG!!! FIXME: we can't use 'more' at all: we don't know * future. It could be a request from upper layer with TX lock * requirements (scan, for example). If "more" is set device * will not send data and wsm_tx_lock() will fail... * It's not obvious how to fix this deadlock. Any ideas? * As a workaround more is set to 0. */ wsm->more = 0; #endif /* 0 */ if (ieee80211_is_deauth(fctl) && priv->mode != NL80211_IFTYPE_AP) { /* Shedule unjoin work */ wsm_printk(XRADIO_DBG_WARN, "Issue unjoin command(TX).\n"); #if 0 wsm->more = 0; #endif /* 0 */ wsm_lock_tx_async(hw_priv); if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0) wsm_unlock_tx(hw_priv); } } break; } return handled; } static int xradio_get_prio_queue(struct xradio_vif *priv, u32 link_id_map, int *total) { struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); static u32 urgent; struct wsm_edca_queue_params *edca; unsigned score, best = -1; int winner = -1; int queued; int i; urgent = BIT(priv->link_id_after_dtim) | BIT(priv->link_id_uapsd); /* search for a winner using edca params */ for (i = 0; i < 4; ++i) { queued = xradio_queue_get_num_queued(priv, &hw_priv->tx_queue[i], link_id_map); if (!queued) continue; *total += queued; edca = &priv->edca.params[i]; score = ((edca->aifns + edca->cwMin) << 16) + (edca->cwMax - edca->cwMin) * #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)) (random32() & 0xFFFF); #else (get_random_int() & 0xFFFF); #endif if (score < best && (winner < 0 || i != 3)) { best = score; winner = i; } } /* override winner if bursting */ if (winner >= 0 && hw_priv->tx_burst_idx >= 0 && winner != hw_priv->tx_burst_idx && !xradio_queue_get_num_queued(priv, &hw_priv->tx_queue[winner], link_id_map & urgent) && xradio_queue_get_num_queued(priv, &hw_priv->tx_queue[hw_priv->tx_burst_idx], link_id_map)) winner = hw_priv->tx_burst_idx; return winner; } static int wsm_get_tx_queue_and_mask(struct xradio_vif *priv, struct xradio_queue **queue_p, u32 *tx_allowed_mask_p, bool *more) { struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); int idx; u32 tx_allowed_mask; int total = 0; /* Search for a queue with multicast frames buffered */ if (priv->tx_multicast) { tx_allowed_mask = BIT(priv->link_id_after_dtim); idx = xradio_get_prio_queue(priv, tx_allowed_mask, &total); if (idx >= 0) { *more = total > 1; goto found; } } /* Search for unicast traffic */ tx_allowed_mask = ~priv->sta_asleep_mask; tx_allowed_mask |= BIT(priv->link_id_uapsd); if (priv->sta_asleep_mask) { tx_allowed_mask |= priv->pspoll_mask; tx_allowed_mask &= ~BIT(priv->link_id_after_dtim); } else { tx_allowed_mask |= BIT(priv->link_id_after_dtim); } idx = xradio_get_prio_queue(priv, tx_allowed_mask, &total); if (idx < 0) return -ENOENT; found: *queue_p = &hw_priv->tx_queue[idx]; *tx_allowed_mask_p = tx_allowed_mask; return 0; } int wsm_get_tx(struct xradio_common *hw_priv, u8 **data, size_t *tx_len, int *burst, int *vif_selected) { struct wsm_tx *wsm = NULL; struct ieee80211_tx_info *tx_info; struct xradio_queue *queue = NULL; int queue_num; u32 tx_allowed_mask = 0; struct xradio_txpriv *txpriv = NULL; #ifdef P2P_MULTIVIF int first = 1; int tmp_if_id = -1; #endif #if BH_PROC_TX u8 *tx_item = NULL; #endif /* * Count was intended as an input for wsm->more flag. * During implementation it was found that wsm->more * is not usable, see details above. It is kept just * in case you would like to try to implement it again. */ int count = 0; #ifdef P2P_MULTIVIF int if_pending = XRWL_MAX_VIFS - 1; #else int if_pending = 1; #endif /* More is used only for broadcasts. */ bool more = false; count = xradio_itp_get_tx(hw_priv, data, tx_len, burst); if (count) return count; #if !BH_PROC_TX if (hw_priv->wsm_cmd.ptr) { ++count; spin_lock(&hw_priv->wsm_cmd.lock); SYS_BUG(!hw_priv->wsm_cmd.ptr); *data = hw_priv->wsm_cmd.ptr; *tx_len = hw_priv->wsm_cmd.len; *burst = 1; *vif_selected = -1; spin_unlock(&hw_priv->wsm_cmd.lock); } else { #endif for (;;) { int ret; struct xradio_vif *priv; #if 0 int num_pending_vif0, num_pending_vif1; #endif if (atomic_add_return(0, &hw_priv->tx_lock)) { wsm_printk(XRADIO_DBG_NIY, "%s:tx_lock!", __func__); break; } if (xradio_is_bt_block(hw_priv)) { wsm_printk(XRADIO_DBG_NIY, "%s:BT is busy, lock tx!", __func__); break; } /* Keep one buffer reserved for commands. Note that, hw_bufs_used has already been incremented before reaching here. */ if (hw_priv->hw_bufs_used >= hw_priv->wsm_caps.numInpChBufs) { DBG_INT_ADD(tx_buf_limit); break; } #ifdef P2P_MULTIVIF if (first) { tmp_if_id = hw_priv->if_id_selected; hw_priv->if_id_selected = 2; } #endif priv = wsm_get_interface_for_tx(hw_priv); /* go to next interface ID to select next packet */ #ifdef P2P_MULTIVIF if (first) { hw_priv->if_id_selected = tmp_if_id; first = 0; } else #endif hw_priv->if_id_selected ^= 1; /* There might be no interface before add_interface * call */ if (!priv) { if (if_pending) { #ifdef P2P_MULTIVIF if_pending--; #else if_pending = 0; #endif continue; } break; } #if 0 if (((priv->if_id == 0) && (hw_priv->hw_bufs_used_vif[0] >= XRWL_FW_VIF0_THROTTLE)) || ((priv->if_id == 1) && (hw_priv->hw_bufs_used_vif[1] >= XRWL_FW_VIF1_THROTTLE))) { spin_unlock(&priv->vif_lock); if (if_pending) { if_pending = 0; continue; } break; } #endif /* This can be removed probably: xradio_vif will not * be in hw_priv->vif_list (as returned from * wsm_get_interface_for_tx) until it's fully * enabled, so statement above will take case of that*/ if (!atomic_read(&priv->enabled)) { spin_unlock(&priv->vif_lock); break; } /* TODO:COMBO: Find the next interface for which * packet needs to be found */ spin_lock_bh(&priv->ps_state_lock); ret = wsm_get_tx_queue_and_mask(priv, &queue, &tx_allowed_mask, &more); queue_num = queue - hw_priv->tx_queue; if (priv->buffered_multicasts && (ret || !more) && (priv->tx_multicast || !priv->sta_asleep_mask)) { priv->buffered_multicasts = false; if (priv->tx_multicast) { priv->tx_multicast = false; queue_work(hw_priv->workqueue, &priv->multicast_stop_work); } } spin_unlock_bh(&priv->ps_state_lock); if (ret) { spin_unlock(&priv->vif_lock); #ifdef P2P_MULTIVIF if (if_pending) { #else if (if_pending == 1) { #endif #ifdef P2P_MULTIVIF if_pending--; #else if_pending = 0; #endif continue; } break; } #if BH_PROC_TX if (xradio_queue_get(queue, priv->if_id, tx_allowed_mask, &wsm, &tx_info, &txpriv, &tx_item)) { spin_unlock(&priv->vif_lock); if_pending = 0; continue; } #else if (xradio_queue_get(queue, priv->if_id, tx_allowed_mask, &wsm, &tx_info, &txpriv)) { wsm_printk(XRADIO_DBG_WARN, "%s, if_id=%d(enable=%d), tx_allowed_mask=%08x," \ "queue_num=%d, queued_item=%d, pending_item=%d" \ "link_id_after_dtim=%d, link_id_uapsd=%d, tx_multicast=%d," \ "pspoll_mask=%d, sta_asleep_mask=%d\n", __func__, priv->if_id, atomic_read(&priv->enabled), tx_allowed_mask, queue_num, queue->num_queued, queue->num_pending, priv->link_id_after_dtim, priv->link_id_uapsd, priv->tx_multicast, priv->pspoll_mask, priv->sta_asleep_mask); spin_unlock(&priv->vif_lock); if_pending = 0; continue; } #endif #ifdef ROC_DEBUG #ifndef P2P_MULTIVIF { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; wsm_printk(XRADIO_DBG_ERROR, "QGET-1 %x, off_id %d," " if_id %d\n", hdr->frame_control, txpriv->offchannel_if_id, priv->if_id); } #else { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; wsm_printk(XRADIO_DBG_ERROR, "QGET-1 %x, off_id %d," " if_id %d\n", hdr->frame_control, txpriv->raw_if_id, priv->if_id); } #endif #endif if (wsm_handle_tx_data(priv, wsm, tx_info, txpriv, queue)) { spin_unlock(&priv->vif_lock); if_pending = 0; continue; /* Handled by WSM */ } wsm->hdr.id &= __cpu_to_le16( ~WSM_TX_IF_ID(WSM_TX_IF_ID_MAX)); #ifdef P2P_MULTIVIF if (txpriv->raw_if_id) wsm->hdr.id |= cpu_to_le16( WSM_TX_IF_ID(txpriv->raw_if_id)); #else if (txpriv->offchannel_if_id) wsm->hdr.id |= cpu_to_le16( WSM_TX_IF_ID(txpriv->offchannel_if_id)); #endif else wsm->hdr.id |= cpu_to_le16( WSM_TX_IF_ID(priv->if_id)); *vif_selected = priv->if_id; #ifdef ROC_DEBUG #ifndef P2P_MULTIVIF { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; wsm_printk(XRADIO_DBG_ERROR, "QGET-2 %x, off_id %d," " if_id %d\n", hdr->frame_control, txpriv->offchannel_if_id, priv->if_id); } #else { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; wsm_printk(XRADIO_DBG_ERROR, "QGET-2 %x, off_id %d," " if_id %d\n", hdr->frame_control, txpriv->raw_if_id, priv->if_id); } #endif #endif priv->pspoll_mask &= ~BIT(txpriv->raw_link_id); #if BH_PROC_TX *data = tx_item; #else *data = (u8 *)wsm; #endif *tx_len = __le16_to_cpu(wsm->hdr.len); /* allow bursting if txop is set */ if (priv->edca.params[queue_num].txOpLimit) *burst = min(*burst, (int)xradio_queue_get_num_queued(priv, queue, tx_allowed_mask) + 1); else *burst = 1; /* store index of bursting queue */ if (*burst > 1) hw_priv->tx_burst_idx = queue_num; else hw_priv->tx_burst_idx = -1; if (more) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; if (strstr(&priv->ssid[0], "6.1.12")) { if (hdr->addr1[0] & 0x01) { hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); } } else { /* more buffered multicast/broadcast frames * ==> set MoreData flag in IEEE 802.11 header * to inform PS STAs */ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); } } wsm_printk(XRADIO_DBG_MSG, ">>> 0x%.4X (%zu) %p %c\n", 0x0004, *tx_len, wsm, wsm->more ? 'M' : ' '); ++count; spin_unlock(&priv->vif_lock); break; } #if !BH_PROC_TX } #endif return count; } void wsm_txed(struct xradio_common *hw_priv, u8 *data) { if (data == hw_priv->wsm_cmd.ptr) { spin_lock(&hw_priv->wsm_cmd.lock); hw_priv->wsm_cmd.ptr = NULL; spin_unlock(&hw_priv->wsm_cmd.lock); } } /* ******************************************************************** */ /* WSM buffer */ void wsm_buf_init(struct wsm_buf *buf, int size) { SYS_BUG(buf->begin); buf->begin = xr_kmalloc(size, true); buf->end = buf->begin ? &buf->begin[size] : buf->begin; wsm_buf_reset(buf); } void wsm_buf_deinit(struct wsm_buf *buf) { if (likely(buf->begin)) kfree(buf->begin); buf->begin = buf->data = buf->end = NULL; } static void wsm_buf_reset(struct wsm_buf *buf) { if (likely(buf->begin)) { buf->data = &buf->begin[4]; *(u32 *)buf->begin = 0; } else buf->data = buf->begin; } static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size) { size_t pos = buf->data - buf->begin; size_t size = pos + extra_size; size = xr_sdio_blksize_align(size); buf->begin = xr_krealloc(buf->begin, size, true); if (buf->begin) { buf->data = &buf->begin[pos]; buf->end = &buf->begin[size]; return 0; } else { buf->end = buf->data = buf->begin; return -ENOMEM; } } static struct xradio_vif * wsm_get_interface_for_tx(struct xradio_common *hw_priv) { struct xradio_vif *priv = NULL, *i_priv; int i = hw_priv->if_id_selected; if (1) { /*TODO:COMBO*/ spin_lock(&hw_priv->vif_list_lock); i_priv = hw_priv->vif_list[i] ? xrwl_get_vif_from_ieee80211(hw_priv->vif_list[i]) : NULL; if (i_priv && atomic_read(&i_priv->enabled)) { priv = i_priv; spin_lock(&priv->vif_lock); } /* TODO:COMBO: * Find next interface based on TX bitmap announced by the FW * Find next interface based on load balancing */ spin_unlock(&hw_priv->vif_list_lock); } else { priv = xrwl_hwpriv_to_vifpriv(hw_priv, 0); } return priv; } static inline int get_interface_id_scanning(struct xradio_common *hw_priv) { if (hw_priv->scan.req || hw_priv->scan.direct_probe) return hw_priv->scan.if_id; else return -1; }