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:
commit
20ac1b325d
|
@ -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 ... ];
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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
|
||||
|
||||
/************************/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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__);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue