Merge ath-next from ath.git

ath.git patches for 4.7. Major changes:

ath10k

* implement push-pull tx model using mac80211 software queuing support
* enable scan in AP mode (NL80211_FEATURE_AP_SCAN)

wil6210

* add basic PBSS (Personal Basic Service Set) support
* add initial P2P support
* add oob_mode module parameter
This commit is contained in:
Kalle Valo 2016-04-07 21:44:37 +03:00
commit 20ac1b325d
70 changed files with 3981 additions and 1794 deletions

View File

@ -5,12 +5,18 @@ Required properties:
* "qcom,ath10k"
* "qcom,ipq4019-wifi"
PCI based devices uses compatible string "qcom,ath10k" and takes only
calibration data via "qcom,ath10k-calibration-data". Rest of the properties
are not applicable for PCI based devices.
PCI based devices uses compatible string "qcom,ath10k" and takes calibration
data along with board specific data via "qcom,ath10k-calibration-data".
Rest of the properties are not applicable for PCI based devices.
AHB based devices (i.e. ipq4019) uses compatible string "qcom,ipq4019-wifi"
and also uses most of the properties defined in this doc.
and also uses most of the properties defined in this doc (except
"qcom,ath10k-calibration-data"). It uses "qcom,ath10k-pre-calibration-data"
to carry pre calibration data.
In general, entry "qcom,ath10k-pre-calibration-data" and
"qcom,ath10k-calibration-data" conflict with each other and only one
can be provided per device.
Optional properties:
- reg: Address and length of the register set for the device.
@ -35,8 +41,11 @@ Optional properties:
- qcom,msi_addr: MSI interrupt address.
- qcom,msi_base: Base value to add before writing MSI data into
MSI address register.
- qcom,ath10k-calibration-data : calibration data as an array, the
length can vary between hw versions
- qcom,ath10k-calibration-data : calibration data + board specific data
as an array, the length can vary between
hw versions.
- qcom,ath10k-pre-calibration-data : pre calibration data as an array,
the length can vary between hw versions.
Example (to supply the calibration data alone):
@ -105,5 +114,5 @@ wifi0: wifi@a000000 {
"legacy";
qcom,msi_addr = <0x0b006040>;
qcom,msi_base = <0x40>;
qcom,ath10k-calibration-data = [ 01 02 03 ... ];
qcom,ath10k-pre-calibration-data = [ 01 02 03 ... ];
};

View File

@ -411,7 +411,8 @@ int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr)
lockdep_assert_held(&ar_pci->ce_lock);
if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) == 0)
if ((pipe->id != 5) &&
CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) == 0)
return -ENOSPC;
desc->addr = __cpu_to_le32(paddr);
@ -425,6 +426,19 @@ int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr)
return 0;
}
void ath10k_ce_rx_update_write_idx(struct ath10k_ce_pipe *pipe, u32 nentries)
{
struct ath10k *ar = pipe->ar;
struct ath10k_ce_ring *dest_ring = pipe->dest_ring;
unsigned int nentries_mask = dest_ring->nentries_mask;
unsigned int write_index = dest_ring->write_index;
u32 ctrl_addr = pipe->ctrl_addr;
write_index = CE_RING_IDX_ADD(nentries_mask, write_index, nentries);
ath10k_ce_dest_ring_write_index_set(ar, ctrl_addr, write_index);
dest_ring->write_index = write_index;
}
int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr)
{
struct ath10k *ar = pipe->ar;
@ -444,14 +458,10 @@ int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr)
*/
int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp,
u32 *bufferp,
unsigned int *nbytesp,
unsigned int *transfer_idp,
unsigned int *flagsp)
unsigned int *nbytesp)
{
struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
unsigned int nentries_mask = dest_ring->nentries_mask;
struct ath10k *ar = ce_state->ar;
unsigned int sw_index = dest_ring->sw_index;
struct ce_desc *base = dest_ring->base_addr_owner_space;
@ -476,21 +486,17 @@ int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
desc->nbytes = 0;
/* Return data from completed destination descriptor */
*bufferp = __le32_to_cpu(sdesc.addr);
*nbytesp = nbytes;
*transfer_idp = MS(__le16_to_cpu(sdesc.flags), CE_DESC_FLAGS_META_DATA);
if (__le16_to_cpu(sdesc.flags) & CE_DESC_FLAGS_BYTE_SWAP)
*flagsp = CE_RECV_FLAG_SWAPPED;
else
*flagsp = 0;
if (per_transfer_contextp)
*per_transfer_contextp =
dest_ring->per_transfer_context[sw_index];
/* sanity */
dest_ring->per_transfer_context[sw_index] = NULL;
/* Copy engine 5 (HTT Rx) will reuse the same transfer context.
* So update transfer context all CEs except CE5.
*/
if (ce_state->id != 5)
dest_ring->per_transfer_context[sw_index] = NULL;
/* Update sw_index */
sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
@ -501,10 +507,7 @@ int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp,
u32 *bufferp,
unsigned int *nbytesp,
unsigned int *transfer_idp,
unsigned int *flagsp)
unsigned int *nbytesp)
{
struct ath10k *ar = ce_state->ar;
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@ -513,8 +516,7 @@ int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state,
spin_lock_bh(&ar_pci->ce_lock);
ret = ath10k_ce_completed_recv_next_nolock(ce_state,
per_transfer_contextp,
bufferp, nbytesp,
transfer_idp, flagsp);
nbytesp);
spin_unlock_bh(&ar_pci->ce_lock);
return ret;

View File

@ -22,7 +22,7 @@
/* Maximum number of Copy Engine's supported */
#define CE_COUNT_MAX 12
#define CE_HTT_H2T_MSG_SRC_NENTRIES 4096
#define CE_HTT_H2T_MSG_SRC_NENTRIES 8192
/* Descriptor rings must be aligned to this boundary */
#define CE_DESC_RING_ALIGN 8
@ -166,6 +166,7 @@ int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe);
int __ath10k_ce_rx_num_free_bufs(struct ath10k_ce_pipe *pipe);
int __ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr);
int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr);
void ath10k_ce_rx_update_write_idx(struct ath10k_ce_pipe *pipe, u32 nentries);
/* recv flags */
/* Data is byte-swapped */
@ -177,10 +178,7 @@ int ath10k_ce_rx_post_buf(struct ath10k_ce_pipe *pipe, void *ctx, u32 paddr);
*/
int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp,
u32 *bufferp,
unsigned int *nbytesp,
unsigned int *transfer_idp,
unsigned int *flagsp);
unsigned int *nbytesp);
/*
* Supply data for the next completed unprocessed send descriptor.
* Pops 1 completed send buffer from Source ring.
@ -212,10 +210,7 @@ int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state,
int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
void **per_transfer_contextp,
u32 *bufferp,
unsigned int *nbytesp,
unsigned int *transfer_idp,
unsigned int *flagsp);
unsigned int *nbytesp);
/*
* Support clean shutdown by allowing the caller to cancel
@ -416,6 +411,8 @@ static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id)
(((int)(toidx)-(int)(fromidx)) & (nentries_mask))
#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask))
#define CE_RING_IDX_ADD(nentries_mask, idx, num) \
(((idx) + (num)) & (nentries_mask))
#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB \
ar->regs->ce_wrap_intr_sum_host_msi_lsb

View File

@ -60,6 +60,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
.hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER,
.cal_data_len = 2116,
.fw = {
.dir = QCA988X_HW_2_0_FW_DIR,
.fw = QCA988X_HW_2_0_FW_FILE,
@ -78,6 +79,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.otp_exe_param = 0,
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
.cal_data_len = 8124,
.fw = {
.dir = QCA6174_HW_2_1_FW_DIR,
.fw = QCA6174_HW_2_1_FW_FILE,
@ -97,6 +99,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
.hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER,
.cal_data_len = 8124,
.fw = {
.dir = QCA6174_HW_2_1_FW_DIR,
.fw = QCA6174_HW_2_1_FW_FILE,
@ -116,6 +119,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
.hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER,
.cal_data_len = 8124,
.fw = {
.dir = QCA6174_HW_3_0_FW_DIR,
.fw = QCA6174_HW_3_0_FW_FILE,
@ -135,6 +139,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
.hw_4addr_pad = ATH10K_HW_4ADDR_PAD_AFTER,
.cal_data_len = 8124,
.fw = {
/* uses same binaries as hw3.0 */
.dir = QCA6174_HW_3_0_FW_DIR,
@ -156,11 +161,10 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.channel_counters_freq_hz = 150000,
.max_probe_resp_desc_thres = 24,
.hw_4addr_pad = ATH10K_HW_4ADDR_PAD_BEFORE,
.num_msdu_desc = 1424,
.qcache_active_peers = 50,
.tx_chain_mask = 0xf,
.rx_chain_mask = 0xf,
.max_spatial_stream = 4,
.cal_data_len = 12064,
.fw = {
.dir = QCA99X0_HW_2_0_FW_DIR,
.fw = QCA99X0_HW_2_0_FW_FILE,
@ -179,6 +183,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.otp_exe_param = 0,
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
.cal_data_len = 8124,
.fw = {
.dir = QCA9377_HW_1_0_FW_DIR,
.fw = QCA9377_HW_1_0_FW_FILE,
@ -197,6 +202,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.otp_exe_param = 0,
.channel_counters_freq_hz = 88000,
.max_probe_resp_desc_thres = 0,
.cal_data_len = 8124,
.fw = {
.dir = QCA9377_HW_1_0_FW_DIR,
.fw = QCA9377_HW_1_0_FW_FILE,
@ -217,11 +223,10 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.channel_counters_freq_hz = 125000,
.max_probe_resp_desc_thres = 24,
.hw_4addr_pad = ATH10K_HW_4ADDR_PAD_BEFORE,
.num_msdu_desc = 2500,
.qcache_active_peers = 35,
.tx_chain_mask = 0x3,
.rx_chain_mask = 0x3,
.max_spatial_stream = 2,
.cal_data_len = 12064,
.fw = {
.dir = QCA4019_HW_1_0_FW_DIR,
.fw = QCA4019_HW_1_0_FW_FILE,
@ -466,18 +471,18 @@ exit:
return ret;
}
static int ath10k_download_cal_file(struct ath10k *ar)
static int ath10k_download_cal_file(struct ath10k *ar,
const struct firmware *file)
{
int ret;
if (!ar->cal_file)
if (!file)
return -ENOENT;
if (IS_ERR(ar->cal_file))
return PTR_ERR(ar->cal_file);
if (IS_ERR(file))
return PTR_ERR(file);
ret = ath10k_download_board_data(ar, ar->cal_file->data,
ar->cal_file->size);
ret = ath10k_download_board_data(ar, file->data, file->size);
if (ret) {
ath10k_err(ar, "failed to download cal_file data: %d\n", ret);
return ret;
@ -488,7 +493,7 @@ static int ath10k_download_cal_file(struct ath10k *ar)
return 0;
}
static int ath10k_download_cal_dt(struct ath10k *ar)
static int ath10k_download_cal_dt(struct ath10k *ar, const char *dt_name)
{
struct device_node *node;
int data_len;
@ -502,13 +507,12 @@ static int ath10k_download_cal_dt(struct ath10k *ar)
*/
return -ENOENT;
if (!of_get_property(node, "qcom,ath10k-calibration-data",
&data_len)) {
if (!of_get_property(node, dt_name, &data_len)) {
/* The calibration data node is optional */
return -ENOENT;
}
if (data_len != QCA988X_CAL_DATA_LEN) {
if (data_len != ar->hw_params.cal_data_len) {
ath10k_warn(ar, "invalid calibration data length in DT: %d\n",
data_len);
ret = -EMSGSIZE;
@ -521,8 +525,7 @@ static int ath10k_download_cal_dt(struct ath10k *ar)
goto out;
}
ret = of_property_read_u8_array(node, "qcom,ath10k-calibration-data",
data, data_len);
ret = of_property_read_u8_array(node, dt_name, data, data_len);
if (ret) {
ath10k_warn(ar, "failed to read calibration data from DT: %d\n",
ret);
@ -726,6 +729,14 @@ static int ath10k_fetch_cal_file(struct ath10k *ar)
{
char filename[100];
/* pre-cal-<bus>-<id>.bin */
scnprintf(filename, sizeof(filename), "pre-cal-%s-%s.bin",
ath10k_bus_str(ar->hif.bus), dev_name(ar->dev));
ar->pre_cal_file = ath10k_fetch_fw_file(ar, ATH10K_FW_DIR, filename);
if (!IS_ERR(ar->pre_cal_file))
goto success;
/* cal-<bus>-<id>.bin */
scnprintf(filename, sizeof(filename), "cal-%s-%s.bin",
ath10k_bus_str(ar->hif.bus), dev_name(ar->dev));
@ -734,7 +745,7 @@ static int ath10k_fetch_cal_file(struct ath10k *ar)
if (IS_ERR(ar->cal_file))
/* calibration file is optional, don't print any warnings */
return PTR_ERR(ar->cal_file);
success:
ath10k_dbg(ar, ATH10K_DBG_BOOT, "found calibration file %s/%s\n",
ATH10K_FW_DIR, filename);
@ -1258,11 +1269,77 @@ success:
return 0;
}
static int ath10k_core_pre_cal_download(struct ath10k *ar)
{
int ret;
ret = ath10k_download_cal_file(ar, ar->pre_cal_file);
if (ret == 0) {
ar->cal_mode = ATH10K_PRE_CAL_MODE_FILE;
goto success;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT,
"boot did not find a pre calibration file, try DT next: %d\n",
ret);
ret = ath10k_download_cal_dt(ar, "qcom,ath10k-pre-calibration-data");
if (ret) {
ath10k_dbg(ar, ATH10K_DBG_BOOT,
"unable to load pre cal data from DT: %d\n", ret);
return ret;
}
ar->cal_mode = ATH10K_PRE_CAL_MODE_DT;
success:
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using calibration mode %s\n",
ath10k_cal_mode_str(ar->cal_mode));
return 0;
}
static int ath10k_core_pre_cal_config(struct ath10k *ar)
{
int ret;
ret = ath10k_core_pre_cal_download(ar);
if (ret) {
ath10k_dbg(ar, ATH10K_DBG_BOOT,
"failed to load pre cal data: %d\n", ret);
return ret;
}
ret = ath10k_core_get_board_id_from_otp(ar);
if (ret) {
ath10k_err(ar, "failed to get board id: %d\n", ret);
return ret;
}
ret = ath10k_download_and_run_otp(ar);
if (ret) {
ath10k_err(ar, "failed to run otp: %d\n", ret);
return ret;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT,
"pre cal configuration done successfully\n");
return 0;
}
static int ath10k_download_cal_data(struct ath10k *ar)
{
int ret;
ret = ath10k_download_cal_file(ar);
ret = ath10k_core_pre_cal_config(ar);
if (ret == 0)
return 0;
ath10k_dbg(ar, ATH10K_DBG_BOOT,
"pre cal download procedure failed, try cal file: %d\n",
ret);
ret = ath10k_download_cal_file(ar, ar->cal_file);
if (ret == 0) {
ar->cal_mode = ATH10K_CAL_MODE_FILE;
goto done;
@ -1272,7 +1349,7 @@ static int ath10k_download_cal_data(struct ath10k *ar)
"boot did not find a calibration file, try DT next: %d\n",
ret);
ret = ath10k_download_cal_dt(ar);
ret = ath10k_download_cal_dt(ar, "qcom,ath10k-calibration-data");
if (ret == 0) {
ar->cal_mode = ATH10K_CAL_MODE_DT;
goto done;
@ -1509,7 +1586,7 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar)
case ATH10K_FW_WMI_OP_VERSION_10_1:
case ATH10K_FW_WMI_OP_VERSION_10_2:
case ATH10K_FW_WMI_OP_VERSION_10_2_4:
if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) {
if (ath10k_peer_stats_enabled(ar)) {
ar->max_num_peers = TARGET_10X_TX_STATS_NUM_PEERS;
ar->max_num_stations = TARGET_10X_TX_STATS_NUM_STATIONS;
} else {
@ -1538,9 +1615,15 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar)
ar->num_active_peers = TARGET_10_4_ACTIVE_PEERS;
ar->max_num_vdevs = TARGET_10_4_NUM_VDEVS;
ar->num_tids = TARGET_10_4_TGT_NUM_TIDS;
ar->htt.max_num_pending_tx = ar->hw_params.num_msdu_desc;
ar->fw_stats_req_mask = WMI_STAT_PEER;
ar->fw_stats_req_mask = WMI_10_4_STAT_PEER |
WMI_10_4_STAT_PEER_EXTD;
ar->max_spatial_stream = ar->hw_params.max_spatial_stream;
if (test_bit(ATH10K_FW_FEATURE_PEER_FLOW_CONTROL,
ar->fw_features))
ar->htt.max_num_pending_tx = TARGET_10_4_NUM_MSDU_DESC_PFC;
else
ar->htt.max_num_pending_tx = TARGET_10_4_NUM_MSDU_DESC;
break;
case ATH10K_FW_WMI_OP_VERSION_UNSET:
case ATH10K_FW_WMI_OP_VERSION_MAX:
@ -1578,6 +1661,7 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar)
int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
{
int status;
u32 val;
lockdep_assert_held(&ar->conf_mutex);
@ -1698,6 +1782,21 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "firmware %s booted\n",
ar->hw->wiphy->fw_version);
if (test_bit(WMI_SERVICE_EXT_RES_CFG_SUPPORT, ar->wmi.svc_map)) {
val = 0;
if (ath10k_peer_stats_enabled(ar))
val = WMI_10_4_PEER_STATS;
status = ath10k_wmi_ext_resource_config(ar,
WMI_HOST_PLATFORM_HIGH_PERF, val);
if (status) {
ath10k_err(ar,
"failed to send ext resource cfg command : %d\n",
status);
goto err_hif_stop;
}
}
status = ath10k_wmi_cmd_init(ar);
if (status) {
ath10k_err(ar, "could not send WMI init command (%d)\n",
@ -1834,11 +1933,20 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
ath10k_debug_print_hwfw_info(ar);
ret = ath10k_core_pre_cal_download(ar);
if (ret) {
/* pre calibration data download is not necessary
* for all the chipsets. Ignore failures and continue.
*/
ath10k_dbg(ar, ATH10K_DBG_BOOT,
"could not load pre cal data: %d\n", ret);
}
ret = ath10k_core_get_board_id_from_otp(ar);
if (ret && ret != -EOPNOTSUPP) {
ath10k_err(ar, "failed to get board id from otp: %d\n",
ret);
return ret;
goto err_free_firmware_files;
}
ret = ath10k_core_fetch_board_file(ar);
@ -2048,7 +2156,9 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
mutex_init(&ar->conf_mutex);
spin_lock_init(&ar->data_lock);
spin_lock_init(&ar->txqs_lock);
INIT_LIST_HEAD(&ar->txqs);
INIT_LIST_HEAD(&ar->peers);
init_waitqueue_head(&ar->peer_mapping_wq);
init_waitqueue_head(&ar->htt.empty_tx_wq);

View File

@ -98,6 +98,7 @@ struct ath10k_skb_cb {
u8 eid;
u16 msdu_id;
struct ieee80211_vif *vif;
struct ieee80211_txq *txq;
} __packed;
struct ath10k_skb_rxcb {
@ -297,6 +298,9 @@ struct ath10k_dfs_stats {
struct ath10k_peer {
struct list_head list;
struct ieee80211_vif *vif;
struct ieee80211_sta *sta;
int vdev_id;
u8 addr[ETH_ALEN];
DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
@ -305,6 +309,12 @@ struct ath10k_peer {
struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
};
struct ath10k_txq {
struct list_head list;
unsigned long num_fw_queued;
unsigned long num_push_allowed;
};
struct ath10k_sta {
struct ath10k_vif *arvif;
@ -313,6 +323,7 @@ struct ath10k_sta {
u32 bw;
u32 nss;
u32 smps;
u16 peer_id;
struct work_struct update_wk;
@ -335,6 +346,7 @@ struct ath10k_vif {
struct list_head list;
u32 vdev_id;
u16 peer_id;
enum wmi_vdev_type vdev_type;
enum wmi_vdev_subtype vdev_subtype;
u32 beacon_interval;
@ -549,12 +561,17 @@ enum ath10k_dev_flags {
/* Bluetooth coexistance enabled */
ATH10K_FLAG_BTCOEX,
/* Per Station statistics service */
ATH10K_FLAG_PEER_STATS,
};
enum ath10k_cal_mode {
ATH10K_CAL_MODE_FILE,
ATH10K_CAL_MODE_OTP,
ATH10K_CAL_MODE_DT,
ATH10K_PRE_CAL_MODE_FILE,
ATH10K_PRE_CAL_MODE_DT,
};
enum ath10k_crypt_mode {
@ -573,6 +590,10 @@ static inline const char *ath10k_cal_mode_str(enum ath10k_cal_mode mode)
return "otp";
case ATH10K_CAL_MODE_DT:
return "dt";
case ATH10K_PRE_CAL_MODE_FILE:
return "pre-cal-file";
case ATH10K_PRE_CAL_MODE_DT:
return "pre-cal-dt";
}
return "unknown";
@ -680,11 +701,10 @@ struct ath10k {
/* The padding bytes's location is different on various chips */
enum ath10k_hw_4addr_pad hw_4addr_pad;
u32 num_msdu_desc;
u32 qcache_active_peers;
u32 tx_chain_mask;
u32 rx_chain_mask;
u32 max_spatial_stream;
u32 cal_data_len;
struct ath10k_hw_params_fw {
const char *dir;
@ -708,6 +728,7 @@ struct ath10k {
const void *firmware_data;
size_t firmware_len;
const struct firmware *pre_cal_file;
const struct firmware *cal_file;
struct {
@ -756,6 +777,9 @@ struct ath10k {
/* current operating channel definition */
struct cfg80211_chan_def chandef;
/* currently configured operating channel in firmware */
struct ieee80211_channel *tgt_oper_chan;
unsigned long long free_vdev_map;
struct ath10k_vif *monitor_arvif;
bool monitor;
@ -786,9 +810,13 @@ struct ath10k {
/* protects shared structure data */
spinlock_t data_lock;
/* protects: ar->txqs, artxq->list */
spinlock_t txqs_lock;
struct list_head txqs;
struct list_head arvifs;
struct list_head peers;
struct ath10k_peer *peer_map[ATH10K_MAX_NUM_PEER_IDS];
wait_queue_head_t peer_mapping_wq;
/* protected by conf_mutex */
@ -876,6 +904,15 @@ struct ath10k {
u8 drv_priv[0] __aligned(sizeof(void *));
};
static inline bool ath10k_peer_stats_enabled(struct ath10k *ar)
{
if (test_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags) &&
test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map))
return true;
return false;
}
struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
enum ath10k_bus bus,
enum ath10k_hw_rev hw_rev,

View File

@ -127,6 +127,7 @@ EXPORT_SYMBOL(ath10k_info);
void ath10k_debug_print_hwfw_info(struct ath10k *ar)
{
char fw_features[128] = {};
u32 crc = 0;
ath10k_core_get_fw_features_str(ar, fw_features, sizeof(fw_features));
@ -143,11 +144,14 @@ void ath10k_debug_print_hwfw_info(struct ath10k *ar)
config_enabled(CONFIG_ATH10K_DFS_CERTIFIED),
config_enabled(CONFIG_NL80211_TESTMODE));
if (ar->firmware)
crc = crc32_le(0, ar->firmware->data, ar->firmware->size);
ath10k_info(ar, "firmware ver %s api %d features %s crc32 %08x\n",
ar->hw->wiphy->fw_version,
ar->fw_api,
fw_features,
crc32_le(0, ar->firmware->data, ar->firmware->size));
crc);
}
void ath10k_debug_print_board_info(struct ath10k *ar)
@ -319,7 +323,7 @@ static void ath10k_debug_fw_stats_reset(struct ath10k *ar)
void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
{
struct ath10k_fw_stats stats = {};
bool is_start, is_started, is_end, peer_stats_svc;
bool is_start, is_started, is_end;
size_t num_peers;
size_t num_vdevs;
int ret;
@ -346,13 +350,11 @@ void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
* b) consume stat update events until another one with pdev stats is
* delivered which is treated as end-of-data and is itself discarded
*/
peer_stats_svc = test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map);
if (peer_stats_svc)
if (ath10k_peer_stats_enabled(ar))
ath10k_sta_update_rx_duration(ar, &stats.peers);
if (ar->debug.fw_stats_done) {
if (!peer_stats_svc)
if (!ath10k_peer_stats_enabled(ar))
ath10k_warn(ar, "received unsolicited stats update event\n");
goto free;
@ -1447,7 +1449,7 @@ static int ath10k_debug_cal_data_open(struct inode *inode, struct file *file)
goto err;
}
buf = vmalloc(QCA988X_CAL_DATA_LEN);
buf = vmalloc(ar->hw_params.cal_data_len);
if (!buf) {
ret = -ENOMEM;
goto err;
@ -1462,7 +1464,7 @@ static int ath10k_debug_cal_data_open(struct inode *inode, struct file *file)
}
ret = ath10k_hif_diag_read(ar, le32_to_cpu(addr), buf,
QCA988X_CAL_DATA_LEN);
ar->hw_params.cal_data_len);
if (ret) {
ath10k_warn(ar, "failed to read calibration data: %d\n", ret);
goto err_vfree;
@ -1487,10 +1489,11 @@ static ssize_t ath10k_debug_cal_data_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
void *buf = file->private_data;
return simple_read_from_buffer(user_buf, count, ppos,
buf, QCA988X_CAL_DATA_LEN);
buf, ar->hw_params.cal_data_len);
}
static int ath10k_debug_cal_data_release(struct inode *inode,
@ -2019,7 +2022,12 @@ static ssize_t ath10k_write_pktlog_filter(struct file *file,
goto out;
}
if (filter && (filter != ar->debug.pktlog_filter)) {
if (filter == ar->debug.pktlog_filter) {
ret = count;
goto out;
}
if (filter) {
ret = ath10k_wmi_pdev_pktlog_enable(ar, filter);
if (ret) {
ath10k_warn(ar, "failed to enable pktlog filter %x: %d\n",
@ -2174,6 +2182,73 @@ static const struct file_operations fops_btcoex = {
.open = simple_open
};
static ssize_t ath10k_write_peer_stats(struct file *file,
const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct ath10k *ar = file->private_data;
char buf[32];
size_t buf_size;
int ret = 0;
bool val;
buf_size = min(count, (sizeof(buf) - 1));
if (copy_from_user(buf, ubuf, buf_size))
return -EFAULT;
buf[buf_size] = '\0';
if (strtobool(buf, &val) != 0)
return -EINVAL;
mutex_lock(&ar->conf_mutex);
if (ar->state != ATH10K_STATE_ON &&
ar->state != ATH10K_STATE_RESTARTED) {
ret = -ENETDOWN;
goto exit;
}
if (!(test_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags) ^ val))
goto exit;
if (val)
set_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags);
else
clear_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags);
ath10k_info(ar, "restarting firmware due to Peer stats change");
queue_work(ar->workqueue, &ar->restart_work);
ret = count;
exit:
mutex_unlock(&ar->conf_mutex);
return ret;
}
static ssize_t ath10k_read_peer_stats(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
char buf[32];
struct ath10k *ar = file->private_data;
int len = 0;
mutex_lock(&ar->conf_mutex);
len = scnprintf(buf, sizeof(buf) - len, "%d\n",
test_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags));
mutex_unlock(&ar->conf_mutex);
return simple_read_from_buffer(ubuf, count, ppos, buf, len);
}
static const struct file_operations fops_peer_stats = {
.read = ath10k_read_peer_stats,
.write = ath10k_write_peer_stats,
.open = simple_open
};
static ssize_t ath10k_debug_fw_checksums_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
@ -2337,6 +2412,11 @@ int ath10k_debug_register(struct ath10k *ar)
debugfs_create_file("btcoex", S_IRUGO | S_IWUSR,
ar->debug.debugfs_phy, ar, &fops_btcoex);
if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map))
debugfs_create_file("peer_stats", S_IRUGO | S_IWUSR,
ar->debug.debugfs_phy, ar,
&fops_peer_stats);
debugfs_create_file("fw_checksums", S_IRUSR,
ar->debug.debugfs_phy, ar, &fops_fw_checksums);

View File

@ -149,7 +149,7 @@ int ath10k_htt_connect(struct ath10k_htt *htt)
memset(&conn_resp, 0, sizeof(conn_resp));
conn_req.ep_ops.ep_tx_complete = ath10k_htt_htc_tx_complete;
conn_req.ep_ops.ep_rx_complete = ath10k_htt_t2h_msg_handler;
conn_req.ep_ops.ep_rx_complete = ath10k_htt_htc_t2h_msg_handler;
/* connect to control service */
conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG;

View File

@ -22,6 +22,7 @@
#include <linux/interrupt.h>
#include <linux/dmapool.h>
#include <linux/hashtable.h>
#include <linux/kfifo.h>
#include <net/mac80211.h>
#include "htc.h"
@ -1461,6 +1462,14 @@ struct htt_tx_mode_switch_ind {
struct htt_tx_mode_switch_record records[0];
} __packed;
struct htt_channel_change {
u8 pad[3];
__le32 freq;
__le32 center_freq1;
__le32 center_freq2;
__le32 phymode;
} __packed;
union htt_rx_pn_t {
/* WEP: 24-bit PN */
u32 pn24;
@ -1511,16 +1520,22 @@ struct htt_resp {
struct htt_tx_fetch_ind tx_fetch_ind;
struct htt_tx_fetch_confirm tx_fetch_confirm;
struct htt_tx_mode_switch_ind tx_mode_switch_ind;
struct htt_channel_change chan_change;
};
} __packed;
/*** host side structures follow ***/
struct htt_tx_done {
u32 msdu_id;
bool discard;
bool no_ack;
bool success;
u16 msdu_id;
u16 status;
};
enum htt_tx_compl_state {
HTT_TX_COMPL_STATE_NONE,
HTT_TX_COMPL_STATE_ACK,
HTT_TX_COMPL_STATE_NOACK,
HTT_TX_COMPL_STATE_DISCARD,
};
struct htt_peer_map_event {
@ -1641,17 +1656,20 @@ struct ath10k_htt {
struct idr pending_tx;
wait_queue_head_t empty_tx_wq;
/* FIFO for storing tx done status {ack, no-ack, discard} and msdu id */
DECLARE_KFIFO_PTR(txdone_fifo, struct htt_tx_done);
/* set if host-fw communication goes haywire
* used to avoid further failures */
bool rx_confused;
struct tasklet_struct rx_replenish_task;
atomic_t num_mpdus_ready;
/* This is used to group tx/rx completions separately and process them
* in batches to reduce cache stalls */
struct tasklet_struct txrx_compl_task;
struct sk_buff_head tx_compl_q;
struct sk_buff_head rx_compl_q;
struct sk_buff_head rx_in_ord_compl_q;
struct sk_buff_head tx_fetch_ind_q;
/* rx_status template */
struct ieee80211_rx_status rx_status;
@ -1667,10 +1685,13 @@ struct ath10k_htt {
} txbuf;
struct {
bool enabled;
struct htt_q_state *vaddr;
dma_addr_t paddr;
u16 num_push_allowed;
u16 num_peers;
u16 num_tids;
enum htt_tx_mode_switch_mode mode;
enum htt_q_depth_type type;
} tx_q_state;
};
@ -1715,7 +1736,7 @@ struct htt_rx_desc {
/* Refill a bunch of RX buffers for each refill round so that FW/HW can handle
* aggregated traffic more nicely. */
#define ATH10K_HTT_MAX_NUM_REFILL 16
#define ATH10K_HTT_MAX_NUM_REFILL 100
/*
* DMA_MAP expects the buffer to be an integral number of cache lines.
@ -1743,7 +1764,8 @@ int ath10k_htt_rx_ring_refill(struct ath10k *ar);
void ath10k_htt_rx_free(struct ath10k_htt *htt);
void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb);
void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb);
void ath10k_htt_htc_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb);
bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb);
int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt);
int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie);
int ath10k_htt_send_frag_desc_bank_cfg(struct ath10k_htt *htt);
@ -1752,8 +1774,23 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
u8 max_subfrms_ampdu,
u8 max_subfrms_amsdu);
void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb);
int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
__le32 token,
__le16 fetch_seq_num,
struct htt_tx_fetch_record *records,
size_t num_records);
void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
void ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
void ath10k_htt_tx_txq_sync(struct ath10k *ar);
void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt);
void ath10k_htt_tx_mgmt_dec_pending(struct ath10k_htt *htt);
int ath10k_htt_tx_mgmt_inc_pending(struct ath10k_htt *htt, bool is_mgmt,
bool is_presp);
void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc);
int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb);
void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);

View File

@ -31,6 +31,8 @@
/* when under memory pressure rx ring refill may fail and needs a retry */
#define HTT_RX_RING_REFILL_RETRY_MS 50
#define HTT_RX_RING_REFILL_RESCHED_MS 5
static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
static void ath10k_htt_txrx_compl_task(unsigned long ptr);
@ -192,7 +194,8 @@ static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt)
mod_timer(&htt->rx_ring.refill_retry_timer, jiffies +
msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS));
} else if (num_deficit > 0) {
tasklet_schedule(&htt->rx_replenish_task);
mod_timer(&htt->rx_ring.refill_retry_timer, jiffies +
msecs_to_jiffies(HTT_RX_RING_REFILL_RESCHED_MS));
}
spin_unlock_bh(&htt->rx_ring.lock);
}
@ -223,12 +226,11 @@ int ath10k_htt_rx_ring_refill(struct ath10k *ar)
void ath10k_htt_rx_free(struct ath10k_htt *htt)
{
del_timer_sync(&htt->rx_ring.refill_retry_timer);
tasklet_kill(&htt->rx_replenish_task);
tasklet_kill(&htt->txrx_compl_task);
skb_queue_purge(&htt->tx_compl_q);
skb_queue_purge(&htt->rx_compl_q);
skb_queue_purge(&htt->rx_in_ord_compl_q);
skb_queue_purge(&htt->tx_fetch_ind_q);
ath10k_htt_rx_ring_free(htt);
@ -281,7 +283,6 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt)
/* return: < 0 fatal error, 0 - non chained msdu, 1 chained msdu */
static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
u8 **fw_desc, int *fw_desc_len,
struct sk_buff_head *amsdu)
{
struct ath10k *ar = htt->ar;
@ -323,48 +324,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
return -EIO;
}
/*
* Copy the FW rx descriptor for this MSDU from the rx
* indication message into the MSDU's netbuf. HL uses the
* same rx indication message definition as LL, and simply
* appends new info (fields from the HW rx desc, and the
* MSDU payload itself). So, the offset into the rx
* indication message only has to account for the standard
* offset of the per-MSDU FW rx desc info within the
* message, and how many bytes of the per-MSDU FW rx desc
* info have already been consumed. (And the endianness of
* the host, since for a big-endian host, the rx ind
* message contents, including the per-MSDU rx desc bytes,
* were byteswapped during upload.)
*/
if (*fw_desc_len > 0) {
rx_desc->fw_desc.info0 = **fw_desc;
/*
* The target is expected to only provide the basic
* per-MSDU rx descriptors. Just to be sure, verify
* that the target has not attached extension data
* (e.g. LRO flow ID).
*/
/* or more, if there's extension data */
(*fw_desc)++;
(*fw_desc_len)--;
} else {
/*
* When an oversized AMSDU happened, FW will lost
* some of MSDU status - in this case, the FW
* descriptors provided will be less than the
* actual MSDUs inside this MPDU. Mark the FW
* descriptors so that it will still deliver to
* upper stack, if no CRC error for this MPDU.
*
* FIX THIS - the FW descriptors are actually for
* MSDUs in the end of this A-MSDU instead of the
* beginning.
*/
rx_desc->fw_desc.info0 = 0;
}
msdu_len_invalid = !!(__le32_to_cpu(rx_desc->attention.flags)
& (RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR |
RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR));
@ -423,13 +382,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
return msdu_chaining;
}
static void ath10k_htt_rx_replenish_task(unsigned long ptr)
{
struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
ath10k_htt_rx_msdu_buff_replenish(htt);
}
static struct sk_buff *ath10k_htt_rx_pop_paddr(struct ath10k_htt *htt,
u32 paddr)
{
@ -563,12 +515,10 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
htt->rx_ring.sw_rd_idx.msdu_payld = 0;
hash_init(htt->rx_ring.skb_table);
tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task,
(unsigned long)htt);
skb_queue_head_init(&htt->tx_compl_q);
skb_queue_head_init(&htt->rx_compl_q);
skb_queue_head_init(&htt->rx_in_ord_compl_q);
skb_queue_head_init(&htt->tx_fetch_ind_q);
atomic_set(&htt->num_mpdus_ready, 0);
tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task,
(unsigned long)htt);
@ -860,6 +810,8 @@ static bool ath10k_htt_rx_h_channel(struct ath10k *ar,
ch = ath10k_htt_rx_h_vdev_channel(ar, vdev_id);
if (!ch)
ch = ath10k_htt_rx_h_any_channel(ar);
if (!ch)
ch = ar->tgt_oper_chan;
spin_unlock_bh(&ar->data_lock);
if (!ch)
@ -1076,20 +1028,25 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
hdr = (void *)msdu->data;
/* Tail */
skb_trim(msdu, msdu->len - ath10k_htt_rx_crypto_tail_len(ar, enctype));
if (status->flag & RX_FLAG_IV_STRIPPED)
skb_trim(msdu, msdu->len -
ath10k_htt_rx_crypto_tail_len(ar, enctype));
/* MMIC */
if (!ieee80211_has_morefrags(hdr->frame_control) &&
if ((status->flag & RX_FLAG_MMIC_STRIPPED) &&
!ieee80211_has_morefrags(hdr->frame_control) &&
enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
skb_trim(msdu, msdu->len - 8);
/* Head */
hdr_len = ieee80211_hdrlen(hdr->frame_control);
crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
if (status->flag & RX_FLAG_IV_STRIPPED) {
hdr_len = ieee80211_hdrlen(hdr->frame_control);
crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
memmove((void *)msdu->data + crypto_len,
(void *)msdu->data, hdr_len);
skb_pull(msdu, crypto_len);
memmove((void *)msdu->data + crypto_len,
(void *)msdu->data, hdr_len);
skb_pull(msdu, crypto_len);
}
}
static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
@ -1343,6 +1300,7 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
bool has_tkip_err;
bool has_peer_idx_invalid;
bool is_decrypted;
bool is_mgmt;
u32 attention;
if (skb_queue_empty(amsdu))
@ -1351,6 +1309,9 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
first = skb_peek(amsdu);
rxd = (void *)first->data - sizeof(*rxd);
is_mgmt = !!(rxd->attention.flags &
__cpu_to_le32(RX_ATTENTION_FLAGS_MGMT_TYPE));
enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
RX_MPDU_START_INFO0_ENCRYPT_TYPE);
@ -1392,6 +1353,7 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
RX_FLAG_MMIC_ERROR |
RX_FLAG_DECRYPTED |
RX_FLAG_IV_STRIPPED |
RX_FLAG_ONLY_MONITOR |
RX_FLAG_MMIC_STRIPPED);
if (has_fcs_err)
@ -1400,10 +1362,21 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
if (has_tkip_err)
status->flag |= RX_FLAG_MMIC_ERROR;
if (is_decrypted)
status->flag |= RX_FLAG_DECRYPTED |
RX_FLAG_IV_STRIPPED |
RX_FLAG_MMIC_STRIPPED;
/* Firmware reports all necessary management frames via WMI already.
* They are not reported to monitor interfaces at all so pass the ones
* coming via HTT to monitor interfaces instead. This simplifies
* matters a lot.
*/
if (is_mgmt)
status->flag |= RX_FLAG_ONLY_MONITOR;
if (is_decrypted) {
status->flag |= RX_FLAG_DECRYPTED;
if (likely(!is_mgmt))
status->flag |= RX_FLAG_IV_STRIPPED |
RX_FLAG_MMIC_STRIPPED;
}
skb_queue_walk(amsdu, msdu) {
ath10k_htt_rx_h_csum_offload(msdu);
@ -1416,6 +1389,8 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
*/
if (!is_decrypted)
continue;
if (is_mgmt)
continue;
hdr = (void *)msdu->data;
hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
@ -1516,14 +1491,6 @@ static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
struct sk_buff_head *amsdu,
struct ieee80211_rx_status *rx_status)
{
struct sk_buff *msdu;
struct htt_rx_desc *rxd;
bool is_mgmt;
bool has_fcs_err;
msdu = skb_peek(amsdu);
rxd = (void *)msdu->data - sizeof(*rxd);
/* FIXME: It might be a good idea to do some fuzzy-testing to drop
* invalid/dangerous frames.
*/
@ -1533,23 +1500,6 @@ static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
return false;
}
is_mgmt = !!(rxd->attention.flags &
__cpu_to_le32(RX_ATTENTION_FLAGS_MGMT_TYPE));
has_fcs_err = !!(rxd->attention.flags &
__cpu_to_le32(RX_ATTENTION_FLAGS_FCS_ERR));
/* Management frames are handled via WMI events. The pros of such
* approach is that channel is explicitly provided in WMI events
* whereas HTT doesn't provide channel information for Rxed frames.
*
* However some firmware revisions don't report corrupted frames via
* WMI so don't drop them.
*/
if (is_mgmt && !has_fcs_err) {
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
return false;
}
if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx cac running\n");
return false;
@ -1571,25 +1521,49 @@ static void ath10k_htt_rx_h_filter(struct ath10k *ar,
__skb_queue_purge(amsdu);
}
static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
struct htt_rx_indication *rx)
static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
{
struct ath10k *ar = htt->ar;
struct ieee80211_rx_status *rx_status = &htt->rx_status;
struct htt_rx_indication_mpdu_range *mpdu_ranges;
static struct ieee80211_rx_status rx_status;
struct sk_buff_head amsdu;
int ret;
__skb_queue_head_init(&amsdu);
spin_lock_bh(&htt->rx_ring.lock);
if (htt->rx_confused) {
spin_unlock_bh(&htt->rx_ring.lock);
return -EIO;
}
ret = ath10k_htt_rx_amsdu_pop(htt, &amsdu);
spin_unlock_bh(&htt->rx_ring.lock);
if (ret < 0) {
ath10k_warn(ar, "rx ring became corrupted: %d\n", ret);
__skb_queue_purge(&amsdu);
/* FIXME: It's probably a good idea to reboot the
* device instead of leaving it inoperable.
*/
htt->rx_confused = true;
return ret;
}
ath10k_htt_rx_h_ppdu(ar, &amsdu, &rx_status, 0xffff);
ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0);
ath10k_htt_rx_h_filter(ar, &amsdu, &rx_status);
ath10k_htt_rx_h_mpdu(ar, &amsdu, &rx_status);
ath10k_htt_rx_h_deliver(ar, &amsdu, &rx_status);
return 0;
}
static void ath10k_htt_rx_proc_rx_ind(struct ath10k_htt *htt,
struct htt_rx_indication *rx)
{
struct ath10k *ar = htt->ar;
struct htt_rx_indication_mpdu_range *mpdu_ranges;
int num_mpdu_ranges;
int fw_desc_len;
u8 *fw_desc;
int i, ret, mpdu_count = 0;
lockdep_assert_held(&htt->rx_ring.lock);
if (htt->rx_confused)
return;
fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes);
fw_desc = (u8 *)&rx->fw_desc;
int i, mpdu_count = 0;
num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1),
HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
@ -1603,80 +1577,19 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
for (i = 0; i < num_mpdu_ranges; i++)
mpdu_count += mpdu_ranges[i].mpdu_count;
while (mpdu_count--) {
__skb_queue_head_init(&amsdu);
ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc,
&fw_desc_len, &amsdu);
if (ret < 0) {
ath10k_warn(ar, "rx ring became corrupted: %d\n", ret);
__skb_queue_purge(&amsdu);
/* FIXME: It's probably a good idea to reboot the
* device instead of leaving it inoperable.
*/
htt->rx_confused = true;
break;
}
atomic_add(mpdu_count, &htt->num_mpdus_ready);
ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status, 0xffff);
ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0);
ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
}
tasklet_schedule(&htt->rx_replenish_task);
tasklet_schedule(&htt->txrx_compl_task);
}
static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
struct htt_rx_fragment_indication *frag)
static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt)
{
struct ath10k *ar = htt->ar;
struct ieee80211_rx_status *rx_status = &htt->rx_status;
struct sk_buff_head amsdu;
int ret;
u8 *fw_desc;
int fw_desc_len;
atomic_inc(&htt->num_mpdus_ready);
fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes);
fw_desc = (u8 *)frag->fw_msdu_rx_desc;
__skb_queue_head_init(&amsdu);
spin_lock_bh(&htt->rx_ring.lock);
ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
&amsdu);
spin_unlock_bh(&htt->rx_ring.lock);
tasklet_schedule(&htt->rx_replenish_task);
ath10k_dbg(ar, ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n");
if (ret) {
ath10k_warn(ar, "failed to pop amsdu from httr rx ring for fragmented rx %d\n",
ret);
__skb_queue_purge(&amsdu);
return;
}
if (skb_queue_len(&amsdu) != 1) {
ath10k_warn(ar, "failed to pop frag amsdu: too many msdus\n");
__skb_queue_purge(&amsdu);
return;
}
ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status, 0xffff);
ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
if (fw_desc_len > 0) {
ath10k_dbg(ar, ATH10K_DBG_HTT,
"expecting more fragmented rx in one indication %d\n",
fw_desc_len);
}
tasklet_schedule(&htt->txrx_compl_task);
}
static void ath10k_htt_rx_frm_tx_compl(struct ath10k *ar,
static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar,
struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
@ -1688,19 +1601,19 @@ static void ath10k_htt_rx_frm_tx_compl(struct ath10k *ar,
switch (status) {
case HTT_DATA_TX_STATUS_NO_ACK:
tx_done.no_ack = true;
tx_done.status = HTT_TX_COMPL_STATE_NOACK;
break;
case HTT_DATA_TX_STATUS_OK:
tx_done.success = true;
tx_done.status = HTT_TX_COMPL_STATE_ACK;
break;
case HTT_DATA_TX_STATUS_DISCARD:
case HTT_DATA_TX_STATUS_POSTPONE:
case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL:
tx_done.discard = true;
tx_done.status = HTT_TX_COMPL_STATE_DISCARD;
break;
default:
ath10k_warn(ar, "unhandled tx completion status %d\n", status);
tx_done.discard = true;
tx_done.status = HTT_TX_COMPL_STATE_DISCARD;
break;
}
@ -1710,7 +1623,20 @@ static void ath10k_htt_rx_frm_tx_compl(struct ath10k *ar,
for (i = 0; i < resp->data_tx_completion.num_msdus; i++) {
msdu_id = resp->data_tx_completion.msdus[i];
tx_done.msdu_id = __le16_to_cpu(msdu_id);
ath10k_txrx_tx_unref(htt, &tx_done);
/* kfifo_put: In practice firmware shouldn't fire off per-CE
* interrupt and main interrupt (MSI/-X range case) for the same
* HTC service so it should be safe to use kfifo_put w/o lock.
*
* From kfifo_put() documentation:
* Note that with only one concurrent reader and one concurrent
* writer, you don't need extra locking to use these macro.
*/
if (!kfifo_put(&htt->txdone_fifo, tx_done)) {
ath10k_warn(ar, "txdone fifo overrun, msdu_id %d status %d\n",
tx_done.msdu_id, tx_done.status);
ath10k_txrx_tx_unref(htt, &tx_done);
}
}
}
@ -1978,11 +1904,324 @@ static void ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
return;
}
}
tasklet_schedule(&htt->rx_replenish_task);
ath10k_htt_rx_msdu_buff_replenish(htt);
}
void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
static void ath10k_htt_rx_tx_fetch_resp_id_confirm(struct ath10k *ar,
const __le32 *resp_ids,
int num_resp_ids)
{
int i;
u32 resp_id;
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm num_resp_ids %d\n",
num_resp_ids);
for (i = 0; i < num_resp_ids; i++) {
resp_id = le32_to_cpu(resp_ids[i]);
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm resp_id %u\n",
resp_id);
/* TODO: free resp_id */
}
}
static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
{
struct ieee80211_hw *hw = ar->hw;
struct ieee80211_txq *txq;
struct htt_resp *resp = (struct htt_resp *)skb->data;
struct htt_tx_fetch_record *record;
size_t len;
size_t max_num_bytes;
size_t max_num_msdus;
size_t num_bytes;
size_t num_msdus;
const __le32 *resp_ids;
u16 num_records;
u16 num_resp_ids;
u16 peer_id;
u8 tid;
int ret;
int i;
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind\n");
len = sizeof(resp->hdr) + sizeof(resp->tx_fetch_ind);
if (unlikely(skb->len < len)) {
ath10k_warn(ar, "received corrupted tx_fetch_ind event: buffer too short\n");
return;
}
num_records = le16_to_cpu(resp->tx_fetch_ind.num_records);
num_resp_ids = le16_to_cpu(resp->tx_fetch_ind.num_resp_ids);
len += sizeof(resp->tx_fetch_ind.records[0]) * num_records;
len += sizeof(resp->tx_fetch_ind.resp_ids[0]) * num_resp_ids;
if (unlikely(skb->len < len)) {
ath10k_warn(ar, "received corrupted tx_fetch_ind event: too many records/resp_ids\n");
return;
}
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind num records %hu num resps %hu seq %hu\n",
num_records, num_resp_ids,
le16_to_cpu(resp->tx_fetch_ind.fetch_seq_num));
if (!ar->htt.tx_q_state.enabled) {
ath10k_warn(ar, "received unexpected tx_fetch_ind event: not enabled\n");
return;
}
if (ar->htt.tx_q_state.mode == HTT_TX_MODE_SWITCH_PUSH) {
ath10k_warn(ar, "received unexpected tx_fetch_ind event: in push mode\n");
return;
}
rcu_read_lock();
for (i = 0; i < num_records; i++) {
record = &resp->tx_fetch_ind.records[i];
peer_id = MS(le16_to_cpu(record->info),
HTT_TX_FETCH_RECORD_INFO_PEER_ID);
tid = MS(le16_to_cpu(record->info),
HTT_TX_FETCH_RECORD_INFO_TID);
max_num_msdus = le16_to_cpu(record->num_msdus);
max_num_bytes = le32_to_cpu(record->num_bytes);
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch record %i peer_id %hu tid %hhu msdus %zu bytes %zu\n",
i, peer_id, tid, max_num_msdus, max_num_bytes);
if (unlikely(peer_id >= ar->htt.tx_q_state.num_peers) ||
unlikely(tid >= ar->htt.tx_q_state.num_tids)) {
ath10k_warn(ar, "received out of range peer_id %hu tid %hhu\n",
peer_id, tid);
continue;
}
spin_lock_bh(&ar->data_lock);
txq = ath10k_mac_txq_lookup(ar, peer_id, tid);
spin_unlock_bh(&ar->data_lock);
/* It is okay to release the lock and use txq because RCU read
* lock is held.
*/
if (unlikely(!txq)) {
ath10k_warn(ar, "failed to lookup txq for peer_id %hu tid %hhu\n",
peer_id, tid);
continue;
}
num_msdus = 0;
num_bytes = 0;
while (num_msdus < max_num_msdus &&
num_bytes < max_num_bytes) {
ret = ath10k_mac_tx_push_txq(hw, txq);
if (ret < 0)
break;
num_msdus++;
num_bytes += ret;
}
record->num_msdus = cpu_to_le16(num_msdus);
record->num_bytes = cpu_to_le32(num_bytes);
ath10k_htt_tx_txq_recalc(hw, txq);
}
rcu_read_unlock();
resp_ids = ath10k_htt_get_tx_fetch_ind_resp_ids(&resp->tx_fetch_ind);
ath10k_htt_rx_tx_fetch_resp_id_confirm(ar, resp_ids, num_resp_ids);
ret = ath10k_htt_tx_fetch_resp(ar,
resp->tx_fetch_ind.token,
resp->tx_fetch_ind.fetch_seq_num,
resp->tx_fetch_ind.records,
num_records);
if (unlikely(ret)) {
ath10k_warn(ar, "failed to submit tx fetch resp for token 0x%08x: %d\n",
le32_to_cpu(resp->tx_fetch_ind.token), ret);
/* FIXME: request fw restart */
}
ath10k_htt_tx_txq_sync(ar);
}
static void ath10k_htt_rx_tx_fetch_confirm(struct ath10k *ar,
struct sk_buff *skb)
{
const struct htt_resp *resp = (void *)skb->data;
size_t len;
int num_resp_ids;
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm\n");
len = sizeof(resp->hdr) + sizeof(resp->tx_fetch_confirm);
if (unlikely(skb->len < len)) {
ath10k_warn(ar, "received corrupted tx_fetch_confirm event: buffer too short\n");
return;
}
num_resp_ids = le16_to_cpu(resp->tx_fetch_confirm.num_resp_ids);
len += sizeof(resp->tx_fetch_confirm.resp_ids[0]) * num_resp_ids;
if (unlikely(skb->len < len)) {
ath10k_warn(ar, "received corrupted tx_fetch_confirm event: resp_ids buffer overflow\n");
return;
}
ath10k_htt_rx_tx_fetch_resp_id_confirm(ar,
resp->tx_fetch_confirm.resp_ids,
num_resp_ids);
}
static void ath10k_htt_rx_tx_mode_switch_ind(struct ath10k *ar,
struct sk_buff *skb)
{
const struct htt_resp *resp = (void *)skb->data;
const struct htt_tx_mode_switch_record *record;
struct ieee80211_txq *txq;
struct ath10k_txq *artxq;
size_t len;
size_t num_records;
enum htt_tx_mode_switch_mode mode;
bool enable;
u16 info0;
u16 info1;
u16 threshold;
u16 peer_id;
u8 tid;
int i;
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx mode switch ind\n");
len = sizeof(resp->hdr) + sizeof(resp->tx_mode_switch_ind);
if (unlikely(skb->len < len)) {
ath10k_warn(ar, "received corrupted tx_mode_switch_ind event: buffer too short\n");
return;
}
info0 = le16_to_cpu(resp->tx_mode_switch_ind.info0);
info1 = le16_to_cpu(resp->tx_mode_switch_ind.info1);
enable = !!(info0 & HTT_TX_MODE_SWITCH_IND_INFO0_ENABLE);
num_records = MS(info0, HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD);
mode = MS(info1, HTT_TX_MODE_SWITCH_IND_INFO1_MODE);
threshold = MS(info1, HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD);
ath10k_dbg(ar, ATH10K_DBG_HTT,
"htt rx tx mode switch ind info0 0x%04hx info1 0x%04hx enable %d num records %zd mode %d threshold %hu\n",
info0, info1, enable, num_records, mode, threshold);
len += sizeof(resp->tx_mode_switch_ind.records[0]) * num_records;
if (unlikely(skb->len < len)) {
ath10k_warn(ar, "received corrupted tx_mode_switch_mode_ind event: too many records\n");
return;
}
switch (mode) {
case HTT_TX_MODE_SWITCH_PUSH:
case HTT_TX_MODE_SWITCH_PUSH_PULL:
break;
default:
ath10k_warn(ar, "received invalid tx_mode_switch_mode_ind mode %d, ignoring\n",
mode);
return;
}
if (!enable)
return;
ar->htt.tx_q_state.enabled = enable;
ar->htt.tx_q_state.mode = mode;
ar->htt.tx_q_state.num_push_allowed = threshold;
rcu_read_lock();
for (i = 0; i < num_records; i++) {
record = &resp->tx_mode_switch_ind.records[i];
info0 = le16_to_cpu(record->info0);
peer_id = MS(info0, HTT_TX_MODE_SWITCH_RECORD_INFO0_PEER_ID);
tid = MS(info0, HTT_TX_MODE_SWITCH_RECORD_INFO0_TID);
if (unlikely(peer_id >= ar->htt.tx_q_state.num_peers) ||
unlikely(tid >= ar->htt.tx_q_state.num_tids)) {
ath10k_warn(ar, "received out of range peer_id %hu tid %hhu\n",
peer_id, tid);
continue;
}
spin_lock_bh(&ar->data_lock);
txq = ath10k_mac_txq_lookup(ar, peer_id, tid);
spin_unlock_bh(&ar->data_lock);
/* It is okay to release the lock and use txq because RCU read
* lock is held.
*/
if (unlikely(!txq)) {
ath10k_warn(ar, "failed to lookup txq for peer_id %hu tid %hhu\n",
peer_id, tid);
continue;
}
spin_lock_bh(&ar->htt.tx_lock);
artxq = (void *)txq->drv_priv;
artxq->num_push_allowed = le16_to_cpu(record->num_max_msdus);
spin_unlock_bh(&ar->htt.tx_lock);
}
rcu_read_unlock();
ath10k_mac_tx_push_pending(ar);
}
static inline enum ieee80211_band phy_mode_to_band(u32 phy_mode)
{
enum ieee80211_band band;
switch (phy_mode) {
case MODE_11A:
case MODE_11NA_HT20:
case MODE_11NA_HT40:
case MODE_11AC_VHT20:
case MODE_11AC_VHT40:
case MODE_11AC_VHT80:
band = IEEE80211_BAND_5GHZ;
break;
case MODE_11G:
case MODE_11B:
case MODE_11GONLY:
case MODE_11NG_HT20:
case MODE_11NG_HT40:
case MODE_11AC_VHT20_2G:
case MODE_11AC_VHT40_2G:
case MODE_11AC_VHT80_2G:
default:
band = IEEE80211_BAND_2GHZ;
}
return band;
}
void ath10k_htt_htc_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
{
bool release;
release = ath10k_htt_t2h_msg_handler(ar, skb);
/* Free the indication buffer */
if (release)
dev_kfree_skb_any(skb);
}
bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
struct htt_resp *resp = (struct htt_resp *)skb->data;
@ -1998,8 +2237,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
if (resp->hdr.msg_type >= ar->htt.t2h_msg_types_max) {
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx, unsupported msg_type: 0x%0X\n max: 0x%0X",
resp->hdr.msg_type, ar->htt.t2h_msg_types_max);
dev_kfree_skb_any(skb);
return;
return true;
}
type = ar->htt.t2h_msg_types[resp->hdr.msg_type];
@ -2011,9 +2249,8 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
break;
}
case HTT_T2H_MSG_TYPE_RX_IND:
skb_queue_tail(&htt->rx_compl_q, skb);
tasklet_schedule(&htt->txrx_compl_task);
return;
ath10k_htt_rx_proc_rx_ind(htt, &resp->rx_ind);
break;
case HTT_T2H_MSG_TYPE_PEER_MAP: {
struct htt_peer_map_event ev = {
.vdev_id = resp->peer_map.vdev_id,
@ -2034,28 +2271,33 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
struct htt_tx_done tx_done = {};
int status = __le32_to_cpu(resp->mgmt_tx_completion.status);
tx_done.msdu_id =
__le32_to_cpu(resp->mgmt_tx_completion.desc_id);
tx_done.msdu_id = __le32_to_cpu(resp->mgmt_tx_completion.desc_id);
switch (status) {
case HTT_MGMT_TX_STATUS_OK:
tx_done.success = true;
tx_done.status = HTT_TX_COMPL_STATE_ACK;
break;
case HTT_MGMT_TX_STATUS_RETRY:
tx_done.no_ack = true;
tx_done.status = HTT_TX_COMPL_STATE_NOACK;
break;
case HTT_MGMT_TX_STATUS_DROP:
tx_done.discard = true;
tx_done.status = HTT_TX_COMPL_STATE_DISCARD;
break;
}
ath10k_txrx_tx_unref(htt, &tx_done);
status = ath10k_txrx_tx_unref(htt, &tx_done);
if (!status) {
spin_lock_bh(&htt->tx_lock);
ath10k_htt_tx_mgmt_dec_pending(htt);
spin_unlock_bh(&htt->tx_lock);
}
ath10k_mac_tx_push_pending(ar);
break;
}
case HTT_T2H_MSG_TYPE_TX_COMPL_IND:
skb_queue_tail(&htt->tx_compl_q, skb);
ath10k_htt_rx_tx_compl_ind(htt->ar, skb);
tasklet_schedule(&htt->txrx_compl_task);
return;
break;
case HTT_T2H_MSG_TYPE_SEC_IND: {
struct ath10k *ar = htt->ar;
struct htt_security_indication *ev = &resp->security_indication;
@ -2071,7 +2313,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
case HTT_T2H_MSG_TYPE_RX_FRAG_IND: {
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
skb->data, skb->len);
ath10k_htt_rx_frag_handler(htt, &resp->rx_frag_ind);
ath10k_htt_rx_frag_handler(htt);
break;
}
case HTT_T2H_MSG_TYPE_TEST:
@ -2111,18 +2353,39 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
case HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND: {
skb_queue_tail(&htt->rx_in_ord_compl_q, skb);
tasklet_schedule(&htt->txrx_compl_task);
return;
return false;
}
case HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND:
break;
case HTT_T2H_MSG_TYPE_CHAN_CHANGE:
case HTT_T2H_MSG_TYPE_CHAN_CHANGE: {
u32 phymode = __le32_to_cpu(resp->chan_change.phymode);
u32 freq = __le32_to_cpu(resp->chan_change.freq);
ar->tgt_oper_chan =
__ieee80211_get_channel(ar->hw->wiphy, freq);
ath10k_dbg(ar, ATH10K_DBG_HTT,
"htt chan change freq %u phymode %s\n",
freq, ath10k_wmi_phymode_str(phymode));
break;
}
case HTT_T2H_MSG_TYPE_AGGR_CONF:
break;
case HTT_T2H_MSG_TYPE_TX_FETCH_IND:
case HTT_T2H_MSG_TYPE_TX_FETCH_IND: {
struct sk_buff *tx_fetch_ind = skb_copy(skb, GFP_ATOMIC);
if (!tx_fetch_ind) {
ath10k_warn(ar, "failed to copy htt tx fetch ind\n");
break;
}
skb_queue_tail(&htt->tx_fetch_ind_q, tx_fetch_ind);
tasklet_schedule(&htt->txrx_compl_task);
break;
}
case HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM:
ath10k_htt_rx_tx_fetch_confirm(ar, skb);
break;
case HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND:
/* TODO: Implement pull-push logic */
ath10k_htt_rx_tx_mode_switch_ind(ar, skb);
break;
case HTT_T2H_MSG_TYPE_EN_STATS:
default:
@ -2132,9 +2395,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
skb->data, skb->len);
break;
};
/* Free the indication buffer */
dev_kfree_skb_any(skb);
return true;
}
EXPORT_SYMBOL(ath10k_htt_t2h_msg_handler);
@ -2150,40 +2411,47 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
{
struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
struct ath10k *ar = htt->ar;
struct sk_buff_head tx_q;
struct sk_buff_head rx_q;
struct htt_tx_done tx_done = {};
struct sk_buff_head rx_ind_q;
struct htt_resp *resp;
struct sk_buff_head tx_ind_q;
struct sk_buff *skb;
unsigned long flags;
int num_mpdus;
__skb_queue_head_init(&tx_q);
__skb_queue_head_init(&rx_q);
__skb_queue_head_init(&rx_ind_q);
spin_lock_irqsave(&htt->tx_compl_q.lock, flags);
skb_queue_splice_init(&htt->tx_compl_q, &tx_q);
spin_unlock_irqrestore(&htt->tx_compl_q.lock, flags);
spin_lock_irqsave(&htt->rx_compl_q.lock, flags);
skb_queue_splice_init(&htt->rx_compl_q, &rx_q);
spin_unlock_irqrestore(&htt->rx_compl_q.lock, flags);
__skb_queue_head_init(&tx_ind_q);
spin_lock_irqsave(&htt->rx_in_ord_compl_q.lock, flags);
skb_queue_splice_init(&htt->rx_in_ord_compl_q, &rx_ind_q);
spin_unlock_irqrestore(&htt->rx_in_ord_compl_q.lock, flags);
while ((skb = __skb_dequeue(&tx_q))) {
ath10k_htt_rx_frm_tx_compl(htt->ar, skb);
spin_lock_irqsave(&htt->tx_fetch_ind_q.lock, flags);
skb_queue_splice_init(&htt->tx_fetch_ind_q, &tx_ind_q);
spin_unlock_irqrestore(&htt->tx_fetch_ind_q.lock, flags);
/* kfifo_get: called only within txrx_tasklet so it's neatly serialized.
* From kfifo_get() documentation:
* Note that with only one concurrent reader and one concurrent writer,
* you don't need extra locking to use these macro.
*/
while (kfifo_get(&htt->txdone_fifo, &tx_done))
ath10k_txrx_tx_unref(htt, &tx_done);
while ((skb = __skb_dequeue(&tx_ind_q))) {
ath10k_htt_rx_tx_fetch_ind(ar, skb);
dev_kfree_skb_any(skb);
}
while ((skb = __skb_dequeue(&rx_q))) {
resp = (struct htt_resp *)skb->data;
spin_lock_bh(&htt->rx_ring.lock);
ath10k_htt_rx_handler(htt, &resp->rx_ind);
spin_unlock_bh(&htt->rx_ring.lock);
dev_kfree_skb_any(skb);
ath10k_mac_tx_push_pending(ar);
num_mpdus = atomic_read(&htt->num_mpdus_ready);
while (num_mpdus) {
if (ath10k_htt_rx_handle_amsdu(htt))
break;
num_mpdus--;
atomic_dec(&htt->num_mpdus_ready);
}
while ((skb = __skb_dequeue(&rx_ind_q))) {
@ -2192,4 +2460,6 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
spin_unlock_bh(&htt->rx_ring.lock);
dev_kfree_skb_any(skb);
}
ath10k_htt_rx_msdu_buff_replenish(htt);
}

View File

@ -22,53 +22,183 @@
#include "txrx.h"
#include "debug.h"
void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc)
static u8 ath10k_htt_tx_txq_calc_size(size_t count)
{
if (limit_mgmt_desc)
htt->num_pending_mgmt_tx--;
int exp;
int factor;
exp = 0;
factor = count >> 7;
while (factor >= 64 && exp < 4) {
factor >>= 3;
exp++;
}
if (exp == 4)
return 0xff;
if (count > 0)
factor = max(1, factor);
return SM(exp, HTT_TX_Q_STATE_ENTRY_EXP) |
SM(factor, HTT_TX_Q_STATE_ENTRY_FACTOR);
}
static void __ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
struct ath10k *ar = hw->priv;
struct ath10k_sta *arsta = (void *)txq->sta->drv_priv;
struct ath10k_vif *arvif = (void *)txq->vif->drv_priv;
unsigned long frame_cnt;
unsigned long byte_cnt;
int idx;
u32 bit;
u16 peer_id;
u8 tid;
u8 count;
lockdep_assert_held(&ar->htt.tx_lock);
if (!ar->htt.tx_q_state.enabled)
return;
if (ar->htt.tx_q_state.mode != HTT_TX_MODE_SWITCH_PUSH_PULL)
return;
if (txq->sta)
peer_id = arsta->peer_id;
else
peer_id = arvif->peer_id;
tid = txq->tid;
bit = BIT(peer_id % 32);
idx = peer_id / 32;
ieee80211_txq_get_depth(txq, &frame_cnt, &byte_cnt);
count = ath10k_htt_tx_txq_calc_size(byte_cnt);
if (unlikely(peer_id >= ar->htt.tx_q_state.num_peers) ||
unlikely(tid >= ar->htt.tx_q_state.num_tids)) {
ath10k_warn(ar, "refusing to update txq for peer_id %hu tid %hhu due to out of bounds\n",
peer_id, tid);
return;
}
ar->htt.tx_q_state.vaddr->count[tid][peer_id] = count;
ar->htt.tx_q_state.vaddr->map[tid][idx] &= ~bit;
ar->htt.tx_q_state.vaddr->map[tid][idx] |= count ? bit : 0;
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx txq state update peer_id %hu tid %hhu count %hhu\n",
peer_id, tid, count);
}
static void __ath10k_htt_tx_txq_sync(struct ath10k *ar)
{
u32 seq;
size_t size;
lockdep_assert_held(&ar->htt.tx_lock);
if (!ar->htt.tx_q_state.enabled)
return;
if (ar->htt.tx_q_state.mode != HTT_TX_MODE_SWITCH_PUSH_PULL)
return;
seq = le32_to_cpu(ar->htt.tx_q_state.vaddr->seq);
seq++;
ar->htt.tx_q_state.vaddr->seq = cpu_to_le32(seq);
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx txq state update commit seq %u\n",
seq);
size = sizeof(*ar->htt.tx_q_state.vaddr);
dma_sync_single_for_device(ar->dev,
ar->htt.tx_q_state.paddr,
size,
DMA_TO_DEVICE);
}
void ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
struct ath10k *ar = hw->priv;
spin_lock_bh(&ar->htt.tx_lock);
__ath10k_htt_tx_txq_recalc(hw, txq);
spin_unlock_bh(&ar->htt.tx_lock);
}
void ath10k_htt_tx_txq_sync(struct ath10k *ar)
{
spin_lock_bh(&ar->htt.tx_lock);
__ath10k_htt_tx_txq_sync(ar);
spin_unlock_bh(&ar->htt.tx_lock);
}
void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
struct ath10k *ar = hw->priv;
spin_lock_bh(&ar->htt.tx_lock);
__ath10k_htt_tx_txq_recalc(hw, txq);
__ath10k_htt_tx_txq_sync(ar);
spin_unlock_bh(&ar->htt.tx_lock);
}
void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt)
{
lockdep_assert_held(&htt->tx_lock);
htt->num_pending_tx--;
if (htt->num_pending_tx == htt->max_num_pending_tx - 1)
ath10k_mac_tx_unlock(htt->ar, ATH10K_TX_PAUSE_Q_FULL);
}
static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
bool limit_mgmt_desc)
int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt)
{
spin_lock_bh(&htt->tx_lock);
__ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
spin_unlock_bh(&htt->tx_lock);
}
lockdep_assert_held(&htt->tx_lock);
static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
bool limit_mgmt_desc, bool is_probe_resp)
{
struct ath10k *ar = htt->ar;
int ret = 0;
spin_lock_bh(&htt->tx_lock);
if (htt->num_pending_tx >= htt->max_num_pending_tx) {
ret = -EBUSY;
goto exit;
}
if (limit_mgmt_desc) {
if (is_probe_resp && (htt->num_pending_mgmt_tx >
ar->hw_params.max_probe_resp_desc_thres)) {
ret = -EBUSY;
goto exit;
}
htt->num_pending_mgmt_tx++;
}
if (htt->num_pending_tx >= htt->max_num_pending_tx)
return -EBUSY;
htt->num_pending_tx++;
if (htt->num_pending_tx == htt->max_num_pending_tx)
ath10k_mac_tx_lock(htt->ar, ATH10K_TX_PAUSE_Q_FULL);
exit:
spin_unlock_bh(&htt->tx_lock);
return ret;
return 0;
}
int ath10k_htt_tx_mgmt_inc_pending(struct ath10k_htt *htt, bool is_mgmt,
bool is_presp)
{
struct ath10k *ar = htt->ar;
lockdep_assert_held(&htt->tx_lock);
if (!is_mgmt || !ar->hw_params.max_probe_resp_desc_thres)
return 0;
if (is_presp &&
ar->hw_params.max_probe_resp_desc_thres < htt->num_pending_mgmt_tx)
return -EBUSY;
htt->num_pending_mgmt_tx++;
return 0;
}
void ath10k_htt_tx_mgmt_dec_pending(struct ath10k_htt *htt)
{
lockdep_assert_held(&htt->tx_lock);
if (!htt->ar->hw_params.max_probe_resp_desc_thres)
return;
htt->num_pending_mgmt_tx--;
}
int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb)
@ -209,8 +339,18 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
goto free_frag_desc;
}
size = roundup_pow_of_two(htt->max_num_pending_tx);
ret = kfifo_alloc(&htt->txdone_fifo, size, GFP_KERNEL);
if (ret) {
ath10k_err(ar, "failed to alloc txdone fifo: %d\n", ret);
goto free_txq;
}
return 0;
free_txq:
ath10k_htt_tx_free_txq(htt);
free_frag_desc:
ath10k_htt_tx_free_cont_frag_desc(htt);
@ -234,8 +374,8 @@ static int ath10k_htt_tx_clean_up_pending(int msdu_id, void *skb, void *ctx)
ath10k_dbg(ar, ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", msdu_id);
tx_done.discard = 1;
tx_done.msdu_id = msdu_id;
tx_done.status = HTT_TX_COMPL_STATE_DISCARD;
ath10k_txrx_tx_unref(htt, &tx_done);
@ -258,6 +398,8 @@ void ath10k_htt_tx_free(struct ath10k_htt *htt)
ath10k_htt_tx_free_txq(htt);
ath10k_htt_tx_free_cont_frag_desc(htt);
WARN_ON(!kfifo_is_empty(&htt->txdone_fifo));
kfifo_free(&htt->txdone_fifo);
}
void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
@ -535,6 +677,55 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
return 0;
}
int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
__le32 token,
__le16 fetch_seq_num,
struct htt_tx_fetch_record *records,
size_t num_records)
{
struct sk_buff *skb;
struct htt_cmd *cmd;
const u16 resp_id = 0;
int len = 0;
int ret;
/* Response IDs are echo-ed back only for host driver convienence
* purposes. They aren't used for anything in the driver yet so use 0.
*/
len += sizeof(cmd->hdr);
len += sizeof(cmd->tx_fetch_resp);
len += sizeof(cmd->tx_fetch_resp.records[0]) * num_records;
skb = ath10k_htc_alloc_skb(ar, len);
if (!skb)
return -ENOMEM;
skb_put(skb, len);
cmd = (struct htt_cmd *)skb->data;
cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FETCH_RESP;
cmd->tx_fetch_resp.resp_id = cpu_to_le16(resp_id);
cmd->tx_fetch_resp.fetch_seq_num = fetch_seq_num;
cmd->tx_fetch_resp.num_records = cpu_to_le16(num_records);
cmd->tx_fetch_resp.token = token;
memcpy(cmd->tx_fetch_resp.records, records,
sizeof(records[0]) * num_records);
ret = ath10k_htc_send(&ar->htc, ar->htt.eid, skb);
if (ret) {
ath10k_warn(ar, "failed to submit htc command: %d\n", ret);
goto err_free_skb;
}
return 0;
err_free_skb:
dev_kfree_skb_any(skb);
return ret;
}
static u8 ath10k_htt_tx_get_vdev_id(struct ath10k *ar, struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@ -576,20 +767,6 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
int msdu_id = -1;
int res;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
bool limit_mgmt_desc = false;
bool is_probe_resp = false;
if (ar->hw_params.max_probe_resp_desc_thres) {
limit_mgmt_desc = true;
if (ieee80211_is_probe_resp(hdr->frame_control))
is_probe_resp = true;
}
res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp);
if (res)
goto err;
len += sizeof(cmd->hdr);
len += sizeof(cmd->mgmt_tx);
@ -598,7 +775,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
spin_unlock_bh(&htt->tx_lock);
if (res < 0)
goto err_tx_dec;
goto err;
msdu_id = res;
@ -649,8 +826,6 @@ err_free_msdu_id:
spin_lock_bh(&htt->tx_lock);
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
spin_unlock_bh(&htt->tx_lock);
err_tx_dec:
ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
err:
return res;
}
@ -677,26 +852,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode,
u32 frags_paddr = 0;
u32 txbuf_paddr;
struct htt_msdu_ext_desc *ext_desc = NULL;
bool limit_mgmt_desc = false;
bool is_probe_resp = false;
if (unlikely(ieee80211_is_mgmt(hdr->frame_control)) &&
ar->hw_params.max_probe_resp_desc_thres) {
limit_mgmt_desc = true;
if (ieee80211_is_probe_resp(hdr->frame_control))
is_probe_resp = true;
}
res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp);
if (res)
goto err;
spin_lock_bh(&htt->tx_lock);
res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
spin_unlock_bh(&htt->tx_lock);
if (res < 0)
goto err_tx_dec;
goto err;
msdu_id = res;
@ -862,11 +1023,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode,
err_unmap_msdu:
dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
err_free_msdu_id:
spin_lock_bh(&htt->tx_lock);
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
spin_unlock_bh(&htt->tx_lock);
err_tx_dec:
ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
err:
return res;
}

View File

@ -134,8 +134,6 @@ enum qca9377_chip_id_rev {
#define REG_DUMP_COUNT_QCA988X 60
#define QCA988X_CAL_DATA_LEN 2116
struct ath10k_fw_ie {
__le32 id;
__le32 len;
@ -431,10 +429,14 @@ enum ath10k_hw_4addr_pad {
#define TARGET_10_4_ACTIVE_PEERS 0
#define TARGET_10_4_NUM_QCACHE_PEERS_MAX 512
#define TARGET_10_4_QCACHE_ACTIVE_PEERS 50
#define TARGET_10_4_QCACHE_ACTIVE_PEERS_PFC 35
#define TARGET_10_4_NUM_OFFLOAD_PEERS 0
#define TARGET_10_4_NUM_OFFLOAD_REORDER_BUFFS 0
#define TARGET_10_4_NUM_PEER_KEYS 2
#define TARGET_10_4_TGT_NUM_TIDS ((TARGET_10_4_NUM_PEERS) * 2)
#define TARGET_10_4_NUM_MSDU_DESC (1024 + 400)
#define TARGET_10_4_NUM_MSDU_DESC_PFC 2500
#define TARGET_10_4_AST_SKID_LIMIT 32
/* 100 ms for video, best-effort, and background */

View File

@ -618,10 +618,15 @@ ath10k_mac_get_any_chandef_iter(struct ieee80211_hw *hw,
*def = &conf->def;
}
static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr,
static int ath10k_peer_create(struct ath10k *ar,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
u32 vdev_id,
const u8 *addr,
enum wmi_peer_type peer_type)
{
struct ath10k_vif *arvif;
struct ath10k_peer *peer;
int num_peers = 0;
int ret;
@ -650,6 +655,22 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr,
return ret;
}
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find(ar, vdev_id, addr);
if (!peer) {
ath10k_warn(ar, "failed to find peer %pM on vdev %i after creation\n",
addr, vdev_id);
ath10k_wmi_peer_delete(ar, vdev_id, addr);
spin_unlock_bh(&ar->data_lock);
return -ENOENT;
}
peer->vif = vif;
peer->sta = sta;
spin_unlock_bh(&ar->data_lock);
ar->num_peers++;
return 0;
@ -731,6 +752,7 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
{
struct ath10k_peer *peer, *tmp;
int peer_id;
lockdep_assert_held(&ar->conf_mutex);
@ -742,6 +764,11 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
ath10k_warn(ar, "removing stale peer %pM from vdev_id %d\n",
peer->addr, vdev_id);
for_each_set_bit(peer_id, peer->peer_ids,
ATH10K_MAX_NUM_PEER_IDS) {
ar->peer_map[peer_id] = NULL;
}
list_del(&peer->list);
kfree(peer);
ar->num_peers--;
@ -2994,6 +3021,13 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
/* TX handlers */
/***************/
enum ath10k_mac_tx_path {
ATH10K_MAC_TX_HTT,
ATH10K_MAC_TX_HTT_MGMT,
ATH10K_MAC_TX_WMI_MGMT,
ATH10K_MAC_TX_UNKNOWN,
};
void ath10k_mac_tx_lock(struct ath10k *ar, int reason)
{
lockdep_assert_held(&ar->htt.tx_lock);
@ -3271,6 +3305,28 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
}
}
static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar,
struct ieee80211_vif *vif,
struct ieee80211_txq *txq,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
cb->flags = 0;
if (!ath10k_tx_h_use_hwcrypto(vif, skb))
cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
if (ieee80211_is_mgmt(hdr->frame_control))
cb->flags |= ATH10K_SKB_F_MGMT;
if (ieee80211_is_data_qos(hdr->frame_control))
cb->flags |= ATH10K_SKB_F_QOS;
cb->vif = vif;
cb->txq = txq;
}
bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar)
{
/* FIXME: Not really sure since when the behaviour changed. At some
@ -3306,26 +3362,50 @@ unlock:
return ret;
}
static void ath10k_mac_tx(struct ath10k *ar, enum ath10k_hw_txrx_mode txmode,
struct sk_buff *skb)
static enum ath10k_mac_tx_path
ath10k_mac_tx_h_get_txpath(struct ath10k *ar,
struct sk_buff *skb,
enum ath10k_hw_txrx_mode txmode)
{
struct ath10k_htt *htt = &ar->htt;
int ret = 0;
switch (txmode) {
case ATH10K_HW_TXRX_RAW:
case ATH10K_HW_TXRX_NATIVE_WIFI:
case ATH10K_HW_TXRX_ETHERNET:
ret = ath10k_htt_tx(htt, txmode, skb);
break;
return ATH10K_MAC_TX_HTT;
case ATH10K_HW_TXRX_MGMT:
if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
ar->fw_features))
ret = ath10k_mac_tx_wmi_mgmt(ar, skb);
return ATH10K_MAC_TX_WMI_MGMT;
else if (ar->htt.target_version_major >= 3)
ret = ath10k_htt_tx(htt, txmode, skb);
return ATH10K_MAC_TX_HTT;
else
ret = ath10k_htt_mgmt_tx(htt, skb);
return ATH10K_MAC_TX_HTT_MGMT;
}
return ATH10K_MAC_TX_UNKNOWN;
}
static int ath10k_mac_tx_submit(struct ath10k *ar,
enum ath10k_hw_txrx_mode txmode,
enum ath10k_mac_tx_path txpath,
struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
int ret = -EINVAL;
switch (txpath) {
case ATH10K_MAC_TX_HTT:
ret = ath10k_htt_tx(htt, txmode, skb);
break;
case ATH10K_MAC_TX_HTT_MGMT:
ret = ath10k_htt_mgmt_tx(htt, skb);
break;
case ATH10K_MAC_TX_WMI_MGMT:
ret = ath10k_mac_tx_wmi_mgmt(ar, skb);
break;
case ATH10K_MAC_TX_UNKNOWN:
WARN_ON_ONCE(1);
ret = -EINVAL;
break;
}
@ -3334,6 +3414,64 @@ static void ath10k_mac_tx(struct ath10k *ar, enum ath10k_hw_txrx_mode txmode,
ret);
ieee80211_free_txskb(ar->hw, skb);
}
return ret;
}
/* This function consumes the sk_buff regardless of return value as far as
* caller is concerned so no freeing is necessary afterwards.
*/
static int ath10k_mac_tx(struct ath10k *ar,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
enum ath10k_hw_txrx_mode txmode,
enum ath10k_mac_tx_path txpath,
struct sk_buff *skb)
{
struct ieee80211_hw *hw = ar->hw;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int ret;
/* We should disable CCK RATE due to P2P */
if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
switch (txmode) {
case ATH10K_HW_TXRX_MGMT:
case ATH10K_HW_TXRX_NATIVE_WIFI:
ath10k_tx_h_nwifi(hw, skb);
ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
ath10k_tx_h_seq_no(vif, skb);
break;
case ATH10K_HW_TXRX_ETHERNET:
ath10k_tx_h_8023(skb);
break;
case ATH10K_HW_TXRX_RAW:
if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
WARN_ON_ONCE(1);
ieee80211_free_txskb(hw, skb);
return -ENOTSUPP;
}
}
if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
if (!ath10k_mac_tx_frm_has_freq(ar)) {
ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
skb);
skb_queue_tail(&ar->offchan_tx_queue, skb);
ieee80211_queue_work(hw, &ar->offchan_tx_work);
return 0;
}
}
ret = ath10k_mac_tx_submit(ar, txmode, txpath, skb);
if (ret) {
ath10k_warn(ar, "failed to submit frame: %d\n", ret);
return ret;
}
return 0;
}
void ath10k_offchan_tx_purge(struct ath10k *ar)
@ -3354,12 +3492,13 @@ void ath10k_offchan_tx_work(struct work_struct *work)
struct ath10k *ar = container_of(work, struct ath10k, offchan_tx_work);
struct ath10k_peer *peer;
struct ath10k_vif *arvif;
enum ath10k_hw_txrx_mode txmode;
enum ath10k_mac_tx_path txpath;
struct ieee80211_hdr *hdr;
struct ieee80211_vif *vif;
struct ieee80211_sta *sta;
struct sk_buff *skb;
const u8 *peer_addr;
enum ath10k_hw_txrx_mode txmode;
int vdev_id;
int ret;
unsigned long time_left;
@ -3396,7 +3535,8 @@ void ath10k_offchan_tx_work(struct work_struct *work)
peer_addr, vdev_id);
if (!peer) {
ret = ath10k_peer_create(ar, vdev_id, peer_addr,
ret = ath10k_peer_create(ar, NULL, NULL, vdev_id,
peer_addr,
WMI_PEER_TYPE_DEFAULT);
if (ret)
ath10k_warn(ar, "failed to create peer %pM on vdev %d: %d\n",
@ -3423,8 +3563,14 @@ void ath10k_offchan_tx_work(struct work_struct *work)
}
txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
ath10k_mac_tx(ar, txmode, skb);
ret = ath10k_mac_tx(ar, vif, sta, txmode, txpath, skb);
if (ret) {
ath10k_warn(ar, "failed to transmit offchannel frame: %d\n",
ret);
/* not serious */
}
time_left =
wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ);
@ -3476,6 +3622,175 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
}
}
static void ath10k_mac_txq_init(struct ieee80211_txq *txq)
{
struct ath10k_txq *artxq = (void *)txq->drv_priv;
if (!txq)
return;
INIT_LIST_HEAD(&artxq->list);
}
static void ath10k_mac_txq_unref(struct ath10k *ar, struct ieee80211_txq *txq)
{
struct ath10k_txq *artxq = (void *)txq->drv_priv;
struct ath10k_skb_cb *cb;
struct sk_buff *msdu;
int msdu_id;
if (!txq)
return;
spin_lock_bh(&ar->txqs_lock);
if (!list_empty(&artxq->list))
list_del_init(&artxq->list);
spin_unlock_bh(&ar->txqs_lock);
spin_lock_bh(&ar->htt.tx_lock);
idr_for_each_entry(&ar->htt.pending_tx, msdu, msdu_id) {
cb = ATH10K_SKB_CB(msdu);
if (cb->txq == txq)
cb->txq = NULL;
}
spin_unlock_bh(&ar->htt.tx_lock);
}
struct ieee80211_txq *ath10k_mac_txq_lookup(struct ath10k *ar,
u16 peer_id,
u8 tid)
{
struct ath10k_peer *peer;
lockdep_assert_held(&ar->data_lock);
peer = ar->peer_map[peer_id];
if (!peer)
return NULL;
if (peer->sta)
return peer->sta->txq[tid];
else if (peer->vif)
return peer->vif->txq;
else
return NULL;
}
static bool ath10k_mac_tx_can_push(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
struct ath10k *ar = hw->priv;
struct ath10k_txq *artxq = (void *)txq->drv_priv;
/* No need to get locks */
if (ar->htt.tx_q_state.mode == HTT_TX_MODE_SWITCH_PUSH)
return true;
if (ar->htt.num_pending_tx < ar->htt.tx_q_state.num_push_allowed)
return true;
if (artxq->num_fw_queued < artxq->num_push_allowed)
return true;
return false;
}
int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
struct ath10k *ar = hw->priv;
struct ath10k_htt *htt = &ar->htt;
struct ath10k_txq *artxq = (void *)txq->drv_priv;
struct ieee80211_vif *vif = txq->vif;
struct ieee80211_sta *sta = txq->sta;
enum ath10k_hw_txrx_mode txmode;
enum ath10k_mac_tx_path txpath;
struct sk_buff *skb;
size_t skb_len;
int ret;
spin_lock_bh(&ar->htt.tx_lock);
ret = ath10k_htt_tx_inc_pending(htt);
spin_unlock_bh(&ar->htt.tx_lock);
if (ret)
return ret;
skb = ieee80211_tx_dequeue(hw, txq);
if (!skb) {
spin_lock_bh(&ar->htt.tx_lock);
ath10k_htt_tx_dec_pending(htt);
spin_unlock_bh(&ar->htt.tx_lock);
return -ENOENT;
}
ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb);
skb_len = skb->len;
txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
ret = ath10k_mac_tx(ar, vif, sta, txmode, txpath, skb);
if (unlikely(ret)) {
ath10k_warn(ar, "failed to push frame: %d\n", ret);
spin_lock_bh(&ar->htt.tx_lock);
ath10k_htt_tx_dec_pending(htt);
spin_unlock_bh(&ar->htt.tx_lock);
return ret;
}
spin_lock_bh(&ar->htt.tx_lock);
artxq->num_fw_queued++;
spin_unlock_bh(&ar->htt.tx_lock);
return skb_len;
}
void ath10k_mac_tx_push_pending(struct ath10k *ar)
{
struct ieee80211_hw *hw = ar->hw;
struct ieee80211_txq *txq;
struct ath10k_txq *artxq;
struct ath10k_txq *last;
int ret;
int max;
spin_lock_bh(&ar->txqs_lock);
rcu_read_lock();
last = list_last_entry(&ar->txqs, struct ath10k_txq, list);
while (!list_empty(&ar->txqs)) {
artxq = list_first_entry(&ar->txqs, struct ath10k_txq, list);
txq = container_of((void *)artxq, struct ieee80211_txq,
drv_priv);
/* Prevent aggressive sta/tid taking over tx queue */
max = 16;
ret = 0;
while (ath10k_mac_tx_can_push(hw, txq) && max--) {
ret = ath10k_mac_tx_push_txq(hw, txq);
if (ret < 0)
break;
}
list_del_init(&artxq->list);
if (ret != -ENOENT)
list_add_tail(&artxq->list, &ar->txqs);
ath10k_htt_tx_txq_update(hw, txq);
if (artxq == last || (ret < 0 && ret != -ENOENT))
break;
}
rcu_read_unlock();
spin_unlock_bh(&ar->txqs_lock);
}
/************/
/* Scanning */
/************/
@ -3638,66 +3953,86 @@ static int ath10k_start_scan(struct ath10k *ar,
/* mac80211 callbacks */
/**********************/
static void ath10k_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_control *control,
struct sk_buff *skb)
static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_control *control,
struct sk_buff *skb)
{
struct ath10k *ar = hw->priv;
struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
struct ath10k_htt *htt = &ar->htt;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = info->control.vif;
struct ieee80211_sta *sta = control->sta;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_txq *txq = NULL;
struct ieee80211_hdr *hdr = (void *)skb->data;
enum ath10k_hw_txrx_mode txmode;
enum ath10k_mac_tx_path txpath;
bool is_htt;
bool is_mgmt;
bool is_presp;
int ret;
/* We should disable CCK RATE due to P2P */
if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb);
txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
is_htt = (txpath == ATH10K_MAC_TX_HTT ||
txpath == ATH10K_MAC_TX_HTT_MGMT);
is_mgmt = (txpath == ATH10K_MAC_TX_HTT_MGMT);
skb_cb->flags = 0;
if (!ath10k_tx_h_use_hwcrypto(vif, skb))
skb_cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
if (is_htt) {
spin_lock_bh(&ar->htt.tx_lock);
is_presp = ieee80211_is_probe_resp(hdr->frame_control);
if (ieee80211_is_mgmt(hdr->frame_control))
skb_cb->flags |= ATH10K_SKB_F_MGMT;
if (ieee80211_is_data_qos(hdr->frame_control))
skb_cb->flags |= ATH10K_SKB_F_QOS;
skb_cb->vif = vif;
switch (txmode) {
case ATH10K_HW_TXRX_MGMT:
case ATH10K_HW_TXRX_NATIVE_WIFI:
ath10k_tx_h_nwifi(hw, skb);
ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
ath10k_tx_h_seq_no(vif, skb);
break;
case ATH10K_HW_TXRX_ETHERNET:
ath10k_tx_h_8023(skb);
break;
case ATH10K_HW_TXRX_RAW:
if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
WARN_ON_ONCE(1);
ieee80211_free_txskb(hw, skb);
ret = ath10k_htt_tx_inc_pending(htt);
if (ret) {
ath10k_warn(ar, "failed to increase tx pending count: %d, dropping\n",
ret);
spin_unlock_bh(&ar->htt.tx_lock);
ieee80211_free_txskb(ar->hw, skb);
return;
}
}
if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
if (!ath10k_mac_tx_frm_has_freq(ar)) {
ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
skb);
skb_queue_tail(&ar->offchan_tx_queue, skb);
ieee80211_queue_work(hw, &ar->offchan_tx_work);
ret = ath10k_htt_tx_mgmt_inc_pending(htt, is_mgmt, is_presp);
if (ret) {
ath10k_dbg(ar, ATH10K_DBG_MAC, "failed to increase tx mgmt pending count: %d, dropping\n",
ret);
ath10k_htt_tx_dec_pending(htt);
spin_unlock_bh(&ar->htt.tx_lock);
ieee80211_free_txskb(ar->hw, skb);
return;
}
spin_unlock_bh(&ar->htt.tx_lock);
}
ath10k_mac_tx(ar, txmode, skb);
ret = ath10k_mac_tx(ar, vif, sta, txmode, txpath, skb);
if (ret) {
ath10k_warn(ar, "failed to transmit frame: %d\n", ret);
if (is_htt) {
spin_lock_bh(&ar->htt.tx_lock);
ath10k_htt_tx_dec_pending(htt);
if (is_mgmt)
ath10k_htt_tx_mgmt_dec_pending(htt);
spin_unlock_bh(&ar->htt.tx_lock);
}
return;
}
}
static void ath10k_mac_op_wake_tx_queue(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
struct ath10k *ar = hw->priv;
struct ath10k_txq *artxq = (void *)txq->drv_priv;
spin_lock_bh(&ar->txqs_lock);
if (list_empty(&artxq->list))
list_add_tail(&artxq->list, &ar->txqs);
spin_unlock_bh(&ar->txqs_lock);
if (ath10k_mac_tx_can_push(hw, txq))
tasklet_schedule(&ar->htt.txrx_compl_task);
ath10k_htt_tx_txq_update(hw, txq);
}
/* Must not be called with conf_mutex held as workers can use that also. */
@ -4100,7 +4435,7 @@ static int ath10k_start(struct ieee80211_hw *hw)
ar->ani_enabled = true;
if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) {
if (ath10k_peer_stats_enabled(ar)) {
param = ar->wmi.pdev_param->peer_stats_update_period;
ret = ath10k_wmi_pdev_set_param(ar, param,
PEER_DEFAULT_STATS_UPDATE_PERIOD);
@ -4313,6 +4648,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
{
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct ath10k_peer *peer;
enum wmi_sta_powersave_param param;
int ret = 0;
u32 value;
@ -4325,6 +4661,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
mutex_lock(&ar->conf_mutex);
memset(arvif, 0, sizeof(*arvif));
ath10k_mac_txq_init(vif->txq);
arvif->ar = ar;
arvif->vif = vif;
@ -4489,7 +4826,10 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
goto err_vdev_delete;
}
if (ar->cfg_tx_chainmask) {
/* Configuring number of spatial stream for monitor interface is causing
* target assert in qca9888 and qca6174.
*/
if (ar->cfg_tx_chainmask && (vif->type != NL80211_IFTYPE_MONITOR)) {
u16 nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
vdev_param = ar->wmi.vdev_param->nss;
@ -4505,13 +4845,31 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
if (arvif->vdev_type == WMI_VDEV_TYPE_AP ||
arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr,
WMI_PEER_TYPE_DEFAULT);
ret = ath10k_peer_create(ar, vif, NULL, arvif->vdev_id,
vif->addr, WMI_PEER_TYPE_DEFAULT);
if (ret) {
ath10k_warn(ar, "failed to create vdev %i peer for AP/IBSS: %d\n",
arvif->vdev_id, ret);
goto err_vdev_delete;
}
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find(ar, arvif->vdev_id, vif->addr);
if (!peer) {
ath10k_warn(ar, "failed to lookup peer %pM on vdev %i\n",
vif->addr, arvif->vdev_id);
spin_unlock_bh(&ar->data_lock);
ret = -ENOENT;
goto err_peer_delete;
}
arvif->peer_id = find_first_bit(peer->peer_ids,
ATH10K_MAX_NUM_PEER_IDS);
spin_unlock_bh(&ar->data_lock);
} else {
arvif->peer_id = HTT_INVALID_PEERID;
}
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
@ -4622,7 +4980,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
{
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct ath10k_peer *peer;
int ret;
int i;
cancel_work_sync(&arvif->ap_csa_work);
cancel_delayed_work_sync(&arvif->connection_loss_work);
@ -4676,7 +5036,22 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
spin_unlock_bh(&ar->data_lock);
}
spin_lock_bh(&ar->data_lock);
for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
peer = ar->peer_map[i];
if (!peer)
continue;
if (peer->vif == vif) {
ath10k_warn(ar, "found vif peer %pM entry on vdev %i after it was supposedly removed\n",
vif->addr, arvif->vdev_id);
peer->vif = NULL;
}
}
spin_unlock_bh(&ar->data_lock);
ath10k_peer_cleanup(ar, arvif->vdev_id);
ath10k_mac_txq_unref(ar, vif->txq);
if (vif->type == NL80211_IFTYPE_MONITOR) {
ar->monitor_arvif = NULL;
@ -4689,6 +5064,8 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
ath10k_mac_vif_tx_unlock_all(arvif);
spin_unlock_bh(&ar->htt.tx_lock);
ath10k_mac_txq_unref(ar, vif->txq);
mutex_unlock(&ar->conf_mutex);
}
@ -5393,13 +5770,18 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
struct ath10k_peer *peer;
int ret = 0;
int i;
if (old_state == IEEE80211_STA_NOTEXIST &&
new_state == IEEE80211_STA_NONE) {
memset(arsta, 0, sizeof(*arsta));
arsta->arvif = arvif;
INIT_WORK(&arsta->update_wk, ath10k_sta_rc_update_wk);
for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
ath10k_mac_txq_init(sta->txq[i]);
}
/* cancel must be done outside the mutex to avoid deadlock */
@ -5434,8 +5816,8 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
if (sta->tdls)
peer_type = WMI_PEER_TYPE_TDLS;
ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr,
peer_type);
ret = ath10k_peer_create(ar, vif, sta, arvif->vdev_id,
sta->addr, peer_type);
if (ret) {
ath10k_warn(ar, "failed to add peer %pM for vdev %d when adding a new sta: %i\n",
sta->addr, arvif->vdev_id, ret);
@ -5443,6 +5825,24 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
goto exit;
}
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find(ar, arvif->vdev_id, sta->addr);
if (!peer) {
ath10k_warn(ar, "failed to lookup peer %pM on vdev %i\n",
vif->addr, arvif->vdev_id);
spin_unlock_bh(&ar->data_lock);
ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
ath10k_mac_dec_num_stations(arvif, sta);
ret = -ENOENT;
goto exit;
}
arsta->peer_id = find_first_bit(peer->peer_ids,
ATH10K_MAX_NUM_PEER_IDS);
spin_unlock_bh(&ar->data_lock);
if (!sta->tdls)
goto exit;
@ -5505,6 +5905,23 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
ath10k_mac_dec_num_stations(arvif, sta);
spin_lock_bh(&ar->data_lock);
for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
peer = ar->peer_map[i];
if (!peer)
continue;
if (peer->sta == sta) {
ath10k_warn(ar, "found sta peer %pM entry on vdev %i after it was supposedly removed\n",
sta->addr, arvif->vdev_id);
peer->sta = NULL;
}
}
spin_unlock_bh(&ar->data_lock);
for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
ath10k_mac_txq_unref(ar, sta->txq[i]);
if (!sta->tdls)
goto exit;
@ -6807,7 +7224,8 @@ ath10k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw,
}
static const struct ieee80211_ops ath10k_ops = {
.tx = ath10k_tx,
.tx = ath10k_mac_op_tx,
.wake_tx_queue = ath10k_mac_op_wake_tx_queue,
.start = ath10k_start,
.stop = ath10k_stop,
.config = ath10k_config,
@ -7262,6 +7680,7 @@ int ath10k_mac_register(struct ath10k *ar)
ar->hw->vif_data_size = sizeof(struct ath10k_vif);
ar->hw->sta_data_size = sizeof(struct ath10k_sta);
ar->hw->txq_data_size = sizeof(struct ath10k_txq);
ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
@ -7286,7 +7705,8 @@ int ath10k_mac_register(struct ath10k *ar)
ar->hw->wiphy->max_remain_on_channel_duration = 5000;
ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
ar->hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
ar->hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
NL80211_FEATURE_AP_SCAN;
ar->hw->wiphy->max_ap_assoc_sta = ar->max_num_stations;

View File

@ -75,6 +75,12 @@ void ath10k_mac_tx_unlock(struct ath10k *ar, int reason);
void ath10k_mac_vif_tx_lock(struct ath10k_vif *arvif, int reason);
void ath10k_mac_vif_tx_unlock(struct ath10k_vif *arvif, int reason);
bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar);
void ath10k_mac_tx_push_pending(struct ath10k *ar);
int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
struct ieee80211_txq *ath10k_mac_txq_lookup(struct ath10k *ar,
u16 peer_id,
u8 tid);
static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
{

View File

@ -809,7 +809,8 @@ static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe)
spin_lock_bh(&ar_pci->ce_lock);
num = __ath10k_ce_rx_num_free_bufs(ce_pipe);
spin_unlock_bh(&ar_pci->ce_lock);
while (num--) {
while (num >= 0) {
ret = __ath10k_pci_rx_post_buf(pipe);
if (ret) {
if (ret == -ENOSPC)
@ -819,6 +820,7 @@ static void ath10k_pci_rx_post_pipe(struct ath10k_pci_pipe *pipe)
ATH10K_PCI_RX_POST_RETRY_MS);
break;
}
num--;
}
}
@ -870,10 +872,8 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int ret = 0;
u32 buf;
u32 *buf;
unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
unsigned int id;
unsigned int flags;
struct ath10k_ce_pipe *ce_diag;
/* Host buffer address in CE space */
u32 ce_data;
@ -909,7 +909,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
nbytes = min_t(unsigned int, remaining_bytes,
DIAG_TRANSFER_LIMIT);
ret = __ath10k_ce_rx_post_buf(ce_diag, NULL, ce_data);
ret = __ath10k_ce_rx_post_buf(ce_diag, &ce_data, ce_data);
if (ret != 0)
goto done;
@ -940,9 +940,10 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
}
i = 0;
while (ath10k_ce_completed_recv_next_nolock(ce_diag, NULL, &buf,
&completed_nbytes,
&id, &flags) != 0) {
while (ath10k_ce_completed_recv_next_nolock(ce_diag,
(void **)&buf,
&completed_nbytes)
!= 0) {
mdelay(1);
if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
@ -956,7 +957,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
goto done;
}
if (buf != ce_data) {
if (*buf != ce_data) {
ret = -EIO;
goto done;
}
@ -1026,10 +1027,8 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int ret = 0;
u32 buf;
u32 *buf;
unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
unsigned int id;
unsigned int flags;
struct ath10k_ce_pipe *ce_diag;
void *data_buf = NULL;
u32 ce_data; /* Host buffer address in CE space */
@ -1078,7 +1077,7 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
nbytes = min_t(int, remaining_bytes, DIAG_TRANSFER_LIMIT);
/* Set up to receive directly into Target(!) address */
ret = __ath10k_ce_rx_post_buf(ce_diag, NULL, address);
ret = __ath10k_ce_rx_post_buf(ce_diag, &address, address);
if (ret != 0)
goto done;
@ -1103,9 +1102,10 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
}
i = 0;
while (ath10k_ce_completed_recv_next_nolock(ce_diag, NULL, &buf,
&completed_nbytes,
&id, &flags) != 0) {
while (ath10k_ce_completed_recv_next_nolock(ce_diag,
(void **)&buf,
&completed_nbytes)
!= 0) {
mdelay(1);
if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
@ -1119,7 +1119,7 @@ int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address,
goto done;
}
if (buf != address) {
if (*buf != address) {
ret = -EIO;
goto done;
}
@ -1181,15 +1181,11 @@ static void ath10k_pci_process_rx_cb(struct ath10k_ce_pipe *ce_state,
struct sk_buff *skb;
struct sk_buff_head list;
void *transfer_context;
u32 ce_data;
unsigned int nbytes, max_nbytes;
unsigned int transfer_id;
unsigned int flags;
__skb_queue_head_init(&list);
while (ath10k_ce_completed_recv_next(ce_state, &transfer_context,
&ce_data, &nbytes, &transfer_id,
&flags) == 0) {
&nbytes) == 0) {
skb = transfer_context;
max_nbytes = skb->len + skb_tailroom(skb);
dma_unmap_single(ar->dev, ATH10K_SKB_RXCB(skb)->paddr,
@ -1218,6 +1214,63 @@ static void ath10k_pci_process_rx_cb(struct ath10k_ce_pipe *ce_state,
ath10k_pci_rx_post_pipe(pipe_info);
}
static void ath10k_pci_process_htt_rx_cb(struct ath10k_ce_pipe *ce_state,
void (*callback)(struct ath10k *ar,
struct sk_buff *skb))
{
struct ath10k *ar = ce_state->ar;
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id];
struct ath10k_ce_pipe *ce_pipe = pipe_info->ce_hdl;
struct sk_buff *skb;
struct sk_buff_head list;
void *transfer_context;
unsigned int nbytes, max_nbytes, nentries;
int orig_len;
/* No need to aquire ce_lock for CE5, since this is the only place CE5
* is processed other than init and deinit. Before releasing CE5
* buffers, interrupts are disabled. Thus CE5 access is serialized.
*/
__skb_queue_head_init(&list);
while (ath10k_ce_completed_recv_next_nolock(ce_state, &transfer_context,
&nbytes) == 0) {
skb = transfer_context;
max_nbytes = skb->len + skb_tailroom(skb);
if (unlikely(max_nbytes < nbytes)) {
ath10k_warn(ar, "rxed more than expected (nbytes %d, max %d)",
nbytes, max_nbytes);
continue;
}
dma_sync_single_for_cpu(ar->dev, ATH10K_SKB_RXCB(skb)->paddr,
max_nbytes, DMA_FROM_DEVICE);
skb_put(skb, nbytes);
__skb_queue_tail(&list, skb);
}
nentries = skb_queue_len(&list);
while ((skb = __skb_dequeue(&list))) {
ath10k_dbg(ar, ATH10K_DBG_PCI, "pci rx ce pipe %d len %d\n",
ce_state->id, skb->len);
ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci rx: ",
skb->data, skb->len);
orig_len = skb->len;
callback(ar, skb);
skb_push(skb, orig_len - skb->len);
skb_reset_tail_pointer(skb);
skb_trim(skb, 0);
/*let device gain the buffer again*/
dma_sync_single_for_device(ar->dev, ATH10K_SKB_RXCB(skb)->paddr,
skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
}
ath10k_ce_rx_update_write_idx(ce_pipe, nentries);
}
/* Called by lower (CE) layer when data is received from the Target. */
static void ath10k_pci_htc_rx_cb(struct ath10k_ce_pipe *ce_state)
{
@ -1274,7 +1327,7 @@ static void ath10k_pci_htt_rx_cb(struct ath10k_ce_pipe *ce_state)
*/
ath10k_ce_per_engine_service(ce_state->ar, 4);
ath10k_pci_process_rx_cb(ce_state, ath10k_pci_htt_rx_deliver);
ath10k_pci_process_htt_rx_cb(ce_state, ath10k_pci_htt_rx_deliver);
}
int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
@ -1835,13 +1888,10 @@ static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state)
{
struct ath10k *ar = ce_state->ar;
struct bmi_xfer *xfer;
u32 ce_data;
unsigned int nbytes;
unsigned int transfer_id;
unsigned int flags;
if (ath10k_ce_completed_recv_next(ce_state, (void **)&xfer, &ce_data,
&nbytes, &transfer_id, &flags))
if (ath10k_ce_completed_recv_next(ce_state, (void **)&xfer,
&nbytes))
return;
if (WARN_ON_ONCE(!xfer))

View File

@ -49,25 +49,25 @@ out:
spin_unlock_bh(&ar->data_lock);
}
void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
const struct htt_tx_done *tx_done)
int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
const struct htt_tx_done *tx_done)
{
struct ath10k *ar = htt->ar;
struct device *dev = ar->dev;
struct ieee80211_tx_info *info;
struct ieee80211_txq *txq;
struct ath10k_skb_cb *skb_cb;
struct ath10k_txq *artxq;
struct sk_buff *msdu;
bool limit_mgmt_desc = false;
ath10k_dbg(ar, ATH10K_DBG_HTT,
"htt tx completion msdu_id %u discard %d no_ack %d success %d\n",
tx_done->msdu_id, !!tx_done->discard,
!!tx_done->no_ack, !!tx_done->success);
"htt tx completion msdu_id %u status %d\n",
tx_done->msdu_id, tx_done->status);
if (tx_done->msdu_id >= htt->max_num_pending_tx) {
ath10k_warn(ar, "warning: msdu_id %d too big, ignoring\n",
tx_done->msdu_id);
return;
return -EINVAL;
}
spin_lock_bh(&htt->tx_lock);
@ -76,17 +76,18 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
ath10k_warn(ar, "received tx completion for invalid msdu_id: %d\n",
tx_done->msdu_id);
spin_unlock_bh(&htt->tx_lock);
return;
return -ENOENT;
}
skb_cb = ATH10K_SKB_CB(msdu);
txq = skb_cb->txq;
artxq = (void *)txq->drv_priv;
if (unlikely(skb_cb->flags & ATH10K_SKB_F_MGMT) &&
ar->hw_params.max_probe_resp_desc_thres)
limit_mgmt_desc = true;
if (txq)
artxq->num_fw_queued--;
ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
__ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
ath10k_htt_tx_dec_pending(htt);
if (htt->num_pending_tx == 0)
wake_up(&htt->empty_tx_wq);
spin_unlock_bh(&htt->tx_lock);
@ -99,22 +100,24 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
memset(&info->status, 0, sizeof(info->status));
trace_ath10k_txrx_tx_unref(ar, tx_done->msdu_id);
if (tx_done->discard) {
if (tx_done->status == HTT_TX_COMPL_STATE_DISCARD) {
ieee80211_free_txskb(htt->ar->hw, msdu);
return;
return 0;
}
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
info->flags |= IEEE80211_TX_STAT_ACK;
if (tx_done->no_ack)
if (tx_done->status == HTT_TX_COMPL_STATE_NOACK)
info->flags &= ~IEEE80211_TX_STAT_ACK;
if (tx_done->success && (info->flags & IEEE80211_TX_CTL_NO_ACK))
if ((tx_done->status == HTT_TX_COMPL_STATE_ACK) &&
(info->flags & IEEE80211_TX_CTL_NO_ACK))
info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
ieee80211_tx_status(htt->ar->hw, msdu);
/* we do not own the msdu anymore */
return 0;
}
struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
@ -203,6 +206,7 @@ void ath10k_peer_map_event(struct ath10k_htt *htt,
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n",
ev->vdev_id, ev->addr, ev->peer_id);
ar->peer_map[ev->peer_id] = peer;
set_bit(ev->peer_id, peer->peer_ids);
exit:
spin_unlock_bh(&ar->data_lock);
@ -225,6 +229,7 @@ void ath10k_peer_unmap_event(struct ath10k_htt *htt,
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt peer unmap vdev %d peer %pM id %d\n",
peer->vdev_id, peer->addr, ev->peer_id);
ar->peer_map[ev->peer_id] = NULL;
clear_bit(ev->peer_id, peer->peer_ids);
if (bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS)) {

View File

@ -19,8 +19,8 @@
#include "htt.h"
void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
const struct htt_tx_done *tx_done);
int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
const struct htt_tx_done *tx_done);
struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
const u8 *addr);

View File

@ -186,6 +186,9 @@ struct wmi_ops {
u8 enable,
u32 detect_level,
u32 detect_margin);
struct sk_buff *(*ext_resource_config)(struct ath10k *ar,
enum wmi_host_platform_type type,
u32 fw_feature_bitmap);
int (*get_vdev_subtype)(struct ath10k *ar,
enum wmi_vdev_subtype subtype);
};
@ -1329,6 +1332,26 @@ ath10k_wmi_pdev_enable_adaptive_cca(struct ath10k *ar, u8 enable,
ar->wmi.cmd->pdev_enable_adaptive_cca_cmdid);
}
static inline int
ath10k_wmi_ext_resource_config(struct ath10k *ar,
enum wmi_host_platform_type type,
u32 fw_feature_bitmap)
{
struct sk_buff *skb;
if (!ar->wmi.ops->ext_resource_config)
return -EOPNOTSUPP;
skb = ar->wmi.ops->ext_resource_config(ar, type,
fw_feature_bitmap);
if (IS_ERR(skb))
return PTR_ERR(skb);
return ath10k_wmi_cmd_send(ar, skb,
ar->wmi.cmd->ext_resource_cfg_cmdid);
}
static inline int
ath10k_wmi_get_vdev_subtype(struct ath10k *ar, enum wmi_vdev_subtype subtype)
{

View File

@ -705,6 +705,7 @@ static struct wmi_cmd_map wmi_10_4_cmd_map = {
.set_cca_params_cmdid = WMI_10_4_SET_CCA_PARAMS_CMDID,
.pdev_bss_chan_info_request_cmdid =
WMI_10_4_PDEV_BSS_CHAN_INFO_REQUEST_CMDID,
.ext_resource_cfg_cmdid = WMI_10_4_EXT_RESOURCE_CFG_CMDID,
};
/* MAIN WMI VDEV param map */
@ -2099,34 +2100,6 @@ int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb)
return 0;
}
static inline enum ieee80211_band phy_mode_to_band(u32 phy_mode)
{
enum ieee80211_band band;
switch (phy_mode) {
case MODE_11A:
case MODE_11NA_HT20:
case MODE_11NA_HT40:
case MODE_11AC_VHT20:
case MODE_11AC_VHT40:
case MODE_11AC_VHT80:
band = IEEE80211_BAND_5GHZ;
break;
case MODE_11G:
case MODE_11B:
case MODE_11GONLY:
case MODE_11NG_HT20:
case MODE_11NG_HT40:
case MODE_11AC_VHT20_2G:
case MODE_11AC_VHT40_2G:
case MODE_11AC_VHT80_2G:
default:
band = IEEE80211_BAND_2GHZ;
}
return band;
}
/* If keys are configured, HW decrypts all frames
* with protected bit set. Mark such frames as decrypted.
*/
@ -2167,8 +2140,10 @@ static int ath10k_wmi_op_pull_mgmt_rx_ev(struct ath10k *ar, struct sk_buff *skb,
struct wmi_mgmt_rx_event_v1 *ev_v1;
struct wmi_mgmt_rx_event_v2 *ev_v2;
struct wmi_mgmt_rx_hdr_v1 *ev_hdr;
struct wmi_mgmt_rx_ext_info *ext_info;
size_t pull_len;
u32 msdu_len;
u32 len;
if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) {
ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data;
@ -2195,6 +2170,12 @@ static int ath10k_wmi_op_pull_mgmt_rx_ev(struct ath10k *ar, struct sk_buff *skb,
if (skb->len < msdu_len)
return -EPROTO;
if (le32_to_cpu(arg->status) & WMI_RX_STATUS_EXT_INFO) {
len = ALIGN(le32_to_cpu(arg->buf_len), 4);
ext_info = (struct wmi_mgmt_rx_ext_info *)(skb->data + len);
memcpy(&arg->ext_info, ext_info,
sizeof(struct wmi_mgmt_rx_ext_info));
}
/* the WMI buffer might've ended up being padded to 4 bytes due to HTC
* trailer with credit update. Trim the excess garbage.
*/
@ -2211,6 +2192,8 @@ static int ath10k_wmi_10_4_op_pull_mgmt_rx_ev(struct ath10k *ar,
struct wmi_10_4_mgmt_rx_hdr *ev_hdr;
size_t pull_len;
u32 msdu_len;
struct wmi_mgmt_rx_ext_info *ext_info;
u32 len;
ev = (struct wmi_10_4_mgmt_rx_event *)skb->data;
ev_hdr = &ev->hdr;
@ -2231,6 +2214,13 @@ static int ath10k_wmi_10_4_op_pull_mgmt_rx_ev(struct ath10k *ar,
if (skb->len < msdu_len)
return -EPROTO;
if (le32_to_cpu(arg->status) & WMI_RX_STATUS_EXT_INFO) {
len = ALIGN(le32_to_cpu(arg->buf_len), 4);
ext_info = (struct wmi_mgmt_rx_ext_info *)(skb->data + len);
memcpy(&arg->ext_info, ext_info,
sizeof(struct wmi_mgmt_rx_ext_info));
}
/* Make sure bytes added for padding are removed. */
skb_trim(skb, msdu_len);
@ -2281,6 +2271,11 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
if (rx_status & WMI_RX_STATUS_ERR_MIC)
status->flag |= RX_FLAG_MMIC_ERROR;
if (rx_status & WMI_RX_STATUS_EXT_INFO) {
status->mactime =
__le64_to_cpu(arg.ext_info.rx_mac_timestamp);
status->flag |= RX_FLAG_MACTIME_END;
}
/* Hardware can Rx CCK rates on 5GHz. In that case phy_mode is set to
* MODE_11B. This means phy_mode is not a reliable source for the band
* of mgmt rx.
@ -2310,6 +2305,12 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
hdr = (struct ieee80211_hdr *)skb->data;
fc = le16_to_cpu(hdr->frame_control);
/* Firmware is guaranteed to report all essential management frames via
* WMI while it can deliver some extra via HTT. Since there can be
* duplicates split the reporting wrt monitor/sniffing.
*/
status->flag |= RX_FLAG_SKIP_MONITOR;
ath10k_wmi_handle_wep_reauth(ar, skb, status);
/* FW delivers WEP Shared Auth frame with Protected Bit set and
@ -2612,6 +2613,16 @@ void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
dst->peer_tx_rate = __le32_to_cpu(src->peer_tx_rate);
}
static void
ath10k_wmi_10_4_pull_peer_stats(const struct wmi_10_4_peer_stats *src,
struct ath10k_fw_stats_peer *dst)
{
ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr);
dst->peer_rssi = __le32_to_cpu(src->peer_rssi);
dst->peer_tx_rate = __le32_to_cpu(src->peer_tx_rate);
dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
}
static int ath10k_wmi_main_op_pull_fw_stats(struct ath10k *ar,
struct sk_buff *skb,
struct ath10k_fw_stats *stats)
@ -2865,11 +2876,8 @@ static int ath10k_wmi_10_2_4_op_pull_fw_stats(struct ath10k *ar,
const struct wmi_10_2_4_ext_peer_stats *src;
struct ath10k_fw_stats_peer *dst;
int stats_len;
bool ext_peer_stats_support;
ext_peer_stats_support = test_bit(WMI_SERVICE_PEER_STATS,
ar->wmi.svc_map);
if (ext_peer_stats_support)
if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map))
stats_len = sizeof(struct wmi_10_2_4_ext_peer_stats);
else
stats_len = sizeof(struct wmi_10_2_4_peer_stats);
@ -2886,7 +2894,7 @@ static int ath10k_wmi_10_2_4_op_pull_fw_stats(struct ath10k *ar,
dst->peer_rx_rate = __le32_to_cpu(src->common.peer_rx_rate);
if (ext_peer_stats_support)
if (ath10k_peer_stats_enabled(ar))
dst->rx_duration = __le32_to_cpu(src->rx_duration);
/* FIXME: expose 10.2 specific values */
@ -2905,6 +2913,7 @@ static int ath10k_wmi_10_4_op_pull_fw_stats(struct ath10k *ar,
u32 num_pdev_ext_stats;
u32 num_vdev_stats;
u32 num_peer_stats;
u32 stats_id;
int i;
if (!skb_pull(skb, sizeof(*ev)))
@ -2914,6 +2923,7 @@ static int ath10k_wmi_10_4_op_pull_fw_stats(struct ath10k *ar,
num_pdev_ext_stats = __le32_to_cpu(ev->num_pdev_ext_stats);
num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
stats_id = __le32_to_cpu(ev->stats_id);
for (i = 0; i < num_pdev_stats; i++) {
const struct wmi_10_4_pdev_stats *src;
@ -2953,22 +2963,28 @@ static int ath10k_wmi_10_4_op_pull_fw_stats(struct ath10k *ar,
/* fw doesn't implement vdev stats */
for (i = 0; i < num_peer_stats; i++) {
const struct wmi_10_4_peer_stats *src;
const struct wmi_10_4_peer_extd_stats *src;
struct ath10k_fw_stats_peer *dst;
int stats_len;
bool extd_peer_stats = !!(stats_id & WMI_10_4_STAT_PEER_EXTD);
if (extd_peer_stats)
stats_len = sizeof(struct wmi_10_4_peer_extd_stats);
else
stats_len = sizeof(struct wmi_10_4_peer_stats);
src = (void *)skb->data;
if (!skb_pull(skb, sizeof(*src)))
if (!skb_pull(skb, stats_len))
return -EPROTO;
dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
if (!dst)
continue;
ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr);
dst->peer_rssi = __le32_to_cpu(src->peer_rssi);
dst->peer_tx_rate = __le32_to_cpu(src->peer_tx_rate);
dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
ath10k_wmi_10_4_pull_peer_stats(&src->common, dst);
/* FIXME: expose 10.4 specific values */
if (extd_peer_stats)
dst->rx_duration = __le32_to_cpu(src->rx_duration);
list_add_tail(&dst->list, &stats->peers);
}
@ -4617,10 +4633,16 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work)
}
if (test_bit(WMI_SERVICE_PEER_CACHING, ar->wmi.svc_map)) {
if (test_bit(ATH10K_FW_FEATURE_PEER_FLOW_CONTROL,
ar->fw_features))
ar->num_active_peers = TARGET_10_4_QCACHE_ACTIVE_PEERS_PFC +
ar->max_num_vdevs;
else
ar->num_active_peers = TARGET_10_4_QCACHE_ACTIVE_PEERS +
ar->max_num_vdevs;
ar->max_num_peers = TARGET_10_4_NUM_QCACHE_PEERS_MAX +
ar->max_num_vdevs;
ar->num_active_peers = ar->hw_params.qcache_active_peers +
ar->max_num_vdevs;
ar->num_tids = ar->num_active_peers * 2;
ar->max_num_stations = TARGET_10_4_NUM_QCACHE_PEERS_MAX;
}
@ -5517,7 +5539,8 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar)
config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS);
config.num_peer_keys = __cpu_to_le32(TARGET_10X_NUM_PEER_KEYS);
if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) {
if (ath10k_peer_stats_enabled(ar)) {
config.num_peers = __cpu_to_le32(TARGET_10X_TX_STATS_NUM_PEERS);
config.num_tids = __cpu_to_le32(TARGET_10X_TX_STATS_NUM_TIDS);
} else {
@ -5579,7 +5602,7 @@ static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar)
test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map))
features |= WMI_10_2_COEX_GPIO;
if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map))
if (ath10k_peer_stats_enabled(ar))
features |= WMI_10_2_PEER_STATS;
cmd->resource_config.feature_mask = __cpu_to_le32(features);
@ -7484,6 +7507,28 @@ static int ath10k_wmi_10_4_op_get_vdev_subtype(struct ath10k *ar,
return -ENOTSUPP;
}
static struct sk_buff *
ath10k_wmi_10_4_ext_resource_config(struct ath10k *ar,
enum wmi_host_platform_type type,
u32 fw_feature_bitmap)
{
struct wmi_ext_resource_config_10_4_cmd *cmd;
struct sk_buff *skb;
skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
if (!skb)
return ERR_PTR(-ENOMEM);
cmd = (struct wmi_ext_resource_config_10_4_cmd *)skb->data;
cmd->host_platform_config = __cpu_to_le32(type);
cmd->fw_feature_bitmap = __cpu_to_le32(fw_feature_bitmap);
ath10k_dbg(ar, ATH10K_DBG_WMI,
"wmi ext resource config host type %d firmware feature bitmap %08x\n",
type, fw_feature_bitmap);
return skb;
}
static const struct wmi_ops wmi_ops = {
.rx = ath10k_wmi_op_rx,
.map_svc = wmi_main_svc_map,
@ -7810,6 +7855,7 @@ static const struct wmi_ops wmi_10_4_ops = {
.gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp,
.gen_delba_send = ath10k_wmi_op_gen_delba_send,
.fw_stats_fill = ath10k_wmi_10_4_op_fw_stats_fill,
.ext_resource_config = ath10k_wmi_10_4_ext_resource_config,
/* shared with 10.2 */
.gen_request_stats = ath10k_wmi_op_gen_request_stats,

View File

@ -816,6 +816,7 @@ struct wmi_cmd_map {
u32 set_cca_params_cmdid;
u32 pdev_bss_chan_info_request_cmdid;
u32 pdev_enable_adaptive_cca_cmdid;
u32 ext_resource_cfg_cmdid;
};
/*
@ -2667,6 +2668,31 @@ struct wmi_resource_config_10_4 {
__le32 qwrap_config;
} __packed;
/**
* enum wmi_10_4_feature_mask - WMI 10.4 feature enable/disable flags
* @WMI_10_4_LTEU_SUPPORT: LTEU config
* @WMI_10_4_COEX_GPIO_SUPPORT: COEX GPIO config
* @WMI_10_4_AUX_RADIO_SPECTRAL_INTF: AUX Radio Enhancement for spectral scan
* @WMI_10_4_AUX_RADIO_CHAN_LOAD_INTF: AUX Radio Enhancement for chan load scan
* @WMI_10_4_BSS_CHANNEL_INFO_64: BSS channel info stats
* @WMI_10_4_PEER_STATS: Per station stats
*/
enum wmi_10_4_feature_mask {
WMI_10_4_LTEU_SUPPORT = BIT(0),
WMI_10_4_COEX_GPIO_SUPPORT = BIT(1),
WMI_10_4_AUX_RADIO_SPECTRAL_INTF = BIT(2),
WMI_10_4_AUX_RADIO_CHAN_LOAD_INTF = BIT(3),
WMI_10_4_BSS_CHANNEL_INFO_64 = BIT(4),
WMI_10_4_PEER_STATS = BIT(5),
};
struct wmi_ext_resource_config_10_4_cmd {
/* contains enum wmi_host_platform_type */
__le32 host_platform_config;
/* see enum wmi_10_4_feature_mask */
__le32 fw_feature_bitmap;
};
/* strucutre describing host memory chunk. */
struct host_memory_chunk {
/* id of the request that is passed up in service ready */
@ -3037,11 +3063,17 @@ struct wmi_10_4_mgmt_rx_event {
u8 buf[0];
} __packed;
struct wmi_mgmt_rx_ext_info {
__le64 rx_mac_timestamp;
} __packed __aligned(4);
#define WMI_RX_STATUS_OK 0x00
#define WMI_RX_STATUS_ERR_CRC 0x01
#define WMI_RX_STATUS_ERR_DECRYPT 0x08
#define WMI_RX_STATUS_ERR_MIC 0x10
#define WMI_RX_STATUS_ERR_KEY_CACHE_MISS 0x20
/* Extension data at the end of mgmt frame */
#define WMI_RX_STATUS_EXT_INFO 0x40
#define PHY_ERROR_GEN_SPECTRAL_SCAN 0x26
#define PHY_ERROR_GEN_FALSE_RADAR_EXT 0x24
@ -4072,6 +4104,13 @@ enum wmi_stats_id {
WMI_STAT_VDEV_RATE = BIT(5),
};
enum wmi_10_4_stats_id {
WMI_10_4_STAT_PEER = BIT(0),
WMI_10_4_STAT_AP = BIT(1),
WMI_10_4_STAT_INST = BIT(2),
WMI_10_4_STAT_PEER_EXTD = BIT(3),
};
struct wlan_inst_rssi_args {
__le16 cfg_retry_count;
__le16 retry_count;
@ -4271,6 +4310,15 @@ struct wmi_10_4_peer_stats {
__le32 peer_rssi_changed;
} __packed;
struct wmi_10_4_peer_extd_stats {
struct wmi_10_4_peer_stats common;
struct wmi_mac_addr peer_macaddr;
__le32 inactive_time;
__le32 peer_chain_rssi;
__le32 rx_duration;
__le32 reserved[10];
} __packed;
struct wmi_10_2_pdev_ext_stats {
__le32 rx_rssi_comb;
__le32 rx_rssi[4];
@ -6116,6 +6164,7 @@ struct wmi_mgmt_rx_ev_arg {
__le32 phy_mode;
__le32 buf_len;
__le32 status; /* %WMI_RX_STATUS_ */
struct wmi_mgmt_rx_ext_info ext_info;
};
struct wmi_ch_info_ev_arg {
@ -6401,6 +6450,11 @@ struct wmi_pdev_set_adaptive_cca_params {
__le32 cca_detect_margin;
} __packed;
enum wmi_host_platform_type {
WMI_HOST_PLATFORM_HIGH_PERF,
WMI_HOST_PLATFORM_LOW_PERF,
};
struct ath10k;
struct ath10k_vif;
struct ath10k_fw_stats_pdev;

View File

@ -77,7 +77,7 @@ static const struct pci_device_id ath5k_led_devices[] = {
/* HP Compaq CQ60-206US (ddreggors@jumptv.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137a), ATH_LED(3, 1) },
/* HP Compaq C700 (nitrousnrg@gmail.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 1) },
{ ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 0) },
/* LiteOn AR5BXB63 (magooz@salug.it) */
{ ATH_SDEVICE(PCI_VENDOR_ID_ATHEROS, 0x3067), ATH_LED(3, 0) },
/* IBM-specific AR5212 (all others) */

View File

@ -1446,7 +1446,7 @@ ath5k_hw_channel(struct ath5k_hw *ah,
"channel frequency (%u MHz) out of supported "
"band range\n",
channel->center_freq);
return -EINVAL;
return -EINVAL;
}
/*

View File

@ -634,7 +634,7 @@ ath5k_hw_on_hold(struct ath5k_hw *ah)
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA |
AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI);
usleep_range(2000, 2500);
usleep_range(2000, 2500);
} else {
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
AR5K_RESET_CTL_BASEBAND | bus_flags);
@ -699,7 +699,7 @@ ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel)
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA |
AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI);
usleep_range(2000, 2500);
usleep_range(2000, 2500);
} else {
if (ath5k_get_bus_type(ah) == ATH_AHB)
ret = ath5k_hw_wisoc_reset(ah, AR5K_RESET_CTL_PCU |

View File

@ -1584,6 +1584,11 @@ static int ath6kl_wmi_txe_notify_event_rx(struct wmi *wmi, u8 *datap, int len,
if (len < sizeof(*ev))
return -EINVAL;
if (vif->nw_type != INFRA_NETWORK ||
!test_bit(ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY,
vif->ar->fw_capabilities))
return -EOPNOTSUPP;
if (vif->sme_state != SME_CONNECTED)
return -ENOTCONN;

View File

@ -331,7 +331,7 @@ static const u32 ar9300_2p2_baseband_postamble[][5] = {
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946220, 0xcf946220},
{0x00009e44, 0x02321e27, 0x02321e27, 0x02291e27, 0x02291e27},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@ -351,7 +351,7 @@ static const u32 ar9300_2p2_baseband_postamble[][5] = {
{0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
{0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
{0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
{0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
{0x0000a2d0, 0x00041983, 0x00041983, 0x00041981, 0x00041982},
{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
{0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},

View File

@ -1203,12 +1203,12 @@ static void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah)
static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g)
{
int offset[8] = {0}, total = 0, test;
int agc_out, i, peak_detect_threshold;
int agc_out, i, peak_detect_threshold = 0;
if (AR_SREV_9550(ah) || AR_SREV_9531(ah))
peak_detect_threshold = 8;
else
peak_detect_threshold = 0;
else if (AR_SREV_9561(ah))
peak_detect_threshold = 11;
/*
* Turn off LNA/SW.
@ -1249,17 +1249,14 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g)
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, 0x0);
if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) ||
AR_SREV_9561(ah)) {
if (is_2g)
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR,
peak_detect_threshold);
else
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR,
peak_detect_threshold);
}
if (is_2g)
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR,
peak_detect_threshold);
else
REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain),
AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR,
peak_detect_threshold);
for (i = 6; i > 0; i--) {
offset[i] = BIT(i - 1);
@ -1311,9 +1308,6 @@ static void ar9003_hw_do_pcoem_manual_peak_cal(struct ath_hw *ah,
struct ath9k_hw_cal_data *caldata = ah->caldata;
int i;
if (!AR_SREV_9462(ah) && !AR_SREV_9565(ah) && !AR_SREV_9485(ah))
return;
if ((ah->caps.hw_caps & ATH9K_HW_CAP_RTT) && !run_rtt_cal)
return;
@ -1641,14 +1635,12 @@ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
skip_tx_iqcal:
if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) {
if (AR_SREV_9330_11(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah) ||
AR_SREV_9561(ah)) {
for (i = 0; i < AR9300_MAX_CHAINS; i++) {
if (!(ah->rxchainmask & (1 << i)))
continue;
ar9003_hw_manual_peak_cal(ah, i,
IS_CHAN_2GHZ(chan));
}
for (i = 0; i < AR9300_MAX_CHAINS; i++) {
if (!(ah->rxchainmask & (1 << i)))
continue;
ar9003_hw_manual_peak_cal(ah, i,
IS_CHAN_2GHZ(chan));
}
/*
@ -1709,7 +1701,7 @@ void ar9003_hw_attach_calib_ops(struct ath_hw *ah)
struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
struct ath_hw_ops *ops = ath9k_hw_ops(ah);
if (AR_SREV_9485(ah) || AR_SREV_9462(ah) || AR_SREV_9565(ah))
if (AR_SREV_9003_PCOEM(ah))
priv_ops->init_cal = ar9003_hw_init_cal_pcoem;
else
priv_ops->init_cal = ar9003_hw_init_cal_soc;

View File

@ -3590,8 +3590,8 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
else
gpio = AR9300_EXT_LNA_CTL_GPIO_AR9485;
ath9k_hw_cfg_output(ah, gpio,
AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED);
ath9k_hw_gpio_request_out(ah, gpio, NULL,
AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED);
}
value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz);

View File

@ -427,21 +427,34 @@ static void ar9003_mci_observation_set_up(struct ath_hw *ah)
struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
if (mci->config & ATH_MCI_CONFIG_MCI_OBS_MCI) {
ath9k_hw_cfg_output(ah, 3, AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_DATA);
ath9k_hw_cfg_output(ah, 2, AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK);
ath9k_hw_cfg_output(ah, 1, AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA);
ath9k_hw_cfg_output(ah, 0, AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK);
ath9k_hw_gpio_request_out(ah, 3, NULL,
AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_DATA);
ath9k_hw_gpio_request_out(ah, 2, NULL,
AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK);
ath9k_hw_gpio_request_out(ah, 1, NULL,
AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA);
ath9k_hw_gpio_request_out(ah, 0, NULL,
AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK);
} else if (mci->config & ATH_MCI_CONFIG_MCI_OBS_TXRX) {
ath9k_hw_cfg_output(ah, 3, AR_GPIO_OUTPUT_MUX_AS_WL_IN_TX);
ath9k_hw_cfg_output(ah, 2, AR_GPIO_OUTPUT_MUX_AS_WL_IN_RX);
ath9k_hw_cfg_output(ah, 1, AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX);
ath9k_hw_cfg_output(ah, 0, AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX);
ath9k_hw_cfg_output(ah, 5, AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_gpio_request_out(ah, 3, NULL,
AR_GPIO_OUTPUT_MUX_AS_WL_IN_TX);
ath9k_hw_gpio_request_out(ah, 2, NULL,
AR_GPIO_OUTPUT_MUX_AS_WL_IN_RX);
ath9k_hw_gpio_request_out(ah, 1, NULL,
AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX);
ath9k_hw_gpio_request_out(ah, 0, NULL,
AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX);
ath9k_hw_gpio_request_out(ah, 5, NULL,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
} else if (mci->config & ATH_MCI_CONFIG_MCI_OBS_BT) {
ath9k_hw_cfg_output(ah, 3, AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX);
ath9k_hw_cfg_output(ah, 2, AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX);
ath9k_hw_cfg_output(ah, 1, AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA);
ath9k_hw_cfg_output(ah, 0, AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK);
ath9k_hw_gpio_request_out(ah, 3, NULL,
AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX);
ath9k_hw_gpio_request_out(ah, 2, NULL,
AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX);
ath9k_hw_gpio_request_out(ah, 1, NULL,
AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA);
ath9k_hw_gpio_request_out(ah, 0, NULL,
AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK);
} else
return;

View File

@ -1337,11 +1337,11 @@ skip_ws_det:
chan->channel,
aniState->mrcCCK ? "on" : "off",
is_on ? "on" : "off");
if (is_on)
ah->stats.ast_ani_ccklow++;
else
ah->stats.ast_ani_cckhigh++;
aniState->mrcCCK = is_on;
if (is_on)
ah->stats.ast_ani_ccklow++;
else
ah->stats.ast_ani_cckhigh++;
aniState->mrcCCK = is_on;
}
break;
}

View File

@ -40,7 +40,7 @@ static const u32 ar9331_1p1_baseband_postamble[][5] = {
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00003221, 0x00003221},
{0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946222, 0xcf946222},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946220, 0xcf946220},
{0x00009e44, 0x02321e27, 0x02321e27, 0x02282324, 0x02282324},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@ -59,7 +59,7 @@ static const u32 ar9331_1p1_baseband_postamble[][5] = {
{0x0000a284, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00058d18, 0x00058d18},
{0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
{0x0000a2d0, 0x00071982, 0x00071982, 0x00071982, 0x00071982},
{0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a},
{0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},

View File

@ -345,7 +345,7 @@ static const u32 ar9331_1p2_baseband_postamble[][5] = {
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00003221, 0x00003221},
{0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946222, 0xcf946222},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946220, 0xcf946220},
{0x00009e44, 0x02321e27, 0x02321e27, 0x02282324, 0x02282324},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@ -364,7 +364,7 @@ static const u32 ar9331_1p2_baseband_postamble[][5] = {
{0x0000a284, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
{0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
{0x0000a2d0, 0x00071981, 0x00071981, 0x00071981, 0x00071981},
{0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a},
{0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},

View File

@ -245,7 +245,7 @@ static const u32 ar9340_1p0_baseband_postamble[][5] = {
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946220, 0xcf946220},
{0x00009e44, 0x02321e27, 0x02321e27, 0x02291e27, 0x02291e27},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@ -265,7 +265,7 @@ static const u32 ar9340_1p0_baseband_postamble[][5] = {
{0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
{0x0000a288, 0x00000220, 0x00000220, 0x00000110, 0x00000110},
{0x0000a28c, 0x00011111, 0x00011111, 0x00022222, 0x00022222},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
{0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
{0x0000a2d0, 0x00041983, 0x00041983, 0x00041982, 0x00041982},
{0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a},
{0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},

View File

@ -59,7 +59,7 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = {
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003a5, 0x000003a5, 0x000003a5, 0x000003a5},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c780, 0xcfd5c280},
{0x00009e44, 0x62321e27, 0x62321e27, 0xfe291e27, 0xfe291e27},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@ -79,7 +79,7 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = {
{0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
{0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
{0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
{0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
{0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982},
{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
{0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},

View File

@ -239,7 +239,7 @@ static const u32 ar9462_2p1_baseband_postamble[][5] = {
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003a5, 0x000003a5, 0x000003a5, 0x000003a5},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c780, 0xcfd5c280},
{0x00009e44, 0x62321e27, 0x62321e27, 0xfe291e27, 0xfe291e27},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@ -259,7 +259,7 @@ static const u32 ar9462_2p1_baseband_postamble[][5] = {
{0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
{0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
{0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
{0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
{0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982},
{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
{0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},

View File

@ -1026,7 +1026,7 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = {
{0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec80d2e, 0x7ec80d2e},
{0x00009e14, 0x31395d53, 0x31396053, 0x312e6053, 0x312e5d53},
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946220, 0xcf946220},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
{0x0000a204, 0x01303fc0, 0x01303fc4, 0x01303fc4, 0x01303fc0},
@ -1044,7 +1044,7 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = {
{0x0000a284, 0x00000000, 0x00000000, 0x000002a0, 0x000002a0},
{0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
{0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
{0x0000a2d0, 0x00071981, 0x00071981, 0x00071982, 0x00071982},
{0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a},
{0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},

View File

@ -988,7 +988,7 @@ static const u32 qca953x_2p0_baseband_postamble[][5] = {
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcf946222, 0xcf946222},
{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcf946220, 0xcf946220},
{0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@ -1008,7 +1008,7 @@ static const u32 qca953x_2p0_baseband_postamble[][5] = {
{0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
{0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
{0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00058d18, 0x00058d18},
{0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33},
{0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982},
{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},

View File

@ -83,7 +83,7 @@ static const u32 ar955x_1p0_baseband_postamble[][5] = {
{0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
{0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
{0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00058d18, 0x00058d18},
{0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
{0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33},
{0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982},
{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},

View File

@ -347,7 +347,7 @@ static const u32 ar9565_1p0_baseband_postamble[][5] = {
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003b5, 0x000003b5, 0x000003a4, 0x000003a4},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
{0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946220, 0xcf946220},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946220, 0xcf946220},
{0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},

View File

@ -220,7 +220,7 @@ static const u32 qca956x_1p0_baseband_postamble[][5] = {
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003b5, 0x000003b5, 0x000003a6, 0x000003a6},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcf946222, 0xcf946222},
{0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcf946220, 0xcf946220},
{0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},

View File

@ -1290,7 +1290,7 @@ static const u32 ar9580_1p0_baseband_postamble[][5] = {
{0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
{0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
{0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222},
{0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946220, 0xcf946220},
{0x00009e44, 0x02321e27, 0x02321e27, 0x02291e27, 0x02291e27},
{0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
{0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
@ -1310,7 +1310,7 @@ static const u32 ar9580_1p0_baseband_postamble[][5] = {
{0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150},
{0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
{0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
{0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
{0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18},
{0x0000a2d0, 0x00041983, 0x00041983, 0x00041981, 0x00041982},
{0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
{0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},

View File

@ -813,7 +813,6 @@ static inline int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
#ifdef CONFIG_MAC80211_LEDS
void ath_init_leds(struct ath_softc *sc);
void ath_deinit_leds(struct ath_softc *sc);
void ath_fill_led_pin(struct ath_softc *sc);
#else
static inline void ath_init_leds(struct ath_softc *sc)
{
@ -822,9 +821,6 @@ static inline void ath_init_leds(struct ath_softc *sc)
static inline void ath_deinit_leds(struct ath_softc *sc)
{
}
static inline void ath_fill_led_pin(struct ath_softc *sc)
{
}
#endif
/************************/

View File

@ -15,6 +15,8 @@
*/
#include <linux/export.h>
#include <linux/types.h>
#include <linux/ath9k_platform.h>
#include "hw.h"
enum ath_bt_mode {
@ -34,6 +36,8 @@ struct ath_btcoex_config {
u8 bt_priority_time;
u8 bt_first_slot_time;
bool bt_hold_rx_clear;
u8 wl_active_time;
u8 wl_qc_time;
};
static const u32 ar9003_wlan_weights[ATH_BTCOEX_STOMP_MAX]
@ -65,31 +69,71 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum)
.bt_priority_time = 2,
.bt_first_slot_time = 5,
.bt_hold_rx_clear = true,
.wl_active_time = 0x20,
.wl_qc_time = 0x20,
};
bool rxclear_polarity = ath_bt_config.bt_rxclear_polarity;
u8 time_extend = ath_bt_config.bt_time_extend;
u8 first_slot_time = ath_bt_config.bt_first_slot_time;
if (AR_SREV_9300_20_OR_LATER(ah))
rxclear_polarity = !ath_bt_config.bt_rxclear_polarity;
if (AR_SREV_SOC(ah)) {
first_slot_time = 0x1d;
time_extend = 0xa;
btcoex_hw->bt_coex_mode3 =
SM(ath_bt_config.wl_active_time, AR_BT_WL_ACTIVE_TIME) |
SM(ath_bt_config.wl_qc_time, AR_BT_WL_QC_TIME);
btcoex_hw->bt_coex_mode2 =
AR_BT_PROTECT_BT_AFTER_WAKEUP |
AR_BT_PHY_ERR_BT_COLL_ENABLE;
}
btcoex_hw->bt_coex_mode =
(btcoex_hw->bt_coex_mode & AR_BT_QCU_THRESH) |
SM(ath_bt_config.bt_time_extend, AR_BT_TIME_EXTEND) |
SM(time_extend, AR_BT_TIME_EXTEND) |
SM(ath_bt_config.bt_txstate_extend, AR_BT_TXSTATE_EXTEND) |
SM(ath_bt_config.bt_txframe_extend, AR_BT_TX_FRAME_EXTEND) |
SM(ath_bt_config.bt_mode, AR_BT_MODE) |
SM(ath_bt_config.bt_quiet_collision, AR_BT_QUIET) |
SM(rxclear_polarity, AR_BT_RX_CLEAR_POLARITY) |
SM(ath_bt_config.bt_priority_time, AR_BT_PRIORITY_TIME) |
SM(ath_bt_config.bt_first_slot_time, AR_BT_FIRST_SLOT_TIME) |
SM(first_slot_time, AR_BT_FIRST_SLOT_TIME) |
SM(qnum, AR_BT_QCU_THRESH);
btcoex_hw->bt_coex_mode2 =
btcoex_hw->bt_coex_mode2 |=
SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) |
SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) |
AR_BT_DISABLE_BT_ANT;
}
EXPORT_SYMBOL(ath9k_hw_init_btcoex_hw);
static void ath9k_hw_btcoex_pin_init(struct ath_hw *ah, u8 wlanactive_gpio,
u8 btactive_gpio, u8 btpriority_gpio)
{
struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
struct ath9k_platform_data *pdata = ah->dev->platform_data;
if (btcoex_hw->scheme != ATH_BTCOEX_CFG_2WIRE &&
btcoex_hw->scheme != ATH_BTCOEX_CFG_3WIRE)
return;
/* bt priority GPIO will be ignored by 2 wire scheme */
if (pdata && (pdata->bt_active_pin || pdata->bt_priority_pin ||
pdata->wlan_active_pin)) {
btcoex_hw->btactive_gpio = pdata->bt_active_pin;
btcoex_hw->wlanactive_gpio = pdata->wlan_active_pin;
btcoex_hw->btpriority_gpio = pdata->bt_priority_pin;
} else {
btcoex_hw->btactive_gpio = btactive_gpio;
btcoex_hw->wlanactive_gpio = wlanactive_gpio;
btcoex_hw->btpriority_gpio = btpriority_gpio;
}
}
void ath9k_hw_btcoex_init_scheme(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
@ -107,19 +151,19 @@ void ath9k_hw_btcoex_init_scheme(struct ath_hw *ah)
btcoex_hw->scheme = ATH_BTCOEX_CFG_MCI;
} else if (AR_SREV_9300_20_OR_LATER(ah)) {
btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE;
btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9300;
btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9300;
btcoex_hw->btpriority_gpio = ATH_BTPRIORITY_GPIO_9300;
} else if (AR_SREV_9280_20_OR_LATER(ah)) {
btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9280;
btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9280;
if (AR_SREV_9285(ah)) {
ath9k_hw_btcoex_pin_init(ah, ATH_WLANACTIVE_GPIO_9300,
ATH_BTACTIVE_GPIO_9300,
ATH_BTPRIORITY_GPIO_9300);
} else if (AR_SREV_9280_20_OR_LATER(ah)) {
if (AR_SREV_9285(ah))
btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE;
btcoex_hw->btpriority_gpio = ATH_BTPRIORITY_GPIO_9285;
} else {
else
btcoex_hw->scheme = ATH_BTCOEX_CFG_2WIRE;
}
ath9k_hw_btcoex_pin_init(ah, ATH_WLANACTIVE_GPIO_9280,
ATH_BTACTIVE_GPIO_9280,
ATH_BTPRIORITY_GPIO_9285);
}
}
EXPORT_SYMBOL(ath9k_hw_btcoex_init_scheme);
@ -137,12 +181,14 @@ void ath9k_hw_btcoex_init_2wire(struct ath_hw *ah)
AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB);
/* Set input mux for bt_active to gpio pin */
REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
AR_GPIO_INPUT_MUX1_BT_ACTIVE,
btcoex_hw->btactive_gpio);
if (!AR_SREV_SOC(ah))
REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
AR_GPIO_INPUT_MUX1_BT_ACTIVE,
btcoex_hw->btactive_gpio);
/* Configure the desired gpio port for input */
ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btactive_gpio);
ath9k_hw_gpio_request_in(ah, btcoex_hw->btactive_gpio,
"ath9k-btactive");
}
EXPORT_SYMBOL(ath9k_hw_btcoex_init_2wire);
@ -157,21 +203,33 @@ void ath9k_hw_btcoex_init_3wire(struct ath_hw *ah)
/* Set input mux for bt_prority_async and
* bt_active_async to GPIO pins */
REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
AR_GPIO_INPUT_MUX1_BT_ACTIVE,
btcoex_hw->btactive_gpio);
REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
AR_GPIO_INPUT_MUX1_BT_PRIORITY,
btcoex_hw->btpriority_gpio);
if (!AR_SREV_SOC(ah)) {
REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
AR_GPIO_INPUT_MUX1_BT_ACTIVE,
btcoex_hw->btactive_gpio);
REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1,
AR_GPIO_INPUT_MUX1_BT_PRIORITY,
btcoex_hw->btpriority_gpio);
}
/* Configure the desired GPIO ports for input */
ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btactive_gpio);
ath9k_hw_cfg_gpio_input(ah, btcoex_hw->btpriority_gpio);
ath9k_hw_gpio_request_in(ah, btcoex_hw->btactive_gpio,
"ath9k-btactive");
ath9k_hw_gpio_request_in(ah, btcoex_hw->btpriority_gpio,
"ath9k-btpriority");
}
EXPORT_SYMBOL(ath9k_hw_btcoex_init_3wire);
void ath9k_hw_btcoex_deinit(struct ath_hw *ah)
{
struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
ath9k_hw_gpio_free(ah, btcoex_hw->btactive_gpio);
ath9k_hw_gpio_free(ah, btcoex_hw->btpriority_gpio);
ath9k_hw_gpio_free(ah, btcoex_hw->wlanactive_gpio);
}
EXPORT_SYMBOL(ath9k_hw_btcoex_deinit);
void ath9k_hw_btcoex_init_mci(struct ath_hw *ah)
{
ah->btcoex_hw.mci.ready = false;
@ -201,8 +259,9 @@ static void ath9k_hw_btcoex_enable_2wire(struct ath_hw *ah)
struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw;
/* Configure the desired GPIO port for TX_FRAME output */
ath9k_hw_cfg_output(ah, btcoex_hw->wlanactive_gpio,
AR_GPIO_OUTPUT_MUX_AS_TX_FRAME);
ath9k_hw_gpio_request_out(ah, btcoex_hw->wlanactive_gpio,
"ath9k-wlanactive",
AR_GPIO_OUTPUT_MUX_AS_TX_FRAME);
}
/*
@ -247,13 +306,13 @@ void ath9k_hw_btcoex_set_weight(struct ath_hw *ah,
txprio_shift[i-1]);
}
}
/* Last WLAN weight has to be adjusted wrt tx priority */
if (concur_tx) {
btcoex_hw->wlan_weight[i-1] &= ~(0xff << txprio_shift[i-1]);
btcoex_hw->wlan_weight[i-1] |= (btcoex_hw->tx_prio[stomp_type]
<< txprio_shift[i-1]);
}
}
EXPORT_SYMBOL(ath9k_hw_btcoex_set_weight);
@ -268,9 +327,14 @@ static void ath9k_hw_btcoex_enable_3wire(struct ath_hw *ah)
* Program coex mode and weight registers to
* enable coex 3-wire
*/
if (AR_SREV_SOC(ah))
REG_CLR_BIT(ah, AR_BT_COEX_MODE2, AR_BT_PHY_ERR_BT_COLL_ENABLE);
REG_WRITE(ah, AR_BT_COEX_MODE, btcoex->bt_coex_mode);
REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex->bt_coex_mode2);
if (AR_SREV_SOC(ah))
REG_WRITE(ah, AR_BT_COEX_MODE3, btcoex->bt_coex_mode3);
if (AR_SREV_9300_20_OR_LATER(ah)) {
REG_WRITE(ah, AR_BT_COEX_WL_WEIGHTS0, btcoex->wlan_weight[0]);
@ -281,8 +345,6 @@ static void ath9k_hw_btcoex_enable_3wire(struct ath_hw *ah)
} else
REG_WRITE(ah, AR_BT_COEX_WEIGHT, btcoex->bt_coex_weights);
if (AR_SREV_9271(ah)) {
val = REG_READ(ah, 0x50040);
val &= 0xFFFFFEFF;
@ -292,8 +354,9 @@ static void ath9k_hw_btcoex_enable_3wire(struct ath_hw *ah)
REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1);
REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0);
ath9k_hw_cfg_output(ah, btcoex->wlanactive_gpio,
AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL);
ath9k_hw_gpio_request_out(ah, btcoex->wlanactive_gpio,
"ath9k-wlanactive",
AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL);
}
static void ath9k_hw_btcoex_enable_mci(struct ath_hw *ah)
@ -339,7 +402,8 @@ void ath9k_hw_btcoex_enable(struct ath_hw *ah)
break;
}
if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_MCI) {
if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_MCI &&
!AR_SREV_SOC(ah)) {
REG_RMW(ah, AR_GPIO_PDPU,
(0x2 << (btcoex_hw->btactive_gpio * 2)),
(0x3 << (btcoex_hw->btactive_gpio * 2)));
@ -364,8 +428,8 @@ void ath9k_hw_btcoex_disable(struct ath_hw *ah)
if (!AR_SREV_9300_20_OR_LATER(ah))
ath9k_hw_set_gpio(ah, btcoex_hw->wlanactive_gpio, 0);
ath9k_hw_cfg_output(ah, btcoex_hw->wlanactive_gpio,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_gpio_request_out(ah, btcoex_hw->wlanactive_gpio,
NULL, AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
if (btcoex_hw->scheme == ATH_BTCOEX_CFG_3WIRE) {
REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE);

View File

@ -115,6 +115,7 @@ struct ath_btcoex_hw {
u32 bt_coex_mode; /* Register setting for AR_BT_COEX_MODE */
u32 bt_coex_weights; /* Register setting for AR_BT_COEX_WEIGHT */
u32 bt_coex_mode2; /* Register setting for AR_BT_COEX_MODE2 */
u32 bt_coex_mode3; /* Register setting for AR_BT_COEX_MODE3 */
u32 bt_weight[AR9300_NUM_BT_WEIGHTS];
u32 wlan_weight[AR9300_NUM_WLAN_WEIGHTS];
u8 tx_prio[ATH_BTCOEX_STOMP_MAX];
@ -123,6 +124,7 @@ struct ath_btcoex_hw {
void ath9k_hw_btcoex_init_scheme(struct ath_hw *ah);
void ath9k_hw_btcoex_init_2wire(struct ath_hw *ah);
void ath9k_hw_btcoex_init_3wire(struct ath_hw *ah);
void ath9k_hw_btcoex_deinit(struct ath_hw *ah);
void ath9k_hw_btcoex_init_mci(struct ath_hw *ah);
void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum);
void ath9k_hw_btcoex_set_weight(struct ath_hw *ah,

View File

@ -916,10 +916,21 @@ static int open_file_regdump(struct inode *inode, struct file *file)
struct ath_softc *sc = inode->i_private;
unsigned int len = 0;
u8 *buf;
int i;
int i, j = 0;
unsigned long num_regs, regdump_len, max_reg_offset;
const struct reg_hole {
u32 start;
u32 end;
} reg_hole_list[] = {
{0x0200, 0x07fc},
{0x0c00, 0x0ffc},
{0x2000, 0x3ffc},
{0x4100, 0x6ffc},
{0x705c, 0x7ffc},
{0x0000, 0x0000}
};
max_reg_offset = AR_SREV_9300_20_OR_LATER(sc->sc_ah) ? 0x16bd4 : 0xb500;
max_reg_offset = AR_SREV_9300_20_OR_LATER(sc->sc_ah) ? 0x8800 : 0xb500;
num_regs = max_reg_offset / 4 + 1;
regdump_len = num_regs * REGDUMP_LINE_SIZE + 1;
buf = vmalloc(regdump_len);
@ -927,9 +938,16 @@ static int open_file_regdump(struct inode *inode, struct file *file)
return -ENOMEM;
ath9k_ps_wakeup(sc);
for (i = 0; i < num_regs; i++)
for (i = 0; i < num_regs; i++) {
if (reg_hole_list[j].start == i << 2) {
i = reg_hole_list[j].end >> 2;
j++;
continue;
}
len += scnprintf(buf + len, regdump_len - len,
"0x%06x 0x%08x\n", i << 2, REG_READ(sc->sc_ah, i << 2));
}
ath9k_ps_restore(sc);
file->private_data = buf;

View File

@ -21,6 +21,33 @@
/********************************/
#ifdef CONFIG_MAC80211_LEDS
void ath_fill_led_pin(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
/* Set default led pin if invalid */
if (ah->led_pin < 0) {
if (AR_SREV_9287(ah))
ah->led_pin = ATH_LED_PIN_9287;
else if (AR_SREV_9485(ah))
ah->led_pin = ATH_LED_PIN_9485;
else if (AR_SREV_9300(ah))
ah->led_pin = ATH_LED_PIN_9300;
else if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
ah->led_pin = ATH_LED_PIN_9462;
else
ah->led_pin = ATH_LED_PIN_DEF;
}
/* Configure gpio for output */
ath9k_hw_gpio_request_out(ah, ah->led_pin, "ath9k-led",
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
/* LED off, active low */
ath9k_hw_set_gpio(ah, ah->led_pin, ah->config.led_active_high ? 0 : 1);
}
static void ath_led_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
@ -40,6 +67,8 @@ void ath_deinit_leds(struct ath_softc *sc)
ath_led_brightness(&sc->led_cdev, LED_OFF);
led_classdev_unregister(&sc->led_cdev);
ath9k_hw_gpio_free(sc->sc_ah, sc->sc_ah->led_pin);
}
void ath_init_leds(struct ath_softc *sc)
@ -49,6 +78,8 @@ void ath_init_leds(struct ath_softc *sc)
if (AR_SREV_9100(sc->sc_ah))
return;
ath_fill_led_pin(sc);
if (!ath9k_led_blink)
sc->led_cdev.default_trigger =
ieee80211_get_radio_led_name(sc->hw);
@ -64,37 +95,6 @@ void ath_init_leds(struct ath_softc *sc)
sc->led_registered = true;
}
void ath_fill_led_pin(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
if (AR_SREV_9100(ah))
return;
if (ah->led_pin >= 0) {
if (!((1 << ah->led_pin) & AR_GPIO_OE_OUT_MASK))
ath9k_hw_request_gpio(ah, ah->led_pin, "ath9k-led");
return;
}
if (AR_SREV_9287(ah))
ah->led_pin = ATH_LED_PIN_9287;
else if (AR_SREV_9485(sc->sc_ah))
ah->led_pin = ATH_LED_PIN_9485;
else if (AR_SREV_9300(sc->sc_ah))
ah->led_pin = ATH_LED_PIN_9300;
else if (AR_SREV_9462(sc->sc_ah) || AR_SREV_9565(sc->sc_ah))
ah->led_pin = ATH_LED_PIN_9462;
else
ah->led_pin = ATH_LED_PIN_DEF;
/* Configure gpio 1 for output */
ath9k_hw_cfg_output(ah, ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
/* LED off, active low */
ath9k_hw_set_gpio(ah, ah->led_pin, (ah->config.led_active_high) ? 0 : 1);
}
#endif
/*******************/
@ -402,6 +402,13 @@ void ath9k_deinit_btcoex(struct ath_softc *sc)
if (ath9k_hw_mci_is_enabled(ah))
ath_mci_cleanup(sc);
else {
enum ath_btcoex_scheme scheme = ath9k_hw_get_btcoex_scheme(ah);
if (scheme == ATH_BTCOEX_CFG_2WIRE ||
scheme == ATH_BTCOEX_CFG_3WIRE)
ath9k_hw_btcoex_deinit(sc->sc_ah);
}
}
int ath9k_init_btcoex(struct ath_softc *sc)

View File

@ -253,17 +253,19 @@ void ath9k_deinit_leds(struct ath9k_htc_priv *priv)
ath9k_led_brightness(&priv->led_cdev, LED_OFF);
led_classdev_unregister(&priv->led_cdev);
cancel_work_sync(&priv->led_work);
ath9k_hw_gpio_free(priv->ah, priv->ah->led_pin);
}
void ath9k_configure_leds(struct ath9k_htc_priv *priv)
{
/* Configure gpio 1 for output */
ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_gpio_request_out(priv->ah, priv->ah->led_pin,
"ath9k-led",
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
/* LED off, active low */
ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1);
}
void ath9k_init_leds(struct ath9k_htc_priv *priv)

View File

@ -262,11 +262,11 @@ static void ath9k_multi_regread(void *hw_priv, u32 *addr,
__be32 tmpval[8];
int i, ret;
for (i = 0; i < count; i++) {
tmpaddr[i] = cpu_to_be32(addr[i]);
}
for (i = 0; i < count; i++) {
tmpaddr[i] = cpu_to_be32(addr[i]);
}
ret = ath9k_wmi_cmd(priv->wmi, WMI_REG_READ_CMDID,
ret = ath9k_wmi_cmd(priv->wmi, WMI_REG_READ_CMDID,
(u8 *)tmpaddr , sizeof(u32) * count,
(u8 *)tmpval, sizeof(u32) * count,
100);
@ -275,9 +275,9 @@ static void ath9k_multi_regread(void *hw_priv, u32 *addr,
"Multiple REGISTER READ FAILED (count: %d)\n", count);
}
for (i = 0; i < count; i++) {
val[i] = be32_to_cpu(tmpval[i]);
}
for (i = 0; i < count; i++) {
val[i] = be32_to_cpu(tmpval[i]);
}
}
static void ath9k_regwrite_multi(struct ath_common *common)

View File

@ -1582,8 +1582,10 @@ static void ath9k_hw_apply_gpio_override(struct ath_hw *ah)
if (!(gpio_mask & 1))
continue;
ath9k_hw_cfg_output(ah, i, AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_gpio_request_out(ah, i, NULL,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_set_gpio(ah, i, !!(ah->gpio_val & BIT(i)));
ath9k_hw_gpio_free(ah, i);
}
}
@ -1958,7 +1960,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
ath9k_hw_init_qos(ah);
if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
ath9k_hw_cfg_gpio_input(ah, ah->rfkill_gpio);
ath9k_hw_gpio_request_in(ah, ah->rfkill_gpio, "ath9k-rfkill");
ath9k_hw_init_global_settings(ah);
@ -2385,6 +2387,61 @@ static bool ath9k_hw_dfs_tested(struct ath_hw *ah)
}
}
static void ath9k_gpio_cap_init(struct ath_hw *ah)
{
struct ath9k_hw_capabilities *pCap = &ah->caps;
if (AR_SREV_9271(ah)) {
pCap->num_gpio_pins = AR9271_NUM_GPIO;
pCap->gpio_mask = AR9271_GPIO_MASK;
} else if (AR_DEVID_7010(ah)) {
pCap->num_gpio_pins = AR7010_NUM_GPIO;
pCap->gpio_mask = AR7010_GPIO_MASK;
} else if (AR_SREV_9287(ah)) {
pCap->num_gpio_pins = AR9287_NUM_GPIO;
pCap->gpio_mask = AR9287_GPIO_MASK;
} else if (AR_SREV_9285(ah)) {
pCap->num_gpio_pins = AR9285_NUM_GPIO;
pCap->gpio_mask = AR9285_GPIO_MASK;
} else if (AR_SREV_9280(ah)) {
pCap->num_gpio_pins = AR9280_NUM_GPIO;
pCap->gpio_mask = AR9280_GPIO_MASK;
} else if (AR_SREV_9300(ah)) {
pCap->num_gpio_pins = AR9300_NUM_GPIO;
pCap->gpio_mask = AR9300_GPIO_MASK;
} else if (AR_SREV_9330(ah)) {
pCap->num_gpio_pins = AR9330_NUM_GPIO;
pCap->gpio_mask = AR9330_GPIO_MASK;
} else if (AR_SREV_9340(ah)) {
pCap->num_gpio_pins = AR9340_NUM_GPIO;
pCap->gpio_mask = AR9340_GPIO_MASK;
} else if (AR_SREV_9462(ah)) {
pCap->num_gpio_pins = AR9462_NUM_GPIO;
pCap->gpio_mask = AR9462_GPIO_MASK;
} else if (AR_SREV_9485(ah)) {
pCap->num_gpio_pins = AR9485_NUM_GPIO;
pCap->gpio_mask = AR9485_GPIO_MASK;
} else if (AR_SREV_9531(ah)) {
pCap->num_gpio_pins = AR9531_NUM_GPIO;
pCap->gpio_mask = AR9531_GPIO_MASK;
} else if (AR_SREV_9550(ah)) {
pCap->num_gpio_pins = AR9550_NUM_GPIO;
pCap->gpio_mask = AR9550_GPIO_MASK;
} else if (AR_SREV_9561(ah)) {
pCap->num_gpio_pins = AR9561_NUM_GPIO;
pCap->gpio_mask = AR9561_GPIO_MASK;
} else if (AR_SREV_9565(ah)) {
pCap->num_gpio_pins = AR9565_NUM_GPIO;
pCap->gpio_mask = AR9565_GPIO_MASK;
} else if (AR_SREV_9580(ah)) {
pCap->num_gpio_pins = AR9580_NUM_GPIO;
pCap->gpio_mask = AR9580_GPIO_MASK;
} else {
pCap->num_gpio_pins = AR_NUM_GPIO;
pCap->gpio_mask = AR_GPIO_MASK;
}
}
int ath9k_hw_fill_cap_info(struct ath_hw *ah)
{
struct ath9k_hw_capabilities *pCap = &ah->caps;
@ -2478,20 +2535,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
else
pCap->hw_caps &= ~ATH9K_HW_CAP_HT;
if (AR_SREV_9271(ah))
pCap->num_gpio_pins = AR9271_NUM_GPIO;
else if (AR_DEVID_7010(ah))
pCap->num_gpio_pins = AR7010_NUM_GPIO;
else if (AR_SREV_9300_20_OR_LATER(ah))
pCap->num_gpio_pins = AR9300_NUM_GPIO;
else if (AR_SREV_9287_11_OR_LATER(ah))
pCap->num_gpio_pins = AR9287_NUM_GPIO;
else if (AR_SREV_9285_12_OR_LATER(ah))
pCap->num_gpio_pins = AR9285_NUM_GPIO;
else if (AR_SREV_9280_20_OR_LATER(ah))
pCap->num_gpio_pins = AR928X_NUM_GPIO;
else
pCap->num_gpio_pins = AR_NUM_GPIO;
ath9k_gpio_cap_init(ah);
if (AR_SREV_9160_10_OR_LATER(ah) || AR_SREV_9100(ah))
pCap->rts_aggr_limit = ATH_AMPDU_LIMIT_MAX;
@ -2612,8 +2656,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
/* GPIO / RFKILL / Antennae */
/****************************/
static void ath9k_hw_gpio_cfg_output_mux(struct ath_hw *ah,
u32 gpio, u32 type)
static void ath9k_hw_gpio_cfg_output_mux(struct ath_hw *ah, u32 gpio, u32 type)
{
int addr;
u32 gpio_shift, tmp;
@ -2627,8 +2670,8 @@ static void ath9k_hw_gpio_cfg_output_mux(struct ath_hw *ah,
gpio_shift = (gpio % 6) * 5;
if (AR_SREV_9280_20_OR_LATER(ah)
|| (addr != AR_GPIO_OUTPUT_MUX1)) {
if (AR_SREV_9280_20_OR_LATER(ah) ||
(addr != AR_GPIO_OUTPUT_MUX1)) {
REG_RMW(ah, addr, (type << gpio_shift),
(0x1f << gpio_shift));
} else {
@ -2640,107 +2683,145 @@ static void ath9k_hw_gpio_cfg_output_mux(struct ath_hw *ah,
}
}
void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio)
/* BSP should set the corresponding MUX register correctly.
*/
static void ath9k_hw_gpio_cfg_soc(struct ath_hw *ah, u32 gpio, bool out,
const char *label)
{
u32 gpio_shift;
if (ah->caps.gpio_requested & BIT(gpio))
return;
BUG_ON(gpio >= ah->caps.num_gpio_pins);
/* may be requested by BSP, free anyway */
gpio_free(gpio);
if (gpio_request_one(gpio, out ? GPIOF_OUT_INIT_LOW : GPIOF_IN, label))
return;
ah->caps.gpio_requested |= BIT(gpio);
}
static void ath9k_hw_gpio_cfg_wmac(struct ath_hw *ah, u32 gpio, bool out,
u32 ah_signal_type)
{
u32 gpio_set, gpio_shift = gpio;
if (AR_DEVID_7010(ah)) {
gpio_shift = gpio;
REG_RMW(ah, AR7010_GPIO_OE,
(AR7010_GPIO_OE_AS_INPUT << gpio_shift),
(AR7010_GPIO_OE_MASK << gpio_shift));
return;
}
gpio_set = out ?
AR7010_GPIO_OE_AS_OUTPUT : AR7010_GPIO_OE_AS_INPUT;
REG_RMW(ah, AR7010_GPIO_OE, gpio_set << gpio_shift,
AR7010_GPIO_OE_MASK << gpio_shift);
} else if (AR_SREV_SOC(ah)) {
gpio_set = out ? 1 : 0;
REG_RMW(ah, AR_GPIO_OE_OUT, gpio_set << gpio_shift,
gpio_set << gpio_shift);
} else {
gpio_shift = gpio << 1;
gpio_set = out ?
AR_GPIO_OE_OUT_DRV_ALL : AR_GPIO_OE_OUT_DRV_NO;
REG_RMW(ah, AR_GPIO_OE_OUT, gpio_set << gpio_shift,
AR_GPIO_OE_OUT_DRV << gpio_shift);
gpio_shift = gpio << 1;
REG_RMW(ah,
AR_GPIO_OE_OUT,
(AR_GPIO_OE_OUT_DRV_NO << gpio_shift),
(AR_GPIO_OE_OUT_DRV << gpio_shift));
if (out)
ath9k_hw_gpio_cfg_output_mux(ah, gpio, ah_signal_type);
}
}
EXPORT_SYMBOL(ath9k_hw_cfg_gpio_input);
static void ath9k_hw_gpio_request(struct ath_hw *ah, u32 gpio, bool out,
const char *label, u32 ah_signal_type)
{
WARN_ON(gpio >= ah->caps.num_gpio_pins);
if (BIT(gpio) & ah->caps.gpio_mask)
ath9k_hw_gpio_cfg_wmac(ah, gpio, out, ah_signal_type);
else if (AR_SREV_SOC(ah))
ath9k_hw_gpio_cfg_soc(ah, gpio, out, label);
else
WARN_ON(1);
}
void ath9k_hw_gpio_request_in(struct ath_hw *ah, u32 gpio, const char *label)
{
ath9k_hw_gpio_request(ah, gpio, false, label, 0);
}
EXPORT_SYMBOL(ath9k_hw_gpio_request_in);
void ath9k_hw_gpio_request_out(struct ath_hw *ah, u32 gpio, const char *label,
u32 ah_signal_type)
{
ath9k_hw_gpio_request(ah, gpio, true, label, ah_signal_type);
}
EXPORT_SYMBOL(ath9k_hw_gpio_request_out);
void ath9k_hw_gpio_free(struct ath_hw *ah, u32 gpio)
{
if (!AR_SREV_SOC(ah))
return;
WARN_ON(gpio >= ah->caps.num_gpio_pins);
if (ah->caps.gpio_requested & BIT(gpio)) {
gpio_free(gpio);
ah->caps.gpio_requested &= ~BIT(gpio);
}
}
EXPORT_SYMBOL(ath9k_hw_gpio_free);
u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio)
{
u32 val = 0xffffffff;
#define MS_REG_READ(x, y) \
(MS(REG_READ(ah, AR_GPIO_IN_OUT), x##_GPIO_IN_VAL) & (AR_GPIO_BIT(y)))
(MS(REG_READ(ah, AR_GPIO_IN_OUT), x##_GPIO_IN_VAL) & BIT(y))
if (gpio >= ah->caps.num_gpio_pins)
return 0xffffffff;
WARN_ON(gpio >= ah->caps.num_gpio_pins);
if (AR_DEVID_7010(ah)) {
u32 val;
val = REG_READ(ah, AR7010_GPIO_IN);
return (MS(val, AR7010_GPIO_IN_VAL) & AR_GPIO_BIT(gpio)) == 0;
} else if (AR_SREV_9300_20_OR_LATER(ah))
return (MS(REG_READ(ah, AR_GPIO_IN), AR9300_GPIO_IN_VAL) &
AR_GPIO_BIT(gpio)) != 0;
else if (AR_SREV_9271(ah))
return MS_REG_READ(AR9271, gpio) != 0;
else if (AR_SREV_9287_11_OR_LATER(ah))
return MS_REG_READ(AR9287, gpio) != 0;
else if (AR_SREV_9285_12_OR_LATER(ah))
return MS_REG_READ(AR9285, gpio) != 0;
else if (AR_SREV_9280_20_OR_LATER(ah))
return MS_REG_READ(AR928X, gpio) != 0;
else
return MS_REG_READ(AR, gpio) != 0;
if (BIT(gpio) & ah->caps.gpio_mask) {
if (AR_SREV_9271(ah))
val = MS_REG_READ(AR9271, gpio);
else if (AR_SREV_9287(ah))
val = MS_REG_READ(AR9287, gpio);
else if (AR_SREV_9285(ah))
val = MS_REG_READ(AR9285, gpio);
else if (AR_SREV_9280(ah))
val = MS_REG_READ(AR928X, gpio);
else if (AR_DEVID_7010(ah))
val = REG_READ(ah, AR7010_GPIO_IN) & BIT(gpio);
else if (AR_SREV_9300_20_OR_LATER(ah))
val = REG_READ(ah, AR_GPIO_IN) & BIT(gpio);
else
val = MS_REG_READ(AR, gpio);
} else if (BIT(gpio) & ah->caps.gpio_requested) {
val = gpio_get_value(gpio) & BIT(gpio);
} else {
WARN_ON(1);
}
return val;
}
EXPORT_SYMBOL(ath9k_hw_gpio_get);
void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio,
u32 ah_signal_type)
{
u32 gpio_shift;
if (AR_DEVID_7010(ah)) {
gpio_shift = gpio;
REG_RMW(ah, AR7010_GPIO_OE,
(AR7010_GPIO_OE_AS_OUTPUT << gpio_shift),
(AR7010_GPIO_OE_MASK << gpio_shift));
return;
}
ath9k_hw_gpio_cfg_output_mux(ah, gpio, ah_signal_type);
gpio_shift = 2 * gpio;
REG_RMW(ah,
AR_GPIO_OE_OUT,
(AR_GPIO_OE_OUT_DRV_ALL << gpio_shift),
(AR_GPIO_OE_OUT_DRV << gpio_shift));
}
EXPORT_SYMBOL(ath9k_hw_cfg_output);
void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val)
{
if (AR_DEVID_7010(ah)) {
val = val ? 0 : 1;
REG_RMW(ah, AR7010_GPIO_OUT, ((val&1) << gpio),
AR_GPIO_BIT(gpio));
return;
}
WARN_ON(gpio >= ah->caps.num_gpio_pins);
if (AR_SREV_9271(ah))
val = ~val;
if ((1 << gpio) & AR_GPIO_OE_OUT_MASK)
REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
AR_GPIO_BIT(gpio));
if (AR_DEVID_7010(ah) || AR_SREV_9271(ah))
val = !val;
else
gpio_set_value(gpio, val & 1);
val = !!val;
if (BIT(gpio) & ah->caps.gpio_mask) {
u32 out_addr = AR_DEVID_7010(ah) ?
AR7010_GPIO_OUT : AR_GPIO_IN_OUT;
REG_RMW(ah, out_addr, val << gpio, BIT(gpio));
} else if (BIT(gpio) & ah->caps.gpio_requested) {
gpio_set_value(gpio, val);
} else {
WARN_ON(1);
}
}
EXPORT_SYMBOL(ath9k_hw_set_gpio);
void ath9k_hw_request_gpio(struct ath_hw *ah, u32 gpio, const char *label)
{
if (gpio >= ah->caps.num_gpio_pins)
return;
gpio_request_one(gpio, GPIOF_DIR_OUT | GPIOF_INIT_LOW, label);
}
EXPORT_SYMBOL(ath9k_hw_request_gpio);
void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna)
{
REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7));

View File

@ -160,7 +160,6 @@
#define AR_GPIO_OUTPUT_MUX_AS_RUCKUS_DATA 0x1e
#define AR_GPIOD_MASK 0x00001FFF
#define AR_GPIO_BIT(_gpio) (1 << (_gpio))
#define BASE_ACTIVATE_DELAY 100
#define RTC_PLL_SETTLE_DELAY (AR_SREV_9340(ah) ? 1000 : 100)
@ -301,6 +300,8 @@ struct ath9k_hw_capabilities {
u8 max_txchains;
u8 max_rxchains;
u8 num_gpio_pins;
u32 gpio_mask;
u32 gpio_requested;
u8 rx_hp_qdepth;
u8 rx_lp_qdepth;
u8 rx_status_len;
@ -1019,12 +1020,12 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah);
u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan);
/* GPIO / RFKILL / Antennae */
void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio);
void ath9k_hw_gpio_request_in(struct ath_hw *ah, u32 gpio, const char *label);
void ath9k_hw_gpio_request_out(struct ath_hw *ah, u32 gpio, const char *label,
u32 ah_signal_type);
void ath9k_hw_gpio_free(struct ath_hw *ah, u32 gpio);
u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio);
void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio,
u32 ah_signal_type);
void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val);
void ath9k_hw_request_gpio(struct ath_hw *ah, u32 gpio, const char *label);
void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna);
/* General Operation */

View File

@ -660,7 +660,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
ath9k_cmn_init_crypto(sc->sc_ah);
ath9k_init_misc(sc);
ath_fill_led_pin(sc);
ath_chanctx_init(sc);
ath9k_offchannel_init(sc);

View File

@ -718,12 +718,9 @@ static int ath9k_start(struct ieee80211_hw *hw)
if (!ath_complete_reset(sc, false))
ah->reset_power_on = false;
if (ah->led_pin >= 0) {
ath9k_hw_cfg_output(ah, ah->led_pin,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
if (ah->led_pin >= 0)
ath9k_hw_set_gpio(ah, ah->led_pin,
(ah->config.led_active_high) ? 1 : 0);
}
/*
* Reset key cache to sane defaults (all entries cleared) instead of
@ -867,11 +864,9 @@ static void ath9k_stop(struct ieee80211_hw *hw)
spin_lock_bh(&sc->sc_pcu_lock);
if (ah->led_pin >= 0) {
if (ah->led_pin >= 0)
ath9k_hw_set_gpio(ah, ah->led_pin,
(ah->config.led_active_high) ? 0 : 1);
ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
}
ath_prepare_reset(sc);

View File

@ -985,6 +985,10 @@
#define AR_SREV_9561(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9561))
#define AR_SREV_SOC(_ah) \
(AR_SREV_9340(_ah) || AR_SREV_9531(_ah) || AR_SREV_9550(ah) || \
AR_SREV_9561(ah))
/* NOTE: When adding chips newer than Peacock, add chip check here */
#define AR_SREV_9580_10_OR_LATER(_ah) \
(AR_SREV_9580(_ah))
@ -1104,14 +1108,46 @@ enum {
#define AR_PCIE_PHY_REG3 0x18c08
/* Define correct GPIO numbers and MASK bits to indicate the WMAC
* GPIO resource.
* Allow SOC chips(AR9340, AR9531, AR9550, AR9561) to access all GPIOs
* which rely on gpiolib framework. But restrict SOC AR9330 only to
* access WMAC GPIO which has the same design with the old chips.
*/
#define AR_NUM_GPIO 14
#define AR928X_NUM_GPIO 10
#define AR9280_NUM_GPIO 10
#define AR9285_NUM_GPIO 12
#define AR9287_NUM_GPIO 11
#define AR9287_NUM_GPIO 10
#define AR9271_NUM_GPIO 16
#define AR9300_NUM_GPIO 17
#define AR9300_NUM_GPIO 16
#define AR9330_NUM_GPIO 16
#define AR9340_NUM_GPIO 23
#define AR9462_NUM_GPIO 10
#define AR9485_NUM_GPIO 12
#define AR9531_NUM_GPIO 18
#define AR9550_NUM_GPIO 24
#define AR9561_NUM_GPIO 23
#define AR9565_NUM_GPIO 12
#define AR9580_NUM_GPIO 16
#define AR7010_NUM_GPIO 16
#define AR_GPIO_MASK 0x00003FFF
#define AR9271_GPIO_MASK 0x0000FFFF
#define AR9280_GPIO_MASK 0x000003FF
#define AR9285_GPIO_MASK 0x00000FFF
#define AR9287_GPIO_MASK 0x000003FF
#define AR9300_GPIO_MASK 0x0000F4FF
#define AR9330_GPIO_MASK 0x0000F4FF
#define AR9340_GPIO_MASK 0x0000000F
#define AR9462_GPIO_MASK 0x000003FF
#define AR9485_GPIO_MASK 0x00000FFF
#define AR9531_GPIO_MASK 0x0000000F
#define AR9550_GPIO_MASK 0x0000000F
#define AR9561_GPIO_MASK 0x0000000F
#define AR9565_GPIO_MASK 0x00000FFF
#define AR9580_GPIO_MASK 0x0000F4FF
#define AR7010_GPIO_MASK 0x0000FFFF
#define AR_GPIO_IN_OUT (AR_SREV_9340(ah) ? 0x4028 : 0x4048)
#define AR_GPIO_IN_VAL 0x0FFFC000
#define AR_GPIO_IN_VAL_S 14
@ -1132,8 +1168,6 @@ enum {
#define AR_GPIO_OE_OUT (AR_SREV_9340(ah) ? 0x4030 : \
(AR_SREV_9300_20_OR_LATER(ah) ? 0x4050 : 0x404c))
#define AR_GPIO_OE_OUT_MASK (AR_SREV_9550_OR_LATER(ah) ? \
0x0000000F : 0xFFFFFFFF)
#define AR_GPIO_OE_OUT_DRV 0x3
#define AR_GPIO_OE_OUT_DRV_NO 0x0
#define AR_GPIO_OE_OUT_DRV_LOW 0x1
@ -1858,15 +1892,33 @@ enum {
#define AR9300_BT_WGHT 0xcccc4444
#define AR_BT_COEX_MODE2 0x817c
#define AR_BT_BCN_MISS_THRESH 0x000000ff
#define AR_BT_BCN_MISS_THRESH_S 0
#define AR_BT_BCN_MISS_CNT 0x0000ff00
#define AR_BT_BCN_MISS_CNT_S 8
#define AR_BT_HOLD_RX_CLEAR 0x00010000
#define AR_BT_HOLD_RX_CLEAR_S 16
#define AR_BT_DISABLE_BT_ANT 0x00100000
#define AR_BT_DISABLE_BT_ANT_S 20
#define AR_BT_COEX_MODE2 0x817c
#define AR_BT_BCN_MISS_THRESH 0x000000ff
#define AR_BT_BCN_MISS_THRESH_S 0
#define AR_BT_BCN_MISS_CNT 0x0000ff00
#define AR_BT_BCN_MISS_CNT_S 8
#define AR_BT_HOLD_RX_CLEAR 0x00010000
#define AR_BT_HOLD_RX_CLEAR_S 16
#define AR_BT_PROTECT_BT_AFTER_WAKEUP 0x00080000
#define AR_BT_PROTECT_BT_AFTER_WAKEUP_S 19
#define AR_BT_DISABLE_BT_ANT 0x00100000
#define AR_BT_DISABLE_BT_ANT_S 20
#define AR_BT_QUIET_2_WIRE 0x00200000
#define AR_BT_QUIET_2_WIRE_S 21
#define AR_BT_WL_ACTIVE_MODE 0x00c00000
#define AR_BT_WL_ACTIVE_MODE_S 22
#define AR_BT_WL_TXRX_SEPARATE 0x01000000
#define AR_BT_WL_TXRX_SEPARATE_S 24
#define AR_BT_RS_DISCARD_EXTEND 0x02000000
#define AR_BT_RS_DISCARD_EXTEND_S 25
#define AR_BT_TSF_BT_ACTIVE_CTRL 0x0c000000
#define AR_BT_TSF_BT_ACTIVE_CTRL_S 26
#define AR_BT_TSF_BT_PRIORITY_CTRL 0x30000000
#define AR_BT_TSF_BT_PRIORITY_CTRL_S 28
#define AR_BT_INTERRUPT_ENABLE 0x40000000
#define AR_BT_INTERRUPT_ENABLE_S 30
#define AR_BT_PHY_ERR_BT_COLL_ENABLE 0x80000000
#define AR_BT_PHY_ERR_BT_COLL_ENABLE_S 31
#define AR_TXSIFS 0x81d0
#define AR_TXSIFS_TIME 0x000000FF
@ -1875,6 +1927,16 @@ enum {
#define AR_TXSIFS_ACK_SHIFT 0x00007000
#define AR_TXSIFS_ACK_SHIFT_S 12
#define AR_BT_COEX_MODE3 0x81d4
#define AR_BT_WL_ACTIVE_TIME 0x000000ff
#define AR_BT_WL_ACTIVE_TIME_S 0
#define AR_BT_WL_QC_TIME 0x0000ff00
#define AR_BT_WL_QC_TIME_S 8
#define AR_BT_ALLOW_CONCURRENT_ACCESS 0x000f0000
#define AR_BT_ALLOW_CONCURRENT_ACCESS_S 16
#define AR_BT_AGC_SATURATION_CNT_ENABLE 0x00100000
#define AR_BT_AGC_SATURATION_CNT_ENABLE_S 20
#define AR_TXOP_X 0x81ec
#define AR_TXOP_X_VAL 0x000000FF

View File

@ -55,11 +55,26 @@ static int ath9k_rng_data_read(struct ath_softc *sc, u32 *buf, u32 buf_size)
return j << 2;
}
static u32 ath9k_rng_delay_get(u32 fail_stats)
{
u32 delay;
if (fail_stats < 100)
delay = 10;
else if (fail_stats < 105)
delay = 1000;
else
delay = 10000;
return delay;
}
static int ath9k_rng_kthread(void *data)
{
int bytes_read;
struct ath_softc *sc = data;
u32 *rng_buf;
u32 delay, fail_stats = 0;
rng_buf = kmalloc_array(ATH9K_RNG_BUF_SIZE, sizeof(u32), GFP_KERNEL);
if (!rng_buf)
@ -69,10 +84,13 @@ static int ath9k_rng_kthread(void *data)
bytes_read = ath9k_rng_data_read(sc, rng_buf,
ATH9K_RNG_BUF_SIZE);
if (unlikely(!bytes_read)) {
msleep_interruptible(10);
delay = ath9k_rng_delay_get(++fail_stats);
msleep_interruptible(delay);
continue;
}
fail_stats = 0;
/* sleep until entropy bits under write_wakeup_threshold */
add_hwgenerator_randomness((void *)rng_buf, bytes_read,
ATH9K_RNG_ENTROPY(bytes_read));

View File

@ -18,6 +18,7 @@ wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
wil6210-y += wil_platform.o
wil6210-y += ethtool.o
wil6210-y += wil_crash_dump.o
wil6210-y += p2p.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)

View File

@ -18,6 +18,8 @@
#include "wil6210.h"
#include "wmi.h"
#define WIL_MAX_ROC_DURATION_MS 5000
#define CHAN60G(_channel, _flags) { \
.band = IEEE80211_BAND_60GHZ, \
.center_freq = 56160 + (2160 * (_channel)), \
@ -76,12 +78,24 @@ wil_mgmt_stypes[NUM_NL80211_IFTYPES] = {
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
[NL80211_IFTYPE_P2P_DEVICE] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
};
static const u32 wil_cipher_suites[] = {
WLAN_CIPHER_SUITE_GCMP,
};
static const char * const key_usage_str[] = {
[WMI_KEY_USE_PAIRWISE] = "PTK",
[WMI_KEY_USE_RX_GROUP] = "RX_GTK",
[WMI_KEY_USE_TX_GROUP] = "TX_GTK",
};
int wil_iftype_nl2wmi(enum nl80211_iftype type)
{
static const struct {
@ -113,7 +127,7 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
.interval_usec = 0,
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
struct wmi_notify_req_done_event evt;
} __packed reply;
struct wil_net_stats *stats = &wil->sta[cid].stats;
@ -226,13 +240,82 @@ static int wil_cfg80211_dump_station(struct wiphy *wiphy,
return rc;
}
static struct wireless_dev *
wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type,
u32 *flags, struct vif_params *params)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *p2p_wdev;
wil_dbg_misc(wil, "%s()\n", __func__);
if (type != NL80211_IFTYPE_P2P_DEVICE) {
wil_err(wil, "%s: unsupported iftype %d\n", __func__, type);
return ERR_PTR(-EINVAL);
}
if (wil->p2p_wdev) {
wil_err(wil, "%s: P2P_DEVICE interface already created\n",
__func__);
return ERR_PTR(-EINVAL);
}
p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL);
if (!p2p_wdev)
return ERR_PTR(-ENOMEM);
p2p_wdev->iftype = type;
p2p_wdev->wiphy = wiphy;
/* use our primary ethernet address */
ether_addr_copy(p2p_wdev->address, ndev->perm_addr);
wil->p2p_wdev = p2p_wdev;
return p2p_wdev;
}
static int wil_cfg80211_del_iface(struct wiphy *wiphy,
struct wireless_dev *wdev)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
wil_dbg_misc(wil, "%s()\n", __func__);
if (wdev != wil->p2p_wdev) {
wil_err(wil, "%s: delete of incorrect interface 0x%p\n",
__func__, wdev);
return -EINVAL;
}
wil_p2p_wdev_free(wil);
return 0;
}
static int wil_cfg80211_change_iface(struct wiphy *wiphy,
struct net_device *ndev,
enum nl80211_iftype type, u32 *flags,
struct vif_params *params)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct wireless_dev *wdev = wil->wdev;
struct wireless_dev *wdev = wil_to_wdev(wil);
int rc;
wil_dbg_misc(wil, "%s() type=%d\n", __func__, type);
if (netif_running(wil_to_ndev(wil)) && !wil_is_recovery_blocked(wil)) {
wil_dbg_misc(wil, "interface is up. resetting...\n");
mutex_lock(&wil->mutex);
__wil_down(wil);
rc = __wil_up(wil);
mutex_unlock(&wil->mutex);
if (rc)
return rc;
}
switch (type) {
case NL80211_IFTYPE_STATION:
@ -260,7 +343,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
struct cfg80211_scan_request *request)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct wireless_dev *wdev = wil->wdev;
struct wireless_dev *wdev = request->wdev;
struct {
struct wmi_start_scan_cmd cmd;
u16 chnl[4];
@ -268,6 +351,9 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
uint i, n;
int rc;
wil_dbg_misc(wil, "%s(), wdev=0x%p iftype=%d\n",
__func__, wdev, wdev->iftype);
if (wil->scan_request) {
wil_err(wil, "Already scanning\n");
return -EAGAIN;
@ -277,6 +363,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_DEVICE:
break;
default:
return -EOPNOTSUPP;
@ -288,6 +375,20 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
return -EBUSY;
}
/* scan on P2P_DEVICE is handled as p2p search */
if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
wil->scan_request = request;
wil->radio_wdev = wdev;
rc = wil_p2p_search(wil, request);
if (rc) {
wil->radio_wdev = wil_to_wdev(wil);
wil->scan_request = NULL;
}
return rc;
}
(void)wil_p2p_stop_discovery(wil);
wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
wil_dbg_misc(wil, "SSID count: %d", request->n_ssids);
@ -313,6 +414,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO);
memset(&cmd, 0, sizeof(cmd));
cmd.cmd.scan_type = WMI_ACTIVE_SCAN;
cmd.cmd.num_channels = 0;
n = min(request->n_channels, 4U);
for (i = 0; i < n; i++) {
@ -340,12 +442,19 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
if (rc)
goto out;
if (wil->discovery_mode && cmd.cmd.scan_type == WMI_ACTIVE_SCAN) {
cmd.cmd.discovery_mode = 1;
wil_dbg_misc(wil, "active scan with discovery_mode=1\n");
}
wil->radio_wdev = wdev;
rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
out:
if (rc) {
del_timer_sync(&wil->scan_timer);
wil->radio_wdev = wil_to_wdev(wil);
wil->scan_request = NULL;
}
@ -390,6 +499,7 @@ static void wil_print_connect_params(struct wil6210_priv *wil,
print_hex_dump(KERN_INFO, " SSID: ", DUMP_PREFIX_OFFSET,
16, 1, sme->ssid, sme->ssid_len, true);
wil_info(wil, " Privacy: %s\n", sme->privacy ? "secure" : "open");
wil_info(wil, " PBSS: %d\n", sme->pbss);
wil_print_crypto(wil, &sme->crypto);
}
@ -404,7 +514,9 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
const u8 *rsn_eid;
int ch;
int rc = 0;
enum ieee80211_bss_type bss_type = IEEE80211_BSS_TYPE_ESS;
wil_dbg_misc(wil, "%s()\n", __func__);
wil_print_connect_params(wil, sme);
if (test_bit(wil_status_fwconnecting, wil->status) ||
@ -422,14 +534,12 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
if (sme->privacy && !rsn_eid)
wil_info(wil, "WSC connection\n");
if (sme->pbss) {
wil_err(wil, "connect - PBSS not yet supported\n");
return -EOPNOTSUPP;
}
if (sme->pbss)
bss_type = IEEE80211_BSS_TYPE_PBSS;
bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
sme->ssid, sme->ssid_len,
IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY);
bss_type, IEEE80211_PRIVACY_ANY);
if (!bss) {
wil_err(wil, "Unable to find BSS\n");
return -ENOENT;
@ -568,10 +678,20 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_mgmt *mgmt_frame = (void *)buf;
struct wmi_sw_tx_req_cmd *cmd;
struct {
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
struct wmi_sw_tx_complete_event evt;
} __packed evt;
/* Note, currently we do not support the "wait" parameter, user-space
* must call remain_on_channel before mgmt_tx or listen on a channel
* another way (AP/PCP or connected station)
* in addition we need to check if specified "chan" argument is
* different from currently "listened" channel and fail if it is.
*/
wil_dbg_misc(wil, "%s()\n", __func__);
print_hex_dump_bytes("mgmt tx frame ", DUMP_PREFIX_OFFSET, buf, len);
cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
if (!cmd) {
rc = -ENOMEM;
@ -598,7 +718,7 @@ static int wil_cfg80211_set_channel(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct wireless_dev *wdev = wil->wdev;
struct wireless_dev *wdev = wil_to_wdev(wil);
wdev->preset_chandef = *chandef;
@ -608,22 +728,19 @@ static int wil_cfg80211_set_channel(struct wiphy *wiphy,
static enum wmi_key_usage wil_detect_key_usage(struct wil6210_priv *wil,
bool pairwise)
{
struct wireless_dev *wdev = wil->wdev;
struct wireless_dev *wdev = wil_to_wdev(wil);
enum wmi_key_usage rc;
static const char * const key_usage_str[] = {
[WMI_KEY_USE_PAIRWISE] = "WMI_KEY_USE_PAIRWISE",
[WMI_KEY_USE_RX_GROUP] = "WMI_KEY_USE_RX_GROUP",
[WMI_KEY_USE_TX_GROUP] = "WMI_KEY_USE_TX_GROUP",
};
if (pairwise) {
rc = WMI_KEY_USE_PAIRWISE;
} else {
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
rc = WMI_KEY_USE_RX_GROUP;
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
rc = WMI_KEY_USE_TX_GROUP;
break;
default:
@ -638,20 +755,86 @@ static enum wmi_key_usage wil_detect_key_usage(struct wil6210_priv *wil,
return rc;
}
static struct wil_tid_crypto_rx_single *
wil_find_crypto_ctx(struct wil6210_priv *wil, u8 key_index,
enum wmi_key_usage key_usage, const u8 *mac_addr)
{
int cid = -EINVAL;
int tid = 0;
struct wil_sta_info *s;
struct wil_tid_crypto_rx *c;
if (key_usage == WMI_KEY_USE_TX_GROUP)
return NULL; /* not needed */
/* supplicant provides Rx group key in STA mode with NULL MAC address */
if (mac_addr)
cid = wil_find_cid(wil, mac_addr);
else if (key_usage == WMI_KEY_USE_RX_GROUP)
cid = wil_find_cid_by_idx(wil, 0);
if (cid < 0) {
wil_err(wil, "No CID for %pM %s[%d]\n", mac_addr,
key_usage_str[key_usage], key_index);
return ERR_PTR(cid);
}
s = &wil->sta[cid];
if (key_usage == WMI_KEY_USE_PAIRWISE)
c = &s->tid_crypto_rx[tid];
else
c = &s->group_crypto_rx;
return &c->key_id[key_index];
}
static int wil_cfg80211_add_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, bool pairwise,
const u8 *mac_addr,
struct key_params *params)
{
int rc;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise);
struct wil_tid_crypto_rx_single *cc = wil_find_crypto_ctx(wil,
key_index,
key_usage,
mac_addr);
wil_dbg_misc(wil, "%s(%pM[%d] %s)\n", __func__, mac_addr, key_index,
pairwise ? "PTK" : "GTK");
wil_dbg_misc(wil, "%s(%pM %s[%d] PN %*phN)\n", __func__,
mac_addr, key_usage_str[key_usage], key_index,
params->seq_len, params->seq);
return wmi_add_cipher_key(wil, key_index, mac_addr, params->key_len,
params->key, key_usage);
if (IS_ERR(cc)) {
wil_err(wil, "Not connected, %s(%pM %s[%d] PN %*phN)\n",
__func__, mac_addr, key_usage_str[key_usage], key_index,
params->seq_len, params->seq);
return -EINVAL;
}
if (cc)
cc->key_set = false;
if (params->seq && params->seq_len != IEEE80211_GCMP_PN_LEN) {
wil_err(wil,
"Wrong PN len %d, %s(%pM %s[%d] PN %*phN)\n",
params->seq_len, __func__, mac_addr,
key_usage_str[key_usage], key_index,
params->seq_len, params->seq);
return -EINVAL;
}
rc = wmi_add_cipher_key(wil, key_index, mac_addr, params->key_len,
params->key, key_usage);
if ((rc == 0) && cc) {
if (params->seq)
memcpy(cc->pn, params->seq, IEEE80211_GCMP_PN_LEN);
else
memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
cc->key_set = true;
}
return rc;
}
static int wil_cfg80211_del_key(struct wiphy *wiphy,
@ -661,9 +844,20 @@ static int wil_cfg80211_del_key(struct wiphy *wiphy,
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise);
struct wil_tid_crypto_rx_single *cc = wil_find_crypto_ctx(wil,
key_index,
key_usage,
mac_addr);
wil_dbg_misc(wil, "%s(%pM[%d] %s)\n", __func__, mac_addr, key_index,
pairwise ? "PTK" : "GTK");
wil_dbg_misc(wil, "%s(%pM %s[%d])\n", __func__, mac_addr,
key_usage_str[key_usage], key_index);
if (IS_ERR(cc))
wil_info(wil, "Not connected, %s(%pM %s[%d])\n", __func__,
mac_addr, key_usage_str[key_usage], key_index);
if (!IS_ERR_OR_NULL(cc))
cc->key_set = false;
return wmi_del_cipher_key(wil, key_index, mac_addr, key_usage);
}
@ -674,6 +868,9 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
u8 key_index, bool unicast,
bool multicast)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
wil_dbg_misc(wil, "%s: entered\n", __func__);
return 0;
}
@ -686,16 +883,19 @@ static int wil_remain_on_channel(struct wiphy *wiphy,
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
/* TODO: handle duration */
wil_info(wil, "%s(%d, %d ms)\n", __func__, chan->center_freq, duration);
wil_dbg_misc(wil, "%s() center_freq=%d, duration=%d iftype=%d\n",
__func__, chan->center_freq, duration, wdev->iftype);
rc = wmi_set_channel(wil, chan->hw_value);
rc = wil_p2p_listen(wil, duration, chan, cookie);
if (rc)
return rc;
rc = wmi_rxon(wil, true);
wil->radio_wdev = wdev;
return rc;
cfg80211_ready_on_channel(wdev, *cookie, chan, duration,
GFP_KERNEL);
return 0;
}
static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
@ -703,13 +903,10 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
u64 cookie)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
wil_info(wil, "%s()\n", __func__);
wil_dbg_misc(wil, "%s()\n", __func__);
rc = wmi_rxon(wil, false);
return rc;
return wil_p2p_cancel_listen(wil, cookie);
}
/**
@ -852,12 +1049,22 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
const u8 *ssid, size_t ssid_len, u32 privacy,
int bi, u8 chan,
struct cfg80211_beacon_data *bcon,
u8 hidden_ssid)
u8 hidden_ssid, u32 pbss)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
struct wireless_dev *wdev = ndev->ieee80211_ptr;
u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
u8 is_go = (wdev->iftype == NL80211_IFTYPE_P2P_GO);
if (pbss)
wmi_nettype = WMI_NETTYPE_P2P;
wil_dbg_misc(wil, "%s: is_go=%d\n", __func__, is_go);
if (is_go && !pbss) {
wil_err(wil, "%s: P2P GO must be in PBSS\n", __func__);
return -ENOTSUPP;
}
wil_set_recovery_state(wil, fw_recovery_idle);
@ -879,10 +1086,11 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
wil->privacy = privacy;
wil->channel = chan;
wil->hidden_ssid = hidden_ssid;
wil->pbss = pbss;
netif_carrier_on(ndev);
rc = wmi_pcp_start(wil, bi, wmi_nettype, chan, hidden_ssid);
rc = wmi_pcp_start(wil, bi, wmi_nettype, chan, hidden_ssid, is_go);
if (rc)
goto err_pcp_start;
@ -928,7 +1136,8 @@ static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
wdev->ssid_len, privacy,
wdev->beacon_interval,
wil->channel, bcon,
wil->hidden_ssid);
wil->hidden_ssid,
wil->pbss);
} else {
rc = _wil_cfg80211_set_ies(wiphy, bcon);
}
@ -954,11 +1163,6 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
return -EINVAL;
}
if (info->pbss) {
wil_err(wil, "AP: PBSS not yet supported\n");
return -EOPNOTSUPP;
}
switch (info->hidden_ssid) {
case NL80211_HIDDEN_SSID_NOT_IN_USE:
hidden_ssid = WMI_HIDDEN_SSID_DISABLED;
@ -984,6 +1188,7 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
info->hidden_ssid);
wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval,
info->dtim_period);
wil_dbg_misc(wil, "PBSS %d\n", info->pbss);
print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
info->ssid, info->ssid_len);
wil_print_bcon_data(bcon);
@ -992,7 +1197,7 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
rc = _wil_cfg80211_start_ap(wiphy, ndev,
info->ssid, info->ssid_len, info->privacy,
info->beacon_interval, channel->hw_value,
bcon, hidden_ssid);
bcon, hidden_ssid, info->pbss);
return rc;
}
@ -1139,7 +1344,26 @@ static int wil_cfg80211_change_bss(struct wiphy *wiphy,
return 0;
}
static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy,
struct wireless_dev *wdev)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
wil_dbg_misc(wil, "%s: entered\n", __func__);
return 0;
}
static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
struct wireless_dev *wdev)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
wil_dbg_misc(wil, "%s: entered\n", __func__);
}
static struct cfg80211_ops wil_cfg80211_ops = {
.add_virtual_intf = wil_cfg80211_add_iface,
.del_virtual_intf = wil_cfg80211_del_iface,
.scan = wil_cfg80211_scan,
.connect = wil_cfg80211_connect,
.disconnect = wil_cfg80211_disconnect,
@ -1160,20 +1384,25 @@ static struct cfg80211_ops wil_cfg80211_ops = {
.del_station = wil_cfg80211_del_station,
.probe_client = wil_cfg80211_probe_client,
.change_bss = wil_cfg80211_change_bss,
/* P2P device */
.start_p2p_device = wil_cfg80211_start_p2p_device,
.stop_p2p_device = wil_cfg80211_stop_p2p_device,
};
static void wil_wiphy_init(struct wiphy *wiphy)
{
wiphy->max_scan_ssids = 1;
wiphy->max_scan_ie_len = WMI_MAX_IE_LEN;
wiphy->max_remain_on_channel_duration = WIL_MAX_ROC_DURATION_MS;
wiphy->max_num_pmkids = 0 /* TODO: */;
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_DEVICE) |
BIT(NL80211_IFTYPE_MONITOR);
/* TODO: enable P2P when integrated with supplicant:
* BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO)
*/
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
dev_dbg(wiphy_dev(wiphy), "%s : flags = 0x%08x\n",
__func__, wiphy->flags);
@ -1241,3 +1470,18 @@ void wil_wdev_free(struct wil6210_priv *wil)
wiphy_free(wdev->wiphy);
kfree(wdev);
}
void wil_p2p_wdev_free(struct wil6210_priv *wil)
{
struct wireless_dev *p2p_wdev;
mutex_lock(&wil->p2p_wdev_mutex);
p2p_wdev = wil->p2p_wdev;
if (p2p_wdev) {
wil->p2p_wdev = NULL;
wil->radio_wdev = wil_to_wdev(wil);
cfg80211_unregister_wdev(p2p_wdev);
kfree(p2p_wdev);
}
mutex_unlock(&wil->p2p_wdev_mutex);
}

View File

@ -37,6 +37,7 @@ enum dbg_off_type {
doff_x32 = 1,
doff_ulong = 2,
doff_io32 = 3,
doff_u8 = 4
};
/* offset to "wil" */
@ -346,6 +347,10 @@ static void wil6210_debugfs_init_offset(struct wil6210_priv *wil,
tbl[i].mode, dbg,
base + tbl[i].off);
break;
case doff_u8:
f = debugfs_create_u8(tbl[i].name, tbl[i].mode, dbg,
base + tbl[i].off);
break;
default:
f = ERR_PTR(-EINVAL);
}
@ -821,13 +826,13 @@ static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;
struct wil6210_mbox_hdr_wmi *wmi;
struct wmi_cmd_hdr *wmi;
void *cmd;
int cmdlen = len - sizeof(struct wil6210_mbox_hdr_wmi);
int cmdlen = len - sizeof(struct wmi_cmd_hdr);
u16 cmdid;
int rc, rc1;
if (cmdlen <= 0)
if (cmdlen < 0)
return -EINVAL;
wmi = kmalloc(len, GFP_KERNEL);
@ -840,8 +845,8 @@ static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,
return rc;
}
cmd = &wmi[1];
cmdid = le16_to_cpu(wmi->id);
cmd = (cmdlen > 0) ? &wmi[1] : NULL;
cmdid = le16_to_cpu(wmi->command_id);
rc1 = wmi_send(wil, cmdid, cmd, cmdlen);
kfree(wmi);
@ -985,7 +990,7 @@ static int wil_bf_debugfs_show(struct seq_file *s, void *data)
.interval_usec = 0,
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
struct wmi_notify_req_done_event evt;
} __packed reply;
@ -1333,6 +1338,34 @@ static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
r->ssn_last_drop);
}
static void wil_print_rxtid_crypto(struct seq_file *s, int tid,
struct wil_tid_crypto_rx *c)
{
int i;
for (i = 0; i < 4; i++) {
struct wil_tid_crypto_rx_single *cc = &c->key_id[i];
if (cc->key_set)
goto has_keys;
}
return;
has_keys:
if (tid < WIL_STA_TID_NUM)
seq_printf(s, " [%2d] PN", tid);
else
seq_puts(s, " [GR] PN");
for (i = 0; i < 4; i++) {
struct wil_tid_crypto_rx_single *cc = &c->key_id[i];
seq_printf(s, " [%i%s]%6phN", i, cc->key_set ? "+" : "-",
cc->pn);
}
seq_puts(s, "\n");
}
static int wil_sta_debugfs_show(struct seq_file *s, void *data)
__acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
{
@ -1360,18 +1393,25 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
spin_lock_bh(&p->tid_rx_lock);
for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
struct wil_tid_ampdu_rx *r = p->tid_rx[tid];
struct wil_tid_crypto_rx *c =
&p->tid_crypto_rx[tid];
if (r) {
seq_printf(s, "[%2d] ", tid);
seq_printf(s, " [%2d] ", tid);
wil_print_rxtid(s, r);
}
wil_print_rxtid_crypto(s, tid, c);
}
wil_print_rxtid_crypto(s, WIL_STA_TID_NUM,
&p->group_crypto_rx);
spin_unlock_bh(&p->tid_rx_lock);
seq_printf(s,
"Rx invalid frame: non-data %lu, short %lu, large %lu\n",
"Rx invalid frame: non-data %lu, short %lu, large %lu, replay %lu\n",
p->stats.rx_non_data_frame,
p->stats.rx_short_frame,
p->stats.rx_large_frame);
p->stats.rx_large_frame,
p->stats.rx_replay);
seq_puts(s, "Rx/MCS:");
for (mcs = 0; mcs < ARRAY_SIZE(p->stats.rx_per_mcs);
@ -1487,6 +1527,7 @@ static const struct dbg_off dbg_wil_off[] = {
WIL_FIELD(hw_version, S_IRUGO, doff_x32),
WIL_FIELD(recovery_count, S_IRUGO, doff_u32),
WIL_FIELD(ap_isolate, S_IRUGO, doff_u32),
WIL_FIELD(discovery_mode, S_IRUGO | S_IWUSR, doff_u8),
{},
};

View File

@ -391,12 +391,14 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);
if (isr & ISR_MISC_FW_ERROR) {
wil->recovery_state = fw_recovery_pending;
wil_fw_core_dump(wil);
wil_notify_fw_error(wil);
isr &= ~ISR_MISC_FW_ERROR;
if (wil->platform_ops.notify_crash) {
if (wil->platform_ops.notify) {
wil_err(wil, "notify platform driver about FW crash");
wil->platform_ops.notify_crash(wil->platform_handle);
wil->platform_ops.notify(wil->platform_handle,
WIL_PLATFORM_EVT_FW_CRASH);
} else {
wil_fw_error_recovery(wil);
}

View File

@ -161,13 +161,20 @@ out_free:
int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd)
{
int ret;
switch (cmd) {
case WIL_IOCTL_MEMIO:
return wil_ioc_memio_dword(wil, data);
ret = wil_ioc_memio_dword(wil, data);
break;
case WIL_IOCTL_MEMIO_BLOCK:
return wil_ioc_memio_block(wil, data);
ret = wil_ioc_memio_block(wil, data);
break;
default:
wil_dbg_ioctl(wil, "Unsupported IOCTL 0x%04x\n", cmd);
return -ENOIOCTLCMD;
}
wil_dbg_ioctl(wil, "ioctl(0x%04x) -> %d\n", cmd, ret);
return ret;
}

View File

@ -27,6 +27,11 @@ bool debug_fw; /* = false; */
module_param(debug_fw, bool, S_IRUGO);
MODULE_PARM_DESC(debug_fw, " do not perform card reset. For FW debug");
static bool oob_mode;
module_param(oob_mode, bool, S_IRUGO);
MODULE_PARM_DESC(oob_mode,
" enable out of the box (OOB) mode in FW, for diagnostics and certification");
bool no_fw_recovery;
module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(no_fw_recovery, " disable automatic FW error recovery");
@ -149,7 +154,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
might_sleep();
wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid,
sta->status);
/* inform upper/lower layers */
if (sta->status != wil_sta_unused) {
if (!from_event)
wmi_disconnect_sta(wil, sta->addr, reason_code, true);
@ -165,7 +170,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
}
sta->status = wil_sta_unused;
}
/* reorder buffers */
for (i = 0; i < WIL_STA_TID_NUM; i++) {
struct wil_tid_ampdu_rx *r;
@ -177,10 +182,15 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
spin_unlock_bh(&sta->tid_rx_lock);
}
/* crypto context */
memset(sta->tid_crypto_rx, 0, sizeof(sta->tid_crypto_rx));
memset(&sta->group_crypto_rx, 0, sizeof(sta->group_crypto_rx));
/* release vrings */
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
if (wil->vring2cid_tid[i][0] == cid)
wil_vring_fini_tx(wil, i);
}
/* statistics */
memset(&sta->stats, 0, sizeof(sta->stats));
}
@ -300,6 +310,11 @@ void wil_set_recovery_state(struct wil6210_priv *wil, int state)
wake_up_interruptible(&wil->wq);
}
bool wil_is_recovery_blocked(struct wil6210_priv *wil)
{
return no_fw_recovery && (wil->recovery_state == fw_recovery_pending);
}
static void wil_fw_error_worker(struct work_struct *work)
{
struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
@ -440,9 +455,8 @@ int wil_priv_init(struct wil6210_priv *wil)
mutex_init(&wil->mutex);
mutex_init(&wil->wmi_mutex);
mutex_init(&wil->back_rx_mutex);
mutex_init(&wil->back_tx_mutex);
mutex_init(&wil->probe_client_mutex);
mutex_init(&wil->p2p_wdev_mutex);
init_completion(&wil->wmi_ready);
init_completion(&wil->wmi_call);
@ -450,17 +464,15 @@ int wil_priv_init(struct wil6210_priv *wil)
wil->bcast_vring = -1;
setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)wil);
setup_timer(&wil->p2p.discovery_timer, wil_p2p_discovery_timer_fn,
(ulong)wil);
INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
INIT_WORK(&wil->back_rx_worker, wil_back_rx_worker);
INIT_WORK(&wil->back_tx_worker, wil_back_tx_worker);
INIT_WORK(&wil->probe_client_worker, wil_probe_client_worker);
INIT_LIST_HEAD(&wil->pending_wmi_ev);
INIT_LIST_HEAD(&wil->back_rx_pending);
INIT_LIST_HEAD(&wil->back_tx_pending);
INIT_LIST_HEAD(&wil->probe_client_pending);
spin_lock_init(&wil->wmi_ev_lock);
init_waitqueue_head(&wil->wq);
@ -514,16 +526,14 @@ void wil_priv_deinit(struct wil6210_priv *wil)
wil_set_recovery_state(wil, fw_recovery_idle);
del_timer_sync(&wil->scan_timer);
del_timer_sync(&wil->p2p.discovery_timer);
cancel_work_sync(&wil->disconnect_worker);
cancel_work_sync(&wil->fw_error_worker);
cancel_work_sync(&wil->p2p.discovery_expired_work);
mutex_lock(&wil->mutex);
wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
mutex_unlock(&wil->mutex);
wmi_event_flush(wil);
wil_back_rx_flush(wil);
cancel_work_sync(&wil->back_rx_worker);
wil_back_tx_flush(wil);
cancel_work_sync(&wil->back_tx_worker);
wil_probe_client_flush(wil);
cancel_work_sync(&wil->probe_client_worker);
destroy_workqueue(wil->wq_service);
@ -542,6 +552,16 @@ static inline void wil_release_cpu(struct wil6210_priv *wil)
wil_w(wil, RGF_USER_USER_CPU_0, 1);
}
static void wil_set_oob_mode(struct wil6210_priv *wil, bool enable)
{
wil_info(wil, "%s: enable=%d\n", __func__, enable);
if (enable) {
wil_s(wil, RGF_USER_USAGE_6, BIT_USER_OOB_MODE);
} else {
wil_c(wil, RGF_USER_USAGE_6, BIT_USER_OOB_MODE);
}
}
static int wil_target_reset(struct wil6210_priv *wil)
{
int delay = 0;
@ -637,6 +657,7 @@ void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
static int wil_get_bl_info(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
struct wiphy *wiphy = wil_to_wiphy(wil);
union {
struct bl_dedicated_registers_v0 bl0;
struct bl_dedicated_registers_v1 bl1;
@ -681,6 +702,7 @@ static int wil_get_bl_info(struct wil6210_priv *wil)
}
ether_addr_copy(ndev->perm_addr, mac);
ether_addr_copy(wiphy->perm_addr, mac);
if (!is_valid_ether_addr(ndev->dev_addr))
ether_addr_copy(ndev->dev_addr, mac);
@ -767,6 +789,15 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
if (wil->hw_version == HW_VER_UNKNOWN)
return -ENODEV;
if (wil->platform_ops.notify) {
rc = wil->platform_ops.notify(wil->platform_handle,
WIL_PLATFORM_EVT_PRE_RESET);
if (rc)
wil_err(wil,
"%s: PRE_RESET platform notify failed, rc %d\n",
__func__, rc);
}
set_bit(wil_status_resetting, wil->status);
cancel_work_sync(&wil->disconnect_worker);
@ -807,6 +838,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
if (rc)
return rc;
wil_set_oob_mode(wil, oob_mode);
if (load_fw) {
wil_info(wil, "Use firmware <%s> + board <%s>\n", WIL_FW_NAME,
WIL_FW2_NAME);
@ -846,8 +878,27 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
/* we just started MAC, wait for FW ready */
rc = wil_wait_for_fw_ready(wil);
if (rc == 0) /* check FW is responsive */
rc = wmi_echo(wil);
if (rc)
return rc;
/* check FW is responsive */
rc = wmi_echo(wil);
if (rc) {
wil_err(wil, "%s: wmi_echo failed, rc %d\n",
__func__, rc);
return rc;
}
if (wil->platform_ops.notify) {
rc = wil->platform_ops.notify(wil->platform_handle,
WIL_PLATFORM_EVT_FW_RDY);
if (rc) {
wil_err(wil,
"%s: FW_RDY notify failed, rc %d\n",
__func__, rc);
rc = 0;
}
}
}
return rc;
@ -954,6 +1005,8 @@ int __wil_down(struct wil6210_priv *wil)
}
wil_enable_irq(wil);
(void)wil_p2p_stop_discovery(wil);
if (wil->scan_request) {
wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
wil->scan_request);

View File

@ -60,11 +60,7 @@ static int wil_do_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
int ret = wil_ioctl(wil, ifr->ifr_data, cmd);
wil_dbg_misc(wil, "ioctl(0x%04x) -> %d\n", cmd, ret);
return ret;
return wil_ioctl(wil, ifr->ifr_data, cmd);
}
static const struct net_device_ops wil_netdev_ops = {
@ -149,6 +145,7 @@ void *wil_if_alloc(struct device *dev)
wil = wdev_to_wil(wdev);
wil->wdev = wdev;
wil->radio_wdev = wdev;
wil_dbg_misc(wil, "%s()\n", __func__);

View File

@ -0,0 +1,253 @@
/*
* Copyright (c) 2014-2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "wil6210.h"
#include "wmi.h"
#define P2P_WILDCARD_SSID "DIRECT-"
#define P2P_DMG_SOCIAL_CHANNEL 2
#define P2P_SEARCH_DURATION_MS 500
#define P2P_DEFAULT_BI 100
void wil_p2p_discovery_timer_fn(ulong x)
{
struct wil6210_priv *wil = (void *)x;
wil_dbg_misc(wil, "%s\n", __func__);
schedule_work(&wil->p2p.discovery_expired_work);
}
int wil_p2p_search(struct wil6210_priv *wil,
struct cfg80211_scan_request *request)
{
int rc;
struct wil_p2p_info *p2p = &wil->p2p;
wil_dbg_misc(wil, "%s: channel %d\n",
__func__, P2P_DMG_SOCIAL_CHANNEL);
mutex_lock(&wil->mutex);
if (p2p->discovery_started) {
wil_err(wil, "%s: search failed. discovery already ongoing\n",
__func__);
rc = -EBUSY;
goto out;
}
rc = wmi_p2p_cfg(wil, P2P_DMG_SOCIAL_CHANNEL, P2P_DEFAULT_BI);
if (rc) {
wil_err(wil, "%s: wmi_p2p_cfg failed\n", __func__);
goto out;
}
rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
if (rc) {
wil_err(wil, "%s: wmi_set_ssid failed\n", __func__);
goto out_stop;
}
/* Set application IE to probe request and probe response */
rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ,
request->ie_len, request->ie);
if (rc) {
wil_err(wil, "%s: wmi_set_ie(WMI_FRAME_PROBE_REQ) failed\n",
__func__);
goto out_stop;
}
/* supplicant doesn't provide Probe Response IEs. As a workaround -
* re-use Probe Request IEs
*/
rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP,
request->ie_len, request->ie);
if (rc) {
wil_err(wil, "%s: wmi_set_ie(WMI_FRAME_PROBE_RESP) failed\n",
__func__);
goto out_stop;
}
rc = wmi_start_search(wil);
if (rc) {
wil_err(wil, "%s: wmi_start_search failed\n", __func__);
goto out_stop;
}
p2p->discovery_started = 1;
INIT_WORK(&p2p->discovery_expired_work, wil_p2p_search_expired);
mod_timer(&p2p->discovery_timer,
jiffies + msecs_to_jiffies(P2P_SEARCH_DURATION_MS));
out_stop:
if (rc)
wmi_stop_discovery(wil);
out:
mutex_unlock(&wil->mutex);
return rc;
}
int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration,
struct ieee80211_channel *chan, u64 *cookie)
{
struct wil_p2p_info *p2p = &wil->p2p;
u8 channel = P2P_DMG_SOCIAL_CHANNEL;
int rc;
if (chan)
channel = chan->hw_value;
wil_dbg_misc(wil, "%s: duration %d\n", __func__, duration);
mutex_lock(&wil->mutex);
if (p2p->discovery_started) {
wil_err(wil, "%s: discovery already ongoing\n", __func__);
rc = -EBUSY;
goto out;
}
rc = wmi_p2p_cfg(wil, channel, P2P_DEFAULT_BI);
if (rc) {
wil_err(wil, "%s: wmi_p2p_cfg failed\n", __func__);
goto out;
}
rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
if (rc) {
wil_err(wil, "%s: wmi_set_ssid failed\n", __func__);
goto out_stop;
}
rc = wmi_start_listen(wil);
if (rc) {
wil_err(wil, "%s: wmi_start_listen failed\n", __func__);
goto out_stop;
}
memcpy(&p2p->listen_chan, chan, sizeof(*chan));
*cookie = ++p2p->cookie;
p2p->discovery_started = 1;
INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired);
mod_timer(&p2p->discovery_timer,
jiffies + msecs_to_jiffies(duration));
out_stop:
if (rc)
wmi_stop_discovery(wil);
out:
mutex_unlock(&wil->mutex);
return rc;
}
u8 wil_p2p_stop_discovery(struct wil6210_priv *wil)
{
struct wil_p2p_info *p2p = &wil->p2p;
u8 started = p2p->discovery_started;
if (p2p->discovery_started) {
del_timer_sync(&p2p->discovery_timer);
p2p->discovery_started = 0;
wmi_stop_discovery(wil);
}
return started;
}
int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie)
{
struct wil_p2p_info *p2p = &wil->p2p;
u8 started;
mutex_lock(&wil->mutex);
if (cookie != p2p->cookie) {
wil_info(wil, "%s: Cookie mismatch: 0x%016llx vs. 0x%016llx\n",
__func__, p2p->cookie, cookie);
mutex_unlock(&wil->mutex);
return -ENOENT;
}
started = wil_p2p_stop_discovery(wil);
mutex_unlock(&wil->mutex);
if (!started) {
wil_err(wil, "%s: listen not started\n", __func__);
return -ENOENT;
}
mutex_lock(&wil->p2p_wdev_mutex);
cfg80211_remain_on_channel_expired(wil->radio_wdev,
p2p->cookie,
&p2p->listen_chan,
GFP_KERNEL);
wil->radio_wdev = wil->wdev;
mutex_unlock(&wil->p2p_wdev_mutex);
return 0;
}
void wil_p2p_listen_expired(struct work_struct *work)
{
struct wil_p2p_info *p2p = container_of(work,
struct wil_p2p_info, discovery_expired_work);
struct wil6210_priv *wil = container_of(p2p,
struct wil6210_priv, p2p);
u8 started;
wil_dbg_misc(wil, "%s()\n", __func__);
mutex_lock(&wil->mutex);
started = wil_p2p_stop_discovery(wil);
mutex_unlock(&wil->mutex);
if (started) {
mutex_lock(&wil->p2p_wdev_mutex);
cfg80211_remain_on_channel_expired(wil->radio_wdev,
p2p->cookie,
&p2p->listen_chan,
GFP_KERNEL);
wil->radio_wdev = wil->wdev;
mutex_unlock(&wil->p2p_wdev_mutex);
}
}
void wil_p2p_search_expired(struct work_struct *work)
{
struct wil_p2p_info *p2p = container_of(work,
struct wil_p2p_info, discovery_expired_work);
struct wil6210_priv *wil = container_of(p2p,
struct wil6210_priv, p2p);
u8 started;
wil_dbg_misc(wil, "%s()\n", __func__);
mutex_lock(&wil->mutex);
started = wil_p2p_stop_discovery(wil);
mutex_unlock(&wil->mutex);
if (started) {
mutex_lock(&wil->p2p_wdev_mutex);
cfg80211_scan_done(wil->scan_request, 0);
wil->scan_request = NULL;
wil->radio_wdev = wil->wdev;
mutex_unlock(&wil->p2p_wdev_mutex);
}
}

View File

@ -275,6 +275,7 @@ static void wil_pcie_remove(struct pci_dev *pdev)
pci_disable_device(pdev);
if (wil->platform_ops.uninit)
wil->platform_ops.uninit(wil->platform_handle);
wil_p2p_wdev_free(wil);
wil_if_free(wil);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014-2015 Qualcomm Atheros, Inc.
* Copyright (c) 2014-2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -291,35 +291,15 @@ static u16 wil_agg_size(struct wil6210_priv *wil, u16 req_agg_wsize)
return min(max_agg_size, req_agg_wsize);
}
/* Block Ack - Rx side (recipient */
/* Block Ack - Rx side (recipient) */
int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid,
u8 dialog_token, __le16 ba_param_set,
__le16 ba_timeout, __le16 ba_seq_ctrl)
{
struct wil_back_rx *req = kzalloc(sizeof(*req), GFP_KERNEL);
if (!req)
return -ENOMEM;
req->cidxtid = cidxtid;
req->dialog_token = dialog_token;
req->ba_param_set = le16_to_cpu(ba_param_set);
req->ba_timeout = le16_to_cpu(ba_timeout);
req->ba_seq_ctrl = le16_to_cpu(ba_seq_ctrl);
mutex_lock(&wil->back_rx_mutex);
list_add_tail(&req->list, &wil->back_rx_pending);
mutex_unlock(&wil->back_rx_mutex);
queue_work(wil->wq_service, &wil->back_rx_worker);
return 0;
}
static void wil_back_rx_handle(struct wil6210_priv *wil,
struct wil_back_rx *req)
__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
{
u16 param_set = le16_to_cpu(ba_param_set);
u16 agg_timeout = le16_to_cpu(ba_timeout);
u16 seq_ctrl = le16_to_cpu(ba_seq_ctrl);
struct wil_sta_info *sta;
u8 cid, tid;
u16 agg_wsize = 0;
@ -328,34 +308,35 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
* bits 2..5: TID
* bits 6..15: buffer size
*/
u16 req_agg_wsize = WIL_GET_BITS(req->ba_param_set, 6, 15);
bool agg_amsdu = !!(req->ba_param_set & BIT(0));
int ba_policy = req->ba_param_set & BIT(1);
u16 agg_timeout = req->ba_timeout;
u16 req_agg_wsize = WIL_GET_BITS(param_set, 6, 15);
bool agg_amsdu = !!(param_set & BIT(0));
int ba_policy = param_set & BIT(1);
u16 status = WLAN_STATUS_SUCCESS;
u16 ssn = req->ba_seq_ctrl >> 4;
u16 ssn = seq_ctrl >> 4;
struct wil_tid_ampdu_rx *r;
int rc;
int rc = 0;
might_sleep();
parse_cidxtid(req->cidxtid, &cid, &tid);
parse_cidxtid(cidxtid, &cid, &tid);
/* sanity checks */
if (cid >= WIL6210_MAX_CID) {
wil_err(wil, "BACK: invalid CID %d\n", cid);
return;
rc = -EINVAL;
goto out;
}
sta = &wil->sta[cid];
if (sta->status != wil_sta_connected) {
wil_err(wil, "BACK: CID %d not connected\n", cid);
return;
rc = -EINVAL;
goto out;
}
wil_dbg_wmi(wil,
"ADDBA request for CID %d %pM TID %d size %d timeout %d AMSDU%s policy %d token %d SSN 0x%03x\n",
cid, sta->addr, tid, req_agg_wsize, req->ba_timeout,
agg_amsdu ? "+" : "-", !!ba_policy, req->dialog_token, ssn);
cid, sta->addr, tid, req_agg_wsize, agg_timeout,
agg_amsdu ? "+" : "-", !!ba_policy, dialog_token, ssn);
/* apply policies */
if (ba_policy) {
@ -365,10 +346,13 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
if (status == WLAN_STATUS_SUCCESS)
agg_wsize = wil_agg_size(wil, req_agg_wsize);
rc = wmi_addba_rx_resp(wil, cid, tid, req->dialog_token, status,
rc = wmi_addba_rx_resp(wil, cid, tid, dialog_token, status,
agg_amsdu, agg_wsize, agg_timeout);
if (rc || (status != WLAN_STATUS_SUCCESS))
return;
if (rc || (status != WLAN_STATUS_SUCCESS)) {
wil_err(wil, "%s: do not apply ba, rc(%d), status(%d)\n",
__func__, rc, status);
goto out;
}
/* apply */
r = wil_tid_ampdu_rx_alloc(wil, agg_wsize, ssn);
@ -376,143 +360,37 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
wil_tid_ampdu_rx_free(wil, sta->tid_rx[tid]);
sta->tid_rx[tid] = r;
spin_unlock_bh(&sta->tid_rx_lock);
out:
return rc;
}
void wil_back_rx_flush(struct wil6210_priv *wil)
/* BACK - Tx side (originator) */
int wil_addba_tx_request(struct wil6210_priv *wil, u8 ringid, u16 wsize)
{
struct wil_back_rx *evt, *t;
wil_dbg_misc(wil, "%s()\n", __func__);
mutex_lock(&wil->back_rx_mutex);
list_for_each_entry_safe(evt, t, &wil->back_rx_pending, list) {
list_del(&evt->list);
kfree(evt);
}
mutex_unlock(&wil->back_rx_mutex);
}
/* Retrieve next ADDBA request from the pending list */
static struct list_head *next_back_rx(struct wil6210_priv *wil)
{
struct list_head *ret = NULL;
mutex_lock(&wil->back_rx_mutex);
if (!list_empty(&wil->back_rx_pending)) {
ret = wil->back_rx_pending.next;
list_del(ret);
}
mutex_unlock(&wil->back_rx_mutex);
return ret;
}
void wil_back_rx_worker(struct work_struct *work)
{
struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
back_rx_worker);
struct wil_back_rx *evt;
struct list_head *lh;
while ((lh = next_back_rx(wil)) != NULL) {
evt = list_entry(lh, struct wil_back_rx, list);
wil_back_rx_handle(wil, evt);
kfree(evt);
}
}
/* BACK - Tx (originator) side */
static void wil_back_tx_handle(struct wil6210_priv *wil,
struct wil_back_tx *req)
{
struct vring_tx_data *txdata = &wil->vring_tx_data[req->ringid];
int rc;
u8 agg_wsize = wil_agg_size(wil, wsize);
u16 agg_timeout = 0;
struct vring_tx_data *txdata = &wil->vring_tx_data[ringid];
int rc = 0;
if (txdata->addba_in_progress) {
wil_dbg_misc(wil, "ADDBA for vring[%d] already in progress\n",
req->ringid);
return;
ringid);
goto out;
}
if (txdata->agg_wsize) {
wil_dbg_misc(wil,
"ADDBA for vring[%d] already established wsize %d\n",
req->ringid, txdata->agg_wsize);
return;
"ADDBA for vring[%d] already done for wsize %d\n",
ringid, txdata->agg_wsize);
goto out;
}
txdata->addba_in_progress = true;
rc = wmi_addba(wil, req->ringid, req->agg_wsize, req->agg_timeout);
if (rc)
rc = wmi_addba(wil, ringid, agg_wsize, agg_timeout);
if (rc) {
wil_err(wil, "%s: wmi_addba failed, rc (%d)", __func__, rc);
txdata->addba_in_progress = false;
}
static struct list_head *next_back_tx(struct wil6210_priv *wil)
{
struct list_head *ret = NULL;
mutex_lock(&wil->back_tx_mutex);
if (!list_empty(&wil->back_tx_pending)) {
ret = wil->back_tx_pending.next;
list_del(ret);
}
mutex_unlock(&wil->back_tx_mutex);
return ret;
}
void wil_back_tx_worker(struct work_struct *work)
{
struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
back_tx_worker);
struct wil_back_tx *evt;
struct list_head *lh;
while ((lh = next_back_tx(wil)) != NULL) {
evt = list_entry(lh, struct wil_back_tx, list);
wil_back_tx_handle(wil, evt);
kfree(evt);
}
}
void wil_back_tx_flush(struct wil6210_priv *wil)
{
struct wil_back_tx *evt, *t;
wil_dbg_misc(wil, "%s()\n", __func__);
mutex_lock(&wil->back_tx_mutex);
list_for_each_entry_safe(evt, t, &wil->back_tx_pending, list) {
list_del(&evt->list);
kfree(evt);
}
mutex_unlock(&wil->back_tx_mutex);
}
int wil_addba_tx_request(struct wil6210_priv *wil, u8 ringid, u16 wsize)
{
struct wil_back_tx *req = kzalloc(sizeof(*req), GFP_KERNEL);
if (!req)
return -ENOMEM;
req->ringid = ringid;
req->agg_wsize = wil_agg_size(wil, wsize);
req->agg_timeout = 0;
mutex_lock(&wil->back_tx_mutex);
list_add_tail(&req->list, &wil->back_tx_pending);
mutex_unlock(&wil->back_tx_mutex);
queue_work(wil->wq_service, &wil->back_tx_worker);
return 0;
out:
return rc;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
* Copyright (c) 2013-2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -37,39 +37,40 @@ static inline void trace_ ## name(proto) {}
#endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */
DECLARE_EVENT_CLASS(wil6210_wmi,
TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
TP_PROTO(struct wmi_cmd_hdr *wmi, void *buf, u16 buf_len),
TP_ARGS(wmi, buf, buf_len),
TP_STRUCT__entry(
__field(u8, mid)
__field(u16, id)
__field(u32, timestamp)
__field(u16, command_id)
__field(u32, fw_timestamp)
__field(u16, buf_len)
__dynamic_array(u8, buf, buf_len)
),
TP_fast_assign(
__entry->mid = wmi->mid;
__entry->id = le16_to_cpu(wmi->id);
__entry->timestamp = le32_to_cpu(wmi->timestamp);
__entry->command_id = le16_to_cpu(wmi->command_id);
__entry->fw_timestamp = le32_to_cpu(wmi->fw_timestamp);
__entry->buf_len = buf_len;
memcpy(__get_dynamic_array(buf), buf, buf_len);
),
TP_printk(
"MID %d id 0x%04x len %d timestamp %d",
__entry->mid, __entry->id, __entry->buf_len, __entry->timestamp
__entry->mid, __entry->command_id, __entry->buf_len,
__entry->fw_timestamp
)
);
DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd,
TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
TP_PROTO(struct wmi_cmd_hdr *wmi, void *buf, u16 buf_len),
TP_ARGS(wmi, buf, buf_len)
);
DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event,
TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
TP_PROTO(struct wmi_cmd_hdr *wmi, void *buf, u16 buf_len),
TP_ARGS(wmi, buf, buf_len)
);

View File

@ -549,6 +549,60 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count)
return rc;
}
/**
* reverse_memcmp - Compare two areas of memory, in reverse order
* @cs: One area of memory
* @ct: Another area of memory
* @count: The size of the area.
*
* Cut'n'paste from original memcmp (see lib/string.c)
* with minimal modifications
*/
static int reverse_memcmp(const void *cs, const void *ct, size_t count)
{
const unsigned char *su1, *su2;
int res = 0;
for (su1 = cs + count - 1, su2 = ct + count - 1; count > 0;
--su1, --su2, count--) {
res = *su1 - *su2;
if (res)
break;
}
return res;
}
static int wil_rx_crypto_check(struct wil6210_priv *wil, struct sk_buff *skb)
{
struct vring_rx_desc *d = wil_skb_rxdesc(skb);
int cid = wil_rxdesc_cid(d);
int tid = wil_rxdesc_tid(d);
int key_id = wil_rxdesc_key_id(d);
int mc = wil_rxdesc_mcast(d);
struct wil_sta_info *s = &wil->sta[cid];
struct wil_tid_crypto_rx *c = mc ? &s->group_crypto_rx :
&s->tid_crypto_rx[tid];
struct wil_tid_crypto_rx_single *cc = &c->key_id[key_id];
const u8 *pn = (u8 *)&d->mac.pn_15_0;
if (!cc->key_set) {
wil_err_ratelimited(wil,
"Key missing. CID %d TID %d MCast %d KEY_ID %d\n",
cid, tid, mc, key_id);
return -EINVAL;
}
if (reverse_memcmp(pn, cc->pn, IEEE80211_GCMP_PN_LEN) <= 0) {
wil_err_ratelimited(wil,
"Replay attack. CID %d TID %d MCast %d KEY_ID %d PN %6phN last %6phN\n",
cid, tid, mc, key_id, pn, cc->pn);
return -EINVAL;
}
memcpy(cc->pn, pn, IEEE80211_GCMP_PN_LEN);
return 0;
}
/*
* Pass Rx packet to the netif. Update statistics.
* Called in softirq context (NAPI poll).
@ -561,6 +615,7 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
unsigned int len = skb->len;
struct vring_rx_desc *d = wil_skb_rxdesc(skb);
int cid = wil_rxdesc_cid(d); /* always 0..7, no need to check */
int security = wil_rxdesc_security(d);
struct ethhdr *eth = (void *)skb->data;
/* here looking for DA, not A1, thus Rxdesc's 'mcast' indication
* is not suitable, need to look at data
@ -586,6 +641,13 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
skb_orphan(skb);
if (security && (wil_rx_crypto_check(wil, skb) != 0)) {
rc = GRO_DROP;
dev_kfree_skb(skb);
stats->rx_replay++;
goto stats;
}
if (wdev->iftype == NL80211_IFTYPE_AP && !wil->ap_isolate) {
if (mcast) {
/* send multicast frames both to higher layers in
@ -627,6 +689,7 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n",
len, gro_res_str[rc]);
}
stats:
/* statistics. rc set to GRO_NORMAL for AP bridging */
if (unlikely(rc == GRO_DROP)) {
ndev->stats.rx_dropped++;
@ -757,7 +820,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
},
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
struct wmi_vring_cfg_done_event cmd;
} __packed reply;
struct vring *vring = &wil->vring_tx[id];
@ -834,7 +897,7 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size)
},
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
struct wmi_vring_cfg_done_event cmd;
} __packed reply;
struct vring *vring = &wil->vring_tx[id];

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2014 Qualcomm Atheros, Inc.
* Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -480,6 +480,16 @@ static inline int wil_rxdesc_ext_subtype(struct vring_rx_desc *d)
return WIL_GET_BITS(d->mac.d0, 28, 31);
}
static inline int wil_rxdesc_key_id(struct vring_rx_desc *d)
{
return WIL_GET_BITS(d->mac.d1, 4, 5);
}
static inline int wil_rxdesc_security(struct vring_rx_desc *d)
{
return WIL_GET_BITS(d->mac.d1, 7, 7);
}
static inline int wil_rxdesc_ds_bits(struct vring_rx_desc *d)
{
return WIL_GET_BITS(d->mac.d1, 8, 9);

View File

@ -22,6 +22,7 @@
#include <net/cfg80211.h>
#include <linux/timex.h>
#include <linux/types.h>
#include "wmi.h"
#include "wil_platform.h"
extern bool no_fw_recovery;
@ -131,6 +132,7 @@ struct RGF_ICR {
/* registers - FW addresses */
#define RGF_USER_USAGE_1 (0x880004)
#define RGF_USER_USAGE_6 (0x880018)
#define BIT_USER_OOB_MODE BIT(31)
#define RGF_USER_HW_MACHINE_STATE (0x8801dc)
#define HW_MACHINE_BOOT_DONE (0x3fffffd)
#define RGF_USER_USER_CPU_0 (0x8801e0)
@ -334,29 +336,11 @@ struct wil6210_mbox_hdr {
/* max. value for wil6210_mbox_hdr.len */
#define MAX_MBOXITEM_SIZE (240)
/**
* struct wil6210_mbox_hdr_wmi - WMI header
*
* @mid: MAC ID
* 00 - default, created by FW
* 01..0f - WiFi ports, driver to create
* 10..fe - debug
* ff - broadcast
* @id: command/event ID
* @timestamp: FW fills for events, free-running msec timer
*/
struct wil6210_mbox_hdr_wmi {
u8 mid;
u8 reserved;
__le16 id;
__le32 timestamp;
} __packed;
struct pending_wmi_event {
struct list_head list;
struct {
struct wil6210_mbox_hdr hdr;
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
u8 data[0];
} __packed event;
};
@ -455,6 +439,29 @@ struct wil_tid_ampdu_rx {
bool first_time; /* is it 1-st time this buffer used? */
};
/**
* struct wil_tid_crypto_rx_single - TID crypto information (Rx).
*
* @pn: GCMP PN for the session
* @key_set: valid key present
*/
struct wil_tid_crypto_rx_single {
u8 pn[IEEE80211_GCMP_PN_LEN];
bool key_set;
};
struct wil_tid_crypto_rx {
struct wil_tid_crypto_rx_single key_id[4];
};
struct wil_p2p_info {
struct ieee80211_channel listen_chan;
u8 discovery_started;
u64 cookie;
struct timer_list discovery_timer; /* listen/search duration */
struct work_struct discovery_expired_work; /* listen/search expire */
};
enum wil_sta_status {
wil_sta_unused = 0,
wil_sta_conn_pending = 1,
@ -474,6 +481,7 @@ struct wil_net_stats {
unsigned long rx_non_data_frame;
unsigned long rx_short_frame;
unsigned long rx_large_frame;
unsigned long rx_replay;
u16 last_mcs_rx;
u64 rx_per_mcs[WIL_MCS_MAX + 1];
};
@ -495,6 +503,8 @@ struct wil_sta_info {
spinlock_t tid_rx_lock; /* guarding tid_rx array */
unsigned long tid_rx_timer_expired[BITS_TO_LONGS(WIL_STA_TID_NUM)];
unsigned long tid_rx_stop_requested[BITS_TO_LONGS(WIL_STA_TID_NUM)];
struct wil_tid_crypto_rx tid_crypto_rx[WIL_STA_TID_NUM];
struct wil_tid_crypto_rx group_crypto_rx;
};
enum {
@ -507,24 +517,6 @@ enum {
hw_capability_last
};
struct wil_back_rx {
struct list_head list;
/* request params, converted to CPU byte order - what we asked for */
u8 cidxtid;
u8 dialog_token;
u16 ba_param_set;
u16 ba_timeout;
u16 ba_seq_ctrl;
};
struct wil_back_tx {
struct list_head list;
/* request params, converted to CPU byte order - what we asked for */
u8 ringid;
u8 agg_wsize;
u16 agg_timeout;
};
struct wil_probe_client_req {
struct list_head list;
u64 cookie;
@ -595,13 +587,6 @@ struct wil6210_priv {
spinlock_t wmi_ev_lock;
struct napi_struct napi_rx;
struct napi_struct napi_tx;
/* BACK */
struct list_head back_rx_pending;
struct mutex back_rx_mutex; /* protect @back_rx_pending */
struct work_struct back_rx_worker;
struct list_head back_tx_pending;
struct mutex back_tx_mutex; /* protect @back_tx_pending */
struct work_struct back_tx_worker;
/* keep alive */
struct list_head probe_client_pending;
struct mutex probe_client_mutex; /* protect @probe_client_pending */
@ -622,11 +607,21 @@ struct wil6210_priv {
/* debugfs */
struct dentry *debug;
struct debugfs_blob_wrapper blobs[ARRAY_SIZE(fw_mapping)];
u8 discovery_mode;
void *platform_handle;
struct wil_platform_ops platform_ops;
struct pmc_ctx pmc;
bool pbss;
struct wil_p2p_info p2p;
/* P2P_DEVICE vif */
struct wireless_dev *p2p_wdev;
struct mutex p2p_wdev_mutex; /* protect @p2p_wdev */
struct wireless_dev *radio_wdev;
};
#define wil_to_wiphy(i) (i->wdev->wiphy)
@ -722,6 +717,7 @@ void wil_priv_deinit(struct wil6210_priv *wil);
int wil_reset(struct wil6210_priv *wil, bool no_fw);
void wil_fw_error_recovery(struct wil6210_priv *wil);
void wil_set_recovery_state(struct wil6210_priv *wil, int state);
bool wil_is_recovery_blocked(struct wil6210_priv *wil);
int wil_up(struct wil6210_priv *wil);
int __wil_up(struct wil6210_priv *wil);
int wil_down(struct wil6210_priv *wil);
@ -752,7 +748,6 @@ int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
int wmi_echo(struct wil6210_priv *wil);
int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie);
int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring);
int wmi_p2p_cfg(struct wil6210_priv *wil, int channel);
int wmi_rxon(struct wil6210_priv *wil, bool on);
int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r);
int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason,
@ -765,11 +760,7 @@ int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token,
int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid,
u8 dialog_token, __le16 ba_param_set,
__le16 ba_timeout, __le16 ba_seq_ctrl);
void wil_back_rx_worker(struct work_struct *work);
void wil_back_rx_flush(struct wil6210_priv *wil);
int wil_addba_tx_request(struct wil6210_priv *wil, u8 ringid, u16 wsize);
void wil_back_tx_worker(struct work_struct *work);
void wil_back_tx_flush(struct wil6210_priv *wil);
void wil6210_clear_irq(struct wil6210_priv *wil);
int wil6210_init_irq(struct wil6210_priv *wil, int irq, bool use_msi);
@ -779,6 +770,24 @@ void wil_unmask_irq(struct wil6210_priv *wil);
void wil_configure_interrupt_moderation(struct wil6210_priv *wil);
void wil_disable_irq(struct wil6210_priv *wil);
void wil_enable_irq(struct wil6210_priv *wil);
/* P2P */
void wil_p2p_discovery_timer_fn(ulong x);
int wil_p2p_search(struct wil6210_priv *wil,
struct cfg80211_scan_request *request);
int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration,
struct ieee80211_channel *chan, u64 *cookie);
u8 wil_p2p_stop_discovery(struct wil6210_priv *wil);
int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie);
void wil_p2p_listen_expired(struct work_struct *work);
void wil_p2p_search_expired(struct work_struct *work);
/* WMI for P2P */
int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi);
int wmi_start_listen(struct wil6210_priv *wil);
int wmi_start_search(struct wil6210_priv *wil);
int wmi_stop_discovery(struct wil6210_priv *wil);
int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params,
u64 *cookie);
@ -790,10 +799,11 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
struct wireless_dev *wil_cfg80211_init(struct device *dev);
void wil_wdev_free(struct wil6210_priv *wil);
void wil_p2p_wdev_free(struct wil6210_priv *wil);
int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
u8 chan, u8 hidden_ssid);
u8 chan, u8 hidden_ssid, u8 is_go);
int wmi_pcp_stop(struct wil6210_priv *wil);
void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
u16 reason_code, bool from_event);

View File

@ -19,6 +19,12 @@
struct device;
enum wil_platform_event {
WIL_PLATFORM_EVT_FW_CRASH = 0,
WIL_PLATFORM_EVT_PRE_RESET = 1,
WIL_PLATFORM_EVT_FW_RDY = 2,
};
/**
* struct wil_platform_ops - wil platform module calls from this
* driver to platform driver
@ -28,7 +34,7 @@ struct wil_platform_ops {
int (*suspend)(void *handle);
int (*resume)(void *handle);
void (*uninit)(void *handle);
int (*notify_crash)(void *handle);
int (*notify)(void *handle, enum wil_platform_event evt);
};
/**

View File

@ -176,7 +176,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
{
struct {
struct wil6210_mbox_hdr hdr;
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
} __packed cmd = {
.hdr = {
.type = WIL_MBOX_HDR_TYPE_WMI,
@ -185,7 +185,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
},
.wmi = {
.mid = 0,
.id = cpu_to_le16(cmdid),
.command_id = cpu_to_le16(cmdid),
},
};
struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx;
@ -368,6 +368,8 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
wil_hex_dump_wmi("IE ", DUMP_PREFIX_OFFSET, 16, 1, ie_buf,
ie_len, true);
wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap);
bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
d_len, signal, GFP_KERNEL);
if (bss) {
@ -378,8 +380,10 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
wil_err(wil, "cfg80211_inform_bss_frame() failed\n");
}
} else {
cfg80211_rx_mgmt(wil->wdev, freq, signal,
mutex_lock(&wil->p2p_wdev_mutex);
cfg80211_rx_mgmt(wil->radio_wdev, freq, signal,
(void *)rx_mgmt_frame, d_len, 0);
mutex_unlock(&wil->p2p_wdev_mutex);
}
}
@ -406,7 +410,10 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
wil->scan_request, aborted);
del_timer_sync(&wil->scan_timer);
mutex_lock(&wil->p2p_wdev_mutex);
cfg80211_scan_done(wil->scan_request, aborted);
wil->radio_wdev = wil->wdev;
mutex_unlock(&wil->p2p_wdev_mutex);
wil->scan_request = NULL;
} else {
wil_err(wil, "SCAN_COMPLETE while not scanning\n");
@ -487,6 +494,14 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
return;
}
del_timer_sync(&wil->connect_timer);
} else if ((wdev->iftype == NL80211_IFTYPE_AP) ||
(wdev->iftype == NL80211_IFTYPE_P2P_GO)) {
if (wil->sta[evt->cid].status != wil_sta_unused) {
wil_err(wil, "%s: AP: Invalid status %d for CID %d\n",
__func__, wil->sta[evt->cid].status, evt->cid);
mutex_unlock(&wil->mutex);
return;
}
}
/* FIXME FW can transmit only ucast frames to peer */
@ -648,7 +663,7 @@ static void wmi_evt_vring_en(struct wil6210_priv *wil, int id, void *d, int len)
static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d,
int len)
{
struct wmi_vring_ba_status_event *evt = d;
struct wmi_ba_status_event *evt = d;
struct vring_tx_data *txdata;
wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d AMSDU%s\n",
@ -834,10 +849,10 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
offsetof(struct wil6210_mbox_ring_desc, sync), 0);
/* indicate */
if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
(len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi;
u16 id = le16_to_cpu(wmi->id);
u32 tstamp = le32_to_cpu(wmi->timestamp);
(len >= sizeof(struct wmi_cmd_hdr))) {
struct wmi_cmd_hdr *wmi = &evt->event.wmi;
u16 id = le16_to_cpu(wmi->command_id);
u32 tstamp = le32_to_cpu(wmi->fw_timestamp);
spin_lock_irqsave(&wil->wmi_ev_lock, flags);
if (wil->reply_id && wil->reply_id == id) {
if (wil->reply_buf) {
@ -947,7 +962,7 @@ int wmi_set_mac_address(struct wil6210_priv *wil, void *addr)
}
int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
u8 chan, u8 hidden_ssid)
u8 chan, u8 hidden_ssid, u8 is_go)
{
int rc;
@ -958,9 +973,10 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
.channel = chan - 1,
.pcp_max_assoc_sta = max_assoc_sta,
.hidden_ssid = hidden_ssid,
.is_go = is_go,
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
struct wmi_pcp_started_event evt;
} __packed reply;
@ -1014,7 +1030,7 @@ int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid)
{
int rc;
struct {
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
struct wmi_set_ssid_cmd cmd;
} __packed reply;
int len; /* reply.cmd.ssid_len in CPU order */
@ -1047,7 +1063,7 @@ int wmi_get_channel(struct wil6210_priv *wil, int *channel)
{
int rc;
struct {
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
struct wmi_set_pcp_channel_cmd cmd;
} __packed reply;
@ -1064,14 +1080,86 @@ int wmi_get_channel(struct wil6210_priv *wil, int *channel)
return 0;
}
int wmi_p2p_cfg(struct wil6210_priv *wil, int channel)
int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi)
{
int rc;
struct wmi_p2p_cfg_cmd cmd = {
.discovery_mode = WMI_DISCOVERY_MODE_NON_OFFLOAD,
.discovery_mode = WMI_DISCOVERY_MODE_PEER2PEER,
.bcon_interval = cpu_to_le16(bi),
.channel = channel - 1,
};
struct {
struct wmi_cmd_hdr wmi;
struct wmi_p2p_cfg_done_event evt;
} __packed reply;
return wmi_send(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd));
wil_dbg_wmi(wil, "sending WMI_P2P_CFG_CMDID\n");
rc = wmi_call(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd),
WMI_P2P_CFG_DONE_EVENTID, &reply, sizeof(reply), 300);
if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) {
wil_err(wil, "P2P_CFG failed. status %d\n", reply.evt.status);
rc = -EINVAL;
}
return rc;
}
int wmi_start_listen(struct wil6210_priv *wil)
{
int rc;
struct {
struct wmi_cmd_hdr wmi;
struct wmi_listen_started_event evt;
} __packed reply;
wil_dbg_wmi(wil, "sending WMI_START_LISTEN_CMDID\n");
rc = wmi_call(wil, WMI_START_LISTEN_CMDID, NULL, 0,
WMI_LISTEN_STARTED_EVENTID, &reply, sizeof(reply), 300);
if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) {
wil_err(wil, "device failed to start listen. status %d\n",
reply.evt.status);
rc = -EINVAL;
}
return rc;
}
int wmi_start_search(struct wil6210_priv *wil)
{
int rc;
struct {
struct wmi_cmd_hdr wmi;
struct wmi_search_started_event evt;
} __packed reply;
wil_dbg_wmi(wil, "sending WMI_START_SEARCH_CMDID\n");
rc = wmi_call(wil, WMI_START_SEARCH_CMDID, NULL, 0,
WMI_SEARCH_STARTED_EVENTID, &reply, sizeof(reply), 300);
if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) {
wil_err(wil, "device failed to start search. status %d\n",
reply.evt.status);
rc = -EINVAL;
}
return rc;
}
int wmi_stop_discovery(struct wil6210_priv *wil)
{
int rc;
wil_dbg_wmi(wil, "sending WMI_DISCOVERY_STOP_CMDID\n");
rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, NULL, 0,
WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 100);
if (rc)
wil_err(wil, "Failed to stop discovery\n");
return rc;
}
int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
@ -1155,7 +1243,7 @@ int wmi_rxon(struct wil6210_priv *wil, bool on)
{
int rc;
struct {
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
struct wmi_listen_started_event evt;
} __packed reply;
@ -1192,7 +1280,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
.host_thrsh = cpu_to_le16(rx_ring_overflow_thrsh),
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
struct wmi_cfg_rx_chain_done_event evt;
} __packed evt;
int rc;
@ -1246,7 +1334,7 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf)
.measure_mode = cpu_to_le32(TEMPERATURE_MEASURE_NOW),
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
struct wmi_temp_sense_done_event evt;
} __packed reply;
@ -1272,7 +1360,7 @@ int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason,
.disconnect_reason = cpu_to_le16(reason),
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
struct wmi_disconnect_event evt;
} __packed reply;
@ -1364,7 +1452,7 @@ int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token,
.ba_timeout = cpu_to_le16(timeout),
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
struct wmi_cmd_hdr wmi;
struct wmi_rcp_addba_resp_sent_event evt;
} __packed reply;
@ -1420,10 +1508,10 @@ static void wmi_event_handle(struct wil6210_priv *wil,
u16 len = le16_to_cpu(hdr->len);
if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) &&
(len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]);
(len >= sizeof(struct wmi_cmd_hdr))) {
struct wmi_cmd_hdr *wmi = (void *)(&hdr[1]);
void *evt_data = (void *)(&wmi[1]);
u16 id = le16_to_cpu(wmi->id);
u16 id = le16_to_cpu(wmi->command_id);
wil_dbg_wmi(wil, "Handle WMI 0x%04x (reply_id 0x%04x)\n",
id, wil->reply_id);

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,10 @@ struct ath9k_platform_data {
u32 gpio_mask;
u32 gpio_val;
u32 bt_active_pin;
u32 bt_priority_pin;
u32 wlan_active_pin;
bool endian_check;
bool is_clk_25mhz;
bool tx_gain_buffalo;