wireless-drivers-next patches for 4.16

Major changes:
 
 wil6210
 
 * add PCI device id for Talyn
 
 * support flashless device
 
 ath9k
 
 * improve RSSI/signal accuracy on AR9003 series
 
 mt76
 
 * validate CCMP PN from received frames to avoid replay attacks
 
 qtnfmac
 
 * support 64-bit network stats
 
 * report more hardware information to kernel log and some via ethtool
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJaa1x7AAoJEG4XJFUm622bdc8H/3JSWFnXlkrQvjZzL1NWR0Hn
 vUYou/W04mgzYtXIcscNL3otDa9TGwLW6Sr5wP0MDH/TGU9zXoEZxBxnYy4zq4np
 7ZP7M6VsOzOhlNNsTcNAiMGyObtIT4btrfKlmp2yvf3Sra7nHNJJmprFeuyH3f02
 vk5ZQWjMcEAe0afBGPcHrzndoAvVqIHHOAbM/w694n/8cdT8edcQeQ8rwlFO6ENU
 db9yqkr4RvjWv9oJwa2us6d0lolJDk8ATWhDTyZL40X9yk84XR7E0jMdN5LQ/CJj
 CCCMWsDYjUpEDUJVmwLdXzGavgo43QAs9a+O+z7MDJ4uwqYkZh3rzQZay+dYLKk=
 =D4LX
 -----END PGP SIGNATURE-----

Merge tag 'wireless-drivers-next-for-davem-2018-01-26' of git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next

Kalle Valo says:

====================
wireless-drivers-next patches for 4.16

Major changes:

wil6210

* add PCI device id for Talyn

* support flashless device

ath9k

* improve RSSI/signal accuracy on AR9003 series

mt76

* validate CCMP PN from received frames to avoid replay attacks

qtnfmac

* support 64-bit network stats

* report more hardware information to kernel log and some via ethtool
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2018-01-28 22:00:16 -05:00
commit 868c36dcc9
49 changed files with 2212 additions and 382 deletions

View File

@ -3310,6 +3310,12 @@ static int ar9300_eeprom_restore_internal(struct ath_hw *ah,
if (ar9300_check_eeprom_header(ah, read, cptr))
goto found;
cptr = AR9300_BASE_ADDR_4K;
ath_dbg(common, EEPROM, "Trying EEPROM access at Address 0x%04x\n",
cptr);
if (ar9300_check_eeprom_header(ah, read, cptr))
goto found;
cptr = AR9300_BASE_ADDR_512;
ath_dbg(common, EEPROM, "Trying EEPROM access at Address 0x%04x\n",
cptr);
@ -3430,6 +3436,60 @@ static u32 ar9003_dump_modal_eeprom(char *buf, u32 len, u32 size,
return len;
}
static u32 ar9003_dump_cal_data(struct ath_hw *ah, char *buf, u32 len, u32 size,
bool is_2g)
{
struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
struct ar9300_base_eep_hdr *pBase;
struct ar9300_cal_data_per_freq_op_loop *cal_pier;
int cal_pier_nr;
int freq;
int i, j;
pBase = &eep->baseEepHeader;
if (is_2g)
cal_pier_nr = AR9300_NUM_2G_CAL_PIERS;
else
cal_pier_nr = AR9300_NUM_5G_CAL_PIERS;
for (i = 0; i < AR9300_MAX_CHAINS; i++) {
if (!((pBase->txrxMask >> i) & 1))
continue;
len += snprintf(buf + len, size - len, "Chain %d\n", i);
len += snprintf(buf + len, size - len,
"Freq\t ref\tvolt\ttemp\tnf_cal\tnf_pow\trx_temp\n");
for (j = 0; j < cal_pier_nr; j++) {
if (is_2g) {
cal_pier = &eep->calPierData2G[i][j];
freq = 2300 + eep->calFreqPier2G[j];
} else {
cal_pier = &eep->calPierData5G[i][j];
freq = 4800 + eep->calFreqPier5G[j] * 5;
}
len += snprintf(buf + len, size - len,
"%d\t", freq);
len += snprintf(buf + len, size - len,
"%d\t%d\t%d\t%d\t%d\t%d\n",
cal_pier->refPower,
cal_pier->voltMeas,
cal_pier->tempMeas,
cal_pier->rxTempMeas ?
N2DBM(cal_pier->rxNoisefloorCal) : 0,
cal_pier->rxTempMeas ?
N2DBM(cal_pier->rxNoisefloorPower) : 0,
cal_pier->rxTempMeas);
}
}
return len;
}
static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
u8 *buf, u32 len, u32 size)
{
@ -3441,10 +3501,18 @@ static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr,
"%20s :\n", "2GHz modal Header");
len = ar9003_dump_modal_eeprom(buf, len, size,
&eep->modalHeader2G);
len += scnprintf(buf + len, size - len,
len += scnprintf(buf + len, size - len, "Calibration data\n");
len = ar9003_dump_cal_data(ah, buf, len, size, true);
len += snprintf(buf + len, size - len,
"%20s :\n", "5GHz modal Header");
len = ar9003_dump_modal_eeprom(buf, len, size,
&eep->modalHeader5G);
len += snprintf(buf + len, size - len, "Calibration data\n");
len = ar9003_dump_cal_data(ah, buf, len, size, false);
goto out;
}
@ -4683,7 +4751,8 @@ static int ar9003_hw_cal_pier_get(struct ath_hw *ah,
int ichain,
int *pfrequency,
int *pcorrection,
int *ptemperature, int *pvoltage)
int *ptemperature, int *pvoltage,
int *pnf_cal, int *pnf_power)
{
u8 *pCalPier;
struct ar9300_cal_data_per_freq_op_loop *pCalPierStruct;
@ -4725,6 +4794,10 @@ static int ar9003_hw_cal_pier_get(struct ath_hw *ah,
*pcorrection = pCalPierStruct->refPower;
*ptemperature = pCalPierStruct->tempMeas;
*pvoltage = pCalPierStruct->voltMeas;
*pnf_cal = pCalPierStruct->rxTempMeas ?
N2DBM(pCalPierStruct->rxNoisefloorCal) : 0;
*pnf_power = pCalPierStruct->rxTempMeas ?
N2DBM(pCalPierStruct->rxNoisefloorPower) : 0;
return 0;
}
@ -4889,14 +4962,18 @@ static int ar9003_hw_calibration_apply(struct ath_hw *ah, int frequency)
int mode;
int lfrequency[AR9300_MAX_CHAINS],
lcorrection[AR9300_MAX_CHAINS],
ltemperature[AR9300_MAX_CHAINS], lvoltage[AR9300_MAX_CHAINS];
ltemperature[AR9300_MAX_CHAINS], lvoltage[AR9300_MAX_CHAINS],
lnf_cal[AR9300_MAX_CHAINS], lnf_pwr[AR9300_MAX_CHAINS];
int hfrequency[AR9300_MAX_CHAINS],
hcorrection[AR9300_MAX_CHAINS],
htemperature[AR9300_MAX_CHAINS], hvoltage[AR9300_MAX_CHAINS];
htemperature[AR9300_MAX_CHAINS], hvoltage[AR9300_MAX_CHAINS],
hnf_cal[AR9300_MAX_CHAINS], hnf_pwr[AR9300_MAX_CHAINS];
int fdiff;
int correction[AR9300_MAX_CHAINS],
voltage[AR9300_MAX_CHAINS], temperature[AR9300_MAX_CHAINS];
int pfrequency, pcorrection, ptemperature, pvoltage;
voltage[AR9300_MAX_CHAINS], temperature[AR9300_MAX_CHAINS],
nf_cal[AR9300_MAX_CHAINS], nf_pwr[AR9300_MAX_CHAINS];
int pfrequency, pcorrection, ptemperature, pvoltage,
pnf_cal, pnf_pwr;
struct ath_common *common = ath9k_hw_common(ah);
mode = (frequency >= 4000);
@ -4914,7 +4991,8 @@ static int ar9003_hw_calibration_apply(struct ath_hw *ah, int frequency)
for (ipier = 0; ipier < npier; ipier++) {
if (!ar9003_hw_cal_pier_get(ah, mode, ipier, ichain,
&pfrequency, &pcorrection,
&ptemperature, &pvoltage)) {
&ptemperature, &pvoltage,
&pnf_cal, &pnf_pwr)) {
fdiff = frequency - pfrequency;
/*
@ -4936,6 +5014,8 @@ static int ar9003_hw_calibration_apply(struct ath_hw *ah, int frequency)
htemperature[ichain] =
ptemperature;
hvoltage[ichain] = pvoltage;
hnf_cal[ichain] = pnf_cal;
hnf_pwr[ichain] = pnf_pwr;
}
}
if (fdiff >= 0) {
@ -4952,6 +5032,8 @@ static int ar9003_hw_calibration_apply(struct ath_hw *ah, int frequency)
ltemperature[ichain] =
ptemperature;
lvoltage[ichain] = pvoltage;
lnf_cal[ichain] = pnf_cal;
lnf_pwr[ichain] = pnf_pwr;
}
}
}
@ -4960,15 +5042,20 @@ static int ar9003_hw_calibration_apply(struct ath_hw *ah, int frequency)
/* interpolate */
for (ichain = 0; ichain < AR9300_MAX_CHAINS; ichain++) {
ath_dbg(common, EEPROM, "ch=%d f=%d low=%d %d h=%d %d\n",
ath_dbg(common, EEPROM,
"ch=%d f=%d low=%d %d h=%d %d n=%d %d p=%d %d\n",
ichain, frequency, lfrequency[ichain],
lcorrection[ichain], hfrequency[ichain],
hcorrection[ichain]);
hcorrection[ichain], lnf_cal[ichain],
hnf_cal[ichain], lnf_pwr[ichain],
hnf_pwr[ichain]);
/* they're the same, so just pick one */
if (hfrequency[ichain] == lfrequency[ichain]) {
correction[ichain] = lcorrection[ichain];
voltage[ichain] = lvoltage[ichain];
temperature[ichain] = ltemperature[ichain];
nf_cal[ichain] = lnf_cal[ichain];
nf_pwr[ichain] = lnf_pwr[ichain];
}
/* the low frequency is good */
else if (frequency - lfrequency[ichain] < 1000) {
@ -4992,12 +5079,26 @@ static int ar9003_hw_calibration_apply(struct ath_hw *ah, int frequency)
hfrequency[ichain],
lvoltage[ichain],
hvoltage[ichain]);
nf_cal[ichain] = interpolate(frequency,
lfrequency[ichain],
hfrequency[ichain],
lnf_cal[ichain],
hnf_cal[ichain]);
nf_pwr[ichain] = interpolate(frequency,
lfrequency[ichain],
hfrequency[ichain],
lnf_pwr[ichain],
hnf_pwr[ichain]);
}
/* only low is good, use it */
else {
correction[ichain] = lcorrection[ichain];
temperature[ichain] = ltemperature[ichain];
voltage[ichain] = lvoltage[ichain];
nf_cal[ichain] = lnf_cal[ichain];
nf_pwr[ichain] = lnf_pwr[ichain];
}
}
/* only high is good, use it */
@ -5005,10 +5106,14 @@ static int ar9003_hw_calibration_apply(struct ath_hw *ah, int frequency)
correction[ichain] = hcorrection[ichain];
temperature[ichain] = htemperature[ichain];
voltage[ichain] = hvoltage[ichain];
nf_cal[ichain] = hnf_cal[ichain];
nf_pwr[ichain] = hnf_pwr[ichain];
} else { /* nothing is good, presume 0???? */
correction[ichain] = 0;
temperature[ichain] = 0;
voltage[ichain] = 0;
nf_cal[ichain] = 0;
nf_pwr[ichain] = 0;
}
}
@ -5019,6 +5124,16 @@ static int ar9003_hw_calibration_apply(struct ath_hw *ah, int frequency)
"for frequency=%d, calibration correction = %d %d %d\n",
frequency, correction[0], correction[1], correction[2]);
/* Store calibrated noise floor values */
for (ichain = 0; ichain < AR5416_MAX_CHAINS; ichain++)
if (mode) {
ah->nf_5g.cal[ichain] = nf_cal[ichain];
ah->nf_5g.pwr[ichain] = nf_pwr[ichain];
} else {
ah->nf_2g.cal[ichain] = nf_cal[ichain];
ah->nf_2g.pwr[ichain] = nf_pwr[ichain];
}
return 0;
}

View File

@ -62,6 +62,16 @@
*/
#define AR9300_PWR_TABLE_OFFSET 0
/* Noise power data definitions
* units are: 4 x dBm - NOISE_PWR_DATA_OFFSET
* (e.g. -25 = (-25/4 - 90) = -96.25 dBm)
* range (for 6 signed bits) is (-32 to 31) + offset => -122dBm to -59dBm
* resolution (2 bits) is 0.25dBm
*/
#define NOISE_PWR_DATA_OFFSET -90
#define NOISE_PWR_DBM_2_INT(_p) ((((_p) + 3) >> 2) + NOISE_PWR_DATA_OFFSET)
#define N2DBM(_p) NOISE_PWR_DBM_2_INT(_p)
/* byte addressable */
#define AR9300_EEPROM_SIZE (16*1024)

View File

@ -58,19 +58,25 @@ static struct ath_nf_limits *ath9k_hw_get_nf_limits(struct ath_hw *ah,
}
static s16 ath9k_hw_get_default_nf(struct ath_hw *ah,
struct ath9k_channel *chan)
struct ath9k_channel *chan,
int chain)
{
return ath9k_hw_get_nf_limits(ah, chan)->nominal;
s16 calib_nf = ath9k_hw_get_nf_limits(ah, chan)->cal[chain];
if (calib_nf)
return calib_nf;
else
return ath9k_hw_get_nf_limits(ah, chan)->nominal;
}
s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan,
s16 nf)
{
s8 noise = ATH_DEFAULT_NOISE_FLOOR;
s8 noise = ath9k_hw_get_default_nf(ah, chan, 0);
if (nf) {
s8 delta = nf - ATH9K_NF_CAL_NOISE_THRESH -
ath9k_hw_get_default_nf(ah, chan);
ath9k_hw_get_default_nf(ah, chan, 0);
if (delta > 0)
noise += delta;
}
@ -240,7 +246,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
unsigned i, j;
u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
struct ath_common *common = ath9k_hw_common(ah);
s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
s16 default_nf = ath9k_hw_get_nf_limits(ah, chan)->nominal;
u32 bb_agc_ctl = REG_READ(ah, AR_PHY_AGC_CONTROL);
if (ah->caldata)
@ -258,8 +264,13 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
nfval = ah->nf_override;
else if (h)
nfval = h[i].privNF;
else
nfval = default_nf;
else {
/* Try to get calibrated noise floor value */
nfval =
ath9k_hw_get_nf_limits(ah, chan)->cal[i];
if (nfval > -60 || nfval < -127)
nfval = default_nf;
}
REG_RMW(ah, ah->nf_regs[i],
(((u32) nfval << 1) & 0x1ff), 0x1ff);
@ -429,20 +440,19 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
struct ath9k_channel *chan)
{
struct ath9k_nfcal_hist *h;
s16 default_nf;
int i, j;
int i, j, k = 0;
ah->caldata->channel = chan->channel;
ah->caldata->channelFlags = chan->channelFlags;
h = ah->caldata->nfCalHist;
default_nf = ath9k_hw_get_default_nf(ah, chan);
for (i = 0; i < NUM_NF_READINGS; i++) {
h[i].currIndex = 0;
h[i].privNF = default_nf;
h[i].privNF = ath9k_hw_get_default_nf(ah, chan, k);
h[i].invalidNFcount = AR_PHY_CCA_FILTERWINDOW_LENGTH;
for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
h[i].nfCalBuffer[j] = default_nf;
}
for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++)
h[i].nfCalBuffer[j] = h[i].privNF;
if (++k >= AR5416_MAX_CHAINS)
k = 0;
}
}

View File

@ -754,6 +754,8 @@ struct ath_nf_limits {
s16 max;
s16 min;
s16 nominal;
s16 cal[AR5416_MAX_CHAINS];
s16 pwr[AR5416_MAX_CHAINS];
};
enum ath_cal_list {

View File

@ -826,9 +826,9 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
sc->rx.discard_next = false;
/*
* Discard zero-length packets.
* Discard zero-length packets and packets smaller than an ACK
*/
if (!rx_stats->rs_datalen) {
if (rx_stats->rs_datalen < 10) {
RX_STAT_INC(rx_len_err);
goto corrupt;
}

View File

@ -236,6 +236,14 @@ static int wcn36xx_dxe_init_descs(struct device *dev, struct wcn36xx_dxe_ch *wcn
return 0;
}
static void wcn36xx_dxe_deinit_descs(struct device *dev, struct wcn36xx_dxe_ch *wcn_ch)
{
size_t size;
size = wcn_ch->desc_num * sizeof(struct wcn36xx_dxe_desc);
dma_free_coherent(dev, size,wcn_ch->cpu_addr, wcn_ch->dma_addr);
}
static void wcn36xx_dxe_init_tx_bd(struct wcn36xx_dxe_ch *ch,
struct wcn36xx_dxe_mem_pool *pool)
{
@ -722,7 +730,11 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
/***************************************/
/* Init descriptors for TX LOW channel */
/***************************************/
wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_l_ch);
ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_l_ch);
if (ret) {
dev_err(wcn->dev, "Error allocating descriptor\n");
return ret;
}
wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_l_ch, &wcn->data_mem_pool);
/* Write channel head to a NEXT register */
@ -740,7 +752,12 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
/***************************************/
/* Init descriptors for TX HIGH channel */
/***************************************/
wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_h_ch);
ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_tx_h_ch);
if (ret) {
dev_err(wcn->dev, "Error allocating descriptor\n");
goto out_err_txh_ch;
}
wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_h_ch, &wcn->mgmt_mem_pool);
/* Write channel head to a NEXT register */
@ -760,7 +777,12 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
/***************************************/
/* Init descriptors for RX LOW channel */
/***************************************/
wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_l_ch);
ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_l_ch);
if (ret) {
dev_err(wcn->dev, "Error allocating descriptor\n");
goto out_err_rxl_ch;
}
/* For RX we need to preallocated buffers */
wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch);
@ -790,7 +812,11 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
/***************************************/
/* Init descriptors for RX HIGH channel */
/***************************************/
wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_h_ch);
ret = wcn36xx_dxe_init_descs(wcn->dev, &wcn->dxe_rx_h_ch);
if (ret) {
dev_err(wcn->dev, "Error allocating descriptor\n");
goto out_err_rxh_ch;
}
/* For RX we need to prealocat buffers */
wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_h_ch);
@ -819,11 +845,19 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
ret = wcn36xx_dxe_request_irqs(wcn);
if (ret < 0)
goto out_err;
goto out_err_irq;
return 0;
out_err:
out_err_irq:
wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_rx_h_ch);
out_err_rxh_ch:
wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_rx_l_ch);
out_err_rxl_ch:
wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_tx_h_ch);
out_err_txh_ch:
wcn36xx_dxe_deinit_descs(wcn->dev, &wcn->dxe_tx_l_ch);
return ret;
}

View File

@ -1,4 +1,5 @@
/* Copyright (c) 2015 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -39,7 +40,8 @@ struct bl_dedicated_registers_v1 {
/* valid only for version 2 and above */
__le32 bl_assert_code; /* 0x880A58 BL Assert code */
__le32 bl_assert_blink; /* 0x880A5C BL Assert Branch */
__le32 bl_reserved[22]; /* 0x880A60 - 0x880AB4 */
__le32 bl_shutdown_handshake; /* 0x880A60 BL cleaner shutdown */
__le32 bl_reserved[21]; /* 0x880A64 - 0x880AB4 */
__le32 bl_magic_number; /* 0x880AB8 BL Magic number */
} __packed;
@ -58,4 +60,9 @@ struct bl_dedicated_registers_v0 {
u8 mac_address[6]; /* 0x880A4c BL mac address */
} __packed;
/* bits for bl_shutdown_handshake */
#define BL_SHUTDOWN_HS_GRTD BIT(0)
#define BL_SHUTDOWN_HS_RTD BIT(1)
#define BL_SHUTDOWN_HS_PROT_VER(x) WIL_GET_BITS(x, 28, 31)
#endif /* BOOT_LOADER_EXPORT_H_ */

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2014,2016 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -58,15 +59,30 @@ struct wil_fw_record_comment { /* type == wil_fw_type_comment */
u8 data[0]; /* free-form data [data_size], see above */
} __packed;
/* Comment header - common for all comment record types */
struct wil_fw_record_comment_hdr {
__le32 magic;
};
/* FW capabilities encoded inside a comment record */
#define WIL_FW_CAPABILITIES_MAGIC (0xabcddcba)
struct wil_fw_record_capabilities { /* type == wil_fw_type_comment */
/* identifies capabilities record */
__le32 magic;
struct wil_fw_record_comment_hdr hdr;
/* capabilities (variable size), see enum wmi_fw_capability */
u8 capabilities[0];
};
/* brd file info encoded inside a comment record */
#define WIL_BRD_FILE_MAGIC (0xabcddcbb)
struct wil_fw_record_brd_file { /* type == wil_fw_type_comment */
/* identifies brd file record */
struct wil_fw_record_comment_hdr hdr;
__le32 version;
__le32 base_addr;
__le32 max_size_bytes;
} __packed;
/* perform action
* data_size = @head.size - offsetof(struct wil_fw_record_action, data)
*/

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -128,14 +129,13 @@ static int fw_ignore_section(struct wil6210_priv *wil, const void *data,
}
static int
fw_handle_comment(struct wil6210_priv *wil, const void *data,
size_t size)
fw_handle_capabilities(struct wil6210_priv *wil, const void *data,
size_t size)
{
const struct wil_fw_record_capabilities *rec = data;
size_t capa_size;
if (size < sizeof(*rec) ||
le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC) {
if (size < sizeof(*rec)) {
wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
data, size, true);
return 0;
@ -151,8 +151,56 @@ fw_handle_comment(struct wil6210_priv *wil, const void *data,
return 0;
}
static int fw_handle_data(struct wil6210_priv *wil, const void *data,
size_t size)
static int
fw_handle_brd_file(struct wil6210_priv *wil, const void *data,
size_t size)
{
const struct wil_fw_record_brd_file *rec = data;
if (size < sizeof(*rec)) {
wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
data, size, true);
return 0;
}
wil->brd_file_addr = le32_to_cpu(rec->base_addr);
wil->brd_file_max_size = le32_to_cpu(rec->max_size_bytes);
wil_dbg_fw(wil, "brd_file_addr 0x%x, brd_file_max_size %d\n",
wil->brd_file_addr, wil->brd_file_max_size);
return 0;
}
static int
fw_handle_comment(struct wil6210_priv *wil, const void *data,
size_t size)
{
const struct wil_fw_record_comment_hdr *hdr = data;
u32 magic;
int rc = 0;
if (size < sizeof(*hdr))
return 0;
magic = le32_to_cpu(hdr->magic);
switch (magic) {
case WIL_FW_CAPABILITIES_MAGIC:
wil_dbg_fw(wil, "magic is WIL_FW_CAPABILITIES_MAGIC\n");
rc = fw_handle_capabilities(wil, data, size);
break;
case WIL_BRD_FILE_MAGIC:
wil_dbg_fw(wil, "magic is WIL_BRD_FILE_MAGIC\n");
rc = fw_handle_brd_file(wil, data, size);
break;
}
return rc;
}
static int __fw_handle_data(struct wil6210_priv *wil, const void *data,
size_t size, __le32 addr)
{
const struct wil_fw_record_data *d = data;
void __iomem *dst;
@ -163,16 +211,23 @@ static int fw_handle_data(struct wil6210_priv *wil, const void *data,
return -EINVAL;
}
if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
if (!wil_fw_addr_check(wil, &dst, addr, s, "address"))
return -EINVAL;
wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr),
s);
wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(addr), s);
wil_memcpy_toio_32(dst, d->data, s);
wmb(); /* finish before processing next record */
return 0;
}
static int fw_handle_data(struct wil6210_priv *wil, const void *data,
size_t size)
{
const struct wil_fw_record_data *d = data;
return __fw_handle_data(wil, data, size, d->addr);
}
static int fw_handle_fill(struct wil6210_priv *wil, const void *data,
size_t size)
{
@ -551,6 +606,100 @@ out:
return rc;
}
/**
* wil_brd_process - process section from BRD file
*
* Return error code
*/
static int wil_brd_process(struct wil6210_priv *wil, const void *data,
size_t size)
{
int rc = 0;
const struct wil_fw_record_head *hdr = data;
size_t s, hdr_sz;
u16 type;
/* Assuming the board file includes only one header record and one data
* record. Each record starts with wil_fw_record_head.
*/
if (size < sizeof(*hdr))
return -EINVAL;
s = sizeof(*hdr) + le32_to_cpu(hdr->size);
if (s > size)
return -EINVAL;
/* Skip the header record and handle the data record */
hdr = (const void *)hdr + s;
size -= s;
if (size < sizeof(*hdr))
return -EINVAL;
hdr_sz = le32_to_cpu(hdr->size);
if (wil->brd_file_max_size && hdr_sz > wil->brd_file_max_size)
return -EINVAL;
if (sizeof(*hdr) + hdr_sz > size)
return -EINVAL;
if (hdr_sz % 4) {
wil_err_fw(wil, "unaligned record size: %zu\n",
hdr_sz);
return -EINVAL;
}
type = le16_to_cpu(hdr->type);
if (type != wil_fw_type_data) {
wil_err_fw(wil, "invalid record type for board file: %d\n",
type);
return -EINVAL;
}
if (hdr_sz < sizeof(struct wil_fw_record_data)) {
wil_err_fw(wil, "data record too short: %zu\n", hdr_sz);
return -EINVAL;
}
wil_dbg_fw(wil, "using addr from fw file: [0x%08x]\n",
wil->brd_file_addr);
rc = __fw_handle_data(wil, &hdr[1], hdr_sz,
cpu_to_le32(wil->brd_file_addr));
return rc;
}
/**
* wil_request_board - Request board file
*
* Request board image from the file
* board file address and max size are read from FW file
* during initialization.
* brd file shall include one header and one data section.
*
* Return error code
*/
int wil_request_board(struct wil6210_priv *wil, const char *name)
{
int rc, dlen;
const struct firmware *brd;
rc = request_firmware(&brd, name, wil_to_dev(wil));
if (rc) {
wil_err_fw(wil, "Failed to load brd %s\n", name);
return rc;
}
wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, brd->size);
/* Verify the header */
dlen = wil_fw_verify(wil, brd->data, brd->size);
if (dlen < 0) {
rc = dlen;
goto out;
}
/* Process the data record */
rc = wil_brd_process(wil, brd->data, dlen);
out:
release_firmware(brd);
return rc;
}
/**
* wil_fw_verify_file_exists - checks if firmware file exist
*

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -395,8 +396,9 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
wil6210_mask_irq_misc(wil, false);
if (isr & ISR_MISC_FW_ERROR) {
u32 fw_assert_code = wil_r(wil, RGF_FW_ASSERT_CODE);
u32 ucode_assert_code = wil_r(wil, RGF_UCODE_ASSERT_CODE);
u32 fw_assert_code = wil_r(wil, wil->rgf_fw_assert_code_addr);
u32 ucode_assert_code =
wil_r(wil, wil->rgf_ucode_assert_code_addr);
wil_err(wil,
"Firmware error detected, assert codes FW 0x%08x, UCODE 0x%08x\n",

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -637,6 +638,98 @@ void wil_priv_deinit(struct wil6210_priv *wil)
destroy_workqueue(wil->wmi_wq);
}
static void wil_shutdown_bl(struct wil6210_priv *wil)
{
u32 val;
wil_s(wil, RGF_USER_BL +
offsetof(struct bl_dedicated_registers_v1,
bl_shutdown_handshake), BL_SHUTDOWN_HS_GRTD);
usleep_range(100, 150);
val = wil_r(wil, RGF_USER_BL +
offsetof(struct bl_dedicated_registers_v1,
bl_shutdown_handshake));
if (val & BL_SHUTDOWN_HS_RTD) {
wil_dbg_misc(wil, "BL is ready for halt\n");
return;
}
wil_err(wil, "BL did not report ready for halt\n");
}
/* this format is used by ARC embedded CPU for instruction memory */
static inline u32 ARC_me_imm32(u32 d)
{
return ((d & 0xffff0000) >> 16) | ((d & 0x0000ffff) << 16);
}
/* defines access to interrupt vectors for wil_freeze_bl */
#define ARC_IRQ_VECTOR_OFFSET(N) ((N) * 8)
/* ARC long jump instruction */
#define ARC_JAL_INST (0x20200f80)
static void wil_freeze_bl(struct wil6210_priv *wil)
{
u32 jal, upc, saved;
u32 ivt3 = ARC_IRQ_VECTOR_OFFSET(3);
jal = wil_r(wil, wil->iccm_base + ivt3);
if (jal != ARC_me_imm32(ARC_JAL_INST)) {
wil_dbg_misc(wil, "invalid IVT entry found, skipping\n");
return;
}
/* prevent the target from entering deep sleep
* and disabling memory access
*/
saved = wil_r(wil, RGF_USER_USAGE_8);
wil_w(wil, RGF_USER_USAGE_8, saved | BIT_USER_PREVENT_DEEP_SLEEP);
usleep_range(20, 25); /* let the BL process the bit */
/* redirect to endless loop in the INT_L1 context and let it trap */
wil_w(wil, wil->iccm_base + ivt3 + 4, ARC_me_imm32(ivt3));
usleep_range(20, 25); /* let the BL get into the trap */
/* verify the BL is frozen */
upc = wil_r(wil, RGF_USER_CPU_PC);
if (upc < ivt3 || (upc > (ivt3 + 8)))
wil_dbg_misc(wil, "BL freeze failed, PC=0x%08X\n", upc);
wil_w(wil, RGF_USER_USAGE_8, saved);
}
static void wil_bl_prepare_halt(struct wil6210_priv *wil)
{
u32 tmp, ver;
/* before halting device CPU driver must make sure BL is not accessing
* host memory. This is done differently depending on BL version:
* 1. For very old BL versions the procedure is skipped
* (not supported).
* 2. For old BL version we use a special trick to freeze the BL
* 3. For new BL versions we shutdown the BL using handshake procedure.
*/
tmp = wil_r(wil, RGF_USER_BL +
offsetof(struct bl_dedicated_registers_v0,
boot_loader_struct_version));
if (!tmp) {
wil_dbg_misc(wil, "old BL, skipping halt preperation\n");
return;
}
tmp = wil_r(wil, RGF_USER_BL +
offsetof(struct bl_dedicated_registers_v1,
bl_shutdown_handshake));
ver = BL_SHUTDOWN_HS_PROT_VER(tmp);
if (ver > 0)
wil_shutdown_bl(wil);
else
wil_freeze_bl(wil);
}
static inline void wil_halt_cpu(struct wil6210_priv *wil)
{
wil_w(wil, RGF_USER_USER_CPU_0, BIT_USER_USER_CPU_MAN_RST);
@ -670,7 +763,7 @@ static void wil_set_oob_mode(struct wil6210_priv *wil, u8 mode)
}
}
static int wil_target_reset(struct wil6210_priv *wil)
static int wil_target_reset(struct wil6210_priv *wil, int no_flash)
{
int delay = 0;
u32 x, x1 = 0;
@ -684,9 +777,16 @@ static int wil_target_reset(struct wil6210_priv *wil)
wil_halt_cpu(wil);
/* clear all boot loader "ready" bits */
wil_w(wil, RGF_USER_BL +
offsetof(struct bl_dedicated_registers_v0, boot_loader_ready), 0);
if (!no_flash) {
/* clear all boot loader "ready" bits */
wil_w(wil, RGF_USER_BL +
offsetof(struct bl_dedicated_registers_v0,
boot_loader_ready), 0);
/* this should be safe to write even with old BLs */
wil_w(wil, RGF_USER_BL +
offsetof(struct bl_dedicated_registers_v1,
bl_shutdown_handshake), 0);
}
/* Clear Fw Download notification */
wil_c(wil, RGF_USER_USAGE_6, BIT(0));
@ -727,21 +827,33 @@ static int wil_target_reset(struct wil6210_priv *wil)
wil_w(wil, RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
/* wait until device ready. typical time is 20..80 msec */
do {
msleep(RST_DELAY);
x = wil_r(wil, RGF_USER_BL +
offsetof(struct bl_dedicated_registers_v0,
boot_loader_ready));
if (x1 != x) {
wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n", x1, x);
x1 = x;
}
if (delay++ > RST_COUNT) {
wil_err(wil, "Reset not completed, bl.ready 0x%08x\n",
x);
return -ETIME;
}
} while (x != BL_READY);
if (no_flash)
do {
msleep(RST_DELAY);
x = wil_r(wil, USER_EXT_USER_PMU_3);
if (delay++ > RST_COUNT) {
wil_err(wil, "Reset not completed, PMU_3 0x%08x\n",
x);
return -ETIME;
}
} while ((x & BIT_PMU_DEVICE_RDY) == 0);
else
do {
msleep(RST_DELAY);
x = wil_r(wil, RGF_USER_BL +
offsetof(struct bl_dedicated_registers_v0,
boot_loader_ready));
if (x1 != x) {
wil_dbg_misc(wil, "BL.ready 0x%08x => 0x%08x\n",
x1, x);
x1 = x;
}
if (delay++ > RST_COUNT) {
wil_err(wil, "Reset not completed, bl.ready 0x%08x\n",
x);
return -ETIME;
}
} while (x != BL_READY);
wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
@ -749,6 +861,21 @@ static int wil_target_reset(struct wil6210_priv *wil)
wil_s(wil, RGF_DMA_OFUL_NID_0, BIT_DMA_OFUL_NID_0_RX_EXT_TR_EN |
BIT_DMA_OFUL_NID_0_RX_EXT_A3_SRC);
if (no_flash) {
/* Reset OTP HW vectors to fit 40MHz */
wil_w(wil, RGF_USER_XPM_IFC_RD_TIME1, 0x60001);
wil_w(wil, RGF_USER_XPM_IFC_RD_TIME2, 0x20027);
wil_w(wil, RGF_USER_XPM_IFC_RD_TIME3, 0x1);
wil_w(wil, RGF_USER_XPM_IFC_RD_TIME4, 0x20027);
wil_w(wil, RGF_USER_XPM_IFC_RD_TIME5, 0x30003);
wil_w(wil, RGF_USER_XPM_IFC_RD_TIME6, 0x20002);
wil_w(wil, RGF_USER_XPM_IFC_RD_TIME7, 0x60001);
wil_w(wil, RGF_USER_XPM_IFC_RD_TIME8, 0x60001);
wil_w(wil, RGF_USER_XPM_IFC_RD_TIME9, 0x60001);
wil_w(wil, RGF_USER_XPM_IFC_RD_TIME10, 0x60001);
wil_w(wil, RGF_USER_XPM_RD_DOUT_SAMPLE_TIME, 0x57);
}
wil_dbg_misc(wil, "Reset completed in %d ms\n", delay * RST_DELAY);
return 0;
}
@ -906,6 +1033,27 @@ static void wil_bl_crash_info(struct wil6210_priv *wil, bool is_err)
}
}
static int wil_get_otp_info(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
struct wiphy *wiphy = wil_to_wiphy(wil);
u8 mac[8];
wil_memcpy_fromio_32(mac, wil->csr + HOSTADDR(RGF_OTP_MAC),
sizeof(mac));
if (!is_valid_ether_addr(mac)) {
wil_err(wil, "Invalid MAC %pM\n", mac);
return -EINVAL;
}
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);
return 0;
}
static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
{
ulong to = msecs_to_jiffies(1000);
@ -999,6 +1147,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
{
int rc;
unsigned long status_flags = BIT(wil_status_resetting);
int no_flash;
wil_dbg_misc(wil, "reset\n");
@ -1074,20 +1223,28 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
flush_workqueue(wil->wq_service);
flush_workqueue(wil->wmi_wq);
wil_bl_crash_info(wil, false);
no_flash = test_bit(hw_capa_no_flash, wil->hw_capa);
if (!no_flash)
wil_bl_crash_info(wil, false);
wil_disable_irq(wil);
rc = wil_target_reset(wil);
rc = wil_target_reset(wil, no_flash);
wil6210_clear_irq(wil);
wil_enable_irq(wil);
wil_rx_fini(wil);
if (rc) {
wil_bl_crash_info(wil, true);
if (!no_flash)
wil_bl_crash_info(wil, true);
goto out;
}
rc = wil_get_bl_info(wil);
if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */
rc = 0;
if (no_flash) {
rc = wil_get_otp_info(wil);
} else {
rc = wil_get_bl_info(wil);
if (rc == -EAGAIN && !load_fw)
/* ignore RF error if not going up */
rc = 0;
}
if (rc)
goto out;
@ -1096,13 +1253,21 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
wil_info(wil, "Use firmware <%s> + board <%s>\n",
wil->wil_fw_name, WIL_BOARD_FILE_NAME);
if (!no_flash)
wil_bl_prepare_halt(wil);
wil_halt_cpu(wil);
memset(wil->fw_version, 0, sizeof(wil->fw_version));
/* Loading f/w from the file */
rc = wil_request_firmware(wil, wil->wil_fw_name, true);
if (rc)
goto out;
rc = wil_request_firmware(wil, WIL_BOARD_FILE_NAME, true);
if (wil->brd_file_addr)
rc = wil_request_board(wil, WIL_BOARD_FILE_NAME);
else
rc = wil_request_firmware(wil,
WIL_BOARD_FILE_NAME,
true);
if (rc)
goto out;

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -35,15 +36,16 @@ static int wil6210_pm_notify(struct notifier_block *notify_block,
unsigned long mode, void *unused);
static
void wil_set_capabilities(struct wil6210_priv *wil)
int wil_set_capabilities(struct wil6210_priv *wil)
{
const char *wil_fw_name;
u32 jtag_id = wil_r(wil, RGF_USER_JTAG_DEV_ID);
u8 chip_revision = (wil_r(wil, RGF_USER_REVISION_ID) &
RGF_USER_REVISION_ID_MASK);
int platform_capa;
struct fw_map *iccm_section, *sct;
bitmap_zero(wil->hw_capabilities, hw_capability_last);
bitmap_zero(wil->hw_capa, hw_capa_last);
bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX);
bitmap_zero(wil->platform_capa, WIL_PLATFORM_CAPA_MAX);
wil->wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_DEFAULT :
@ -52,6 +54,8 @@ void wil_set_capabilities(struct wil6210_priv *wil)
switch (jtag_id) {
case JTAG_DEV_ID_SPARROW:
memcpy(fw_mapping, sparrow_fw_mapping,
sizeof(sparrow_fw_mapping));
switch (chip_revision) {
case REVISION_ID_SPARROW_D0:
wil->hw_name = "Sparrow D0";
@ -61,6 +65,12 @@ void wil_set_capabilities(struct wil6210_priv *wil)
if (wil_fw_verify_file_exists(wil, wil_fw_name))
wil->wil_fw_name = wil_fw_name;
sct = wil_find_fw_mapping("mac_rgf_ext");
if (!sct) {
wil_err(wil, "mac_rgf_ext section not found in fw_mapping\n");
return -EINVAL;
}
memcpy(sct, &sparrow_d0_mac_rgf_ext, sizeof(*sct));
break;
case REVISION_ID_SPARROW_B0:
wil->hw_name = "Sparrow B0";
@ -71,15 +81,36 @@ void wil_set_capabilities(struct wil6210_priv *wil)
wil->hw_version = HW_VER_UNKNOWN;
break;
}
wil->rgf_fw_assert_code_addr = SPARROW_RGF_FW_ASSERT_CODE;
wil->rgf_ucode_assert_code_addr = SPARROW_RGF_UCODE_ASSERT_CODE;
break;
case JTAG_DEV_ID_TALYN:
wil->hw_name = "Talyn";
wil->hw_version = HW_VER_TALYN;
memcpy(fw_mapping, talyn_fw_mapping, sizeof(talyn_fw_mapping));
wil->rgf_fw_assert_code_addr = TALYN_RGF_FW_ASSERT_CODE;
wil->rgf_ucode_assert_code_addr = TALYN_RGF_UCODE_ASSERT_CODE;
if (wil_r(wil, RGF_USER_OTP_HW_RD_MACHINE_1) &
BIT_NO_FLASH_INDICATION)
set_bit(hw_capa_no_flash, wil->hw_capa);
break;
default:
wil_err(wil, "Unknown board hardware, chip_id 0x%08x, chip_revision 0x%08x\n",
jtag_id, chip_revision);
wil->hw_name = "Unknown";
wil->hw_version = HW_VER_UNKNOWN;
return -EINVAL;
}
wil_info(wil, "Board hardware is %s\n", wil->hw_name);
iccm_section = wil_find_fw_mapping("fw_code");
if (!iccm_section) {
wil_err(wil, "fw_code section not found in fw_mapping\n");
return -EINVAL;
}
wil->iccm_base = iccm_section->host;
wil_info(wil, "Board hardware is %s, flash %sexist\n", wil->hw_name,
test_bit(hw_capa_no_flash, wil->hw_capa) ? "doesn't " : "");
/* Get platform capabilities */
if (wil->platform_ops.get_capa) {
@ -92,6 +123,8 @@ void wil_set_capabilities(struct wil6210_priv *wil)
/* extract FW capabilities from file without loading the FW */
wil_request_firmware(wil, wil->wil_fw_name, false);
wil_refresh_fw_capabilities(wil);
return 0;
}
void wil_disable_irq(struct wil6210_priv *wil)
@ -302,7 +335,11 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* rollback to err_iounmap */
wil_info(wil, "CSR at %pR -> 0x%p\n", &pdev->resource[0], wil->csr);
wil_set_capabilities(wil);
rc = wil_set_capabilities(wil);
if (rc) {
wil_err(wil, "wil_set_capabilities failed, rc %d\n", rc);
goto err_iounmap;
}
wil6210_clear_irq(wil);
/* FW should raise IRQ when ready */
@ -378,6 +415,7 @@ static void wil_pcie_remove(struct pci_dev *pdev)
static const struct pci_device_id wil6210_pcie_ids[] = {
{ PCI_DEVICE(0x1ae9, 0x0310) },
{ PCI_DEVICE(0x1ae9, 0x0302) }, /* same as above, firmware broken */
{ PCI_DEVICE(0x17cb, 0x1201) }, /* Talyn */
{ /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -169,6 +170,7 @@ struct RGF_ICR {
#define HW_MACHINE_BOOT_DONE (0x3fffffd)
#define RGF_USER_USER_CPU_0 (0x8801e0)
#define BIT_USER_USER_CPU_MAN_RST BIT(1) /* user_cpu_man_rst */
#define RGF_USER_CPU_PC (0x8801e8)
#define RGF_USER_MAC_CPU_0 (0x8801fc)
#define BIT_USER_MAC_CPU_MAN_RST BIT(1) /* mac_cpu_man_rst */
#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
@ -194,6 +196,19 @@ struct RGF_ICR {
#define RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1 (0x880c2c)
#define RGF_USER_SPARROW_M_4 (0x880c50) /* Sparrow */
#define BIT_SPARROW_M_4_SEL_SLEEP_OR_REF BIT(2)
#define RGF_USER_OTP_HW_RD_MACHINE_1 (0x880ce0)
#define BIT_NO_FLASH_INDICATION BIT(8)
#define RGF_USER_XPM_IFC_RD_TIME1 (0x880cec)
#define RGF_USER_XPM_IFC_RD_TIME2 (0x880cf0)
#define RGF_USER_XPM_IFC_RD_TIME3 (0x880cf4)
#define RGF_USER_XPM_IFC_RD_TIME4 (0x880cf8)
#define RGF_USER_XPM_IFC_RD_TIME5 (0x880cfc)
#define RGF_USER_XPM_IFC_RD_TIME6 (0x880d00)
#define RGF_USER_XPM_IFC_RD_TIME7 (0x880d04)
#define RGF_USER_XPM_IFC_RD_TIME8 (0x880d08)
#define RGF_USER_XPM_IFC_RD_TIME9 (0x880d0c)
#define RGF_USER_XPM_IFC_RD_TIME10 (0x880d10)
#define RGF_USER_XPM_RD_DOUT_SAMPLE_TIME (0x880d64)
#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */
#define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0)
@ -284,22 +299,33 @@ struct RGF_ICR {
#define RGF_CAF_PLL_LOCK_STATUS (0x88afec)
#define BIT_CAF_OSC_DIG_XTAL_STABLE BIT(0)
#define USER_EXT_USER_PMU_3 (0x88d00c)
#define BIT_PMU_DEVICE_RDY BIT(0)
#define RGF_USER_JTAG_DEV_ID (0x880b34) /* device ID */
#define JTAG_DEV_ID_SPARROW (0x2632072f)
#define JTAG_DEV_ID_TALYN (0x7e0e1)
#define RGF_USER_REVISION_ID (0x88afe4)
#define RGF_USER_REVISION_ID_MASK (3)
#define REVISION_ID_SPARROW_B0 (0x0)
#define REVISION_ID_SPARROW_D0 (0x3)
#define RGF_OTP_MAC (0x8a0620)
/* crash codes for FW/Ucode stored here */
#define RGF_FW_ASSERT_CODE (0x91f020)
#define RGF_UCODE_ASSERT_CODE (0x91f028)
/* ASSERT RGFs */
#define SPARROW_RGF_FW_ASSERT_CODE (0x91f020)
#define SPARROW_RGF_UCODE_ASSERT_CODE (0x91f028)
#define TALYN_RGF_FW_ASSERT_CODE (0xa37020)
#define TALYN_RGF_UCODE_ASSERT_CODE (0xa37028)
enum {
HW_VER_UNKNOWN,
HW_VER_SPARROW_B0, /* REVISION_ID_SPARROW_B0 */
HW_VER_SPARROW_D0, /* REVISION_ID_SPARROW_D0 */
HW_VER_TALYN, /* JTAG_DEV_ID_TALYN */
};
/* popular locations */
@ -315,6 +341,10 @@ enum {
#define WIL_DATA_COMPLETION_TO_MS 200
/* Hardware definitions end */
#define SPARROW_FW_MAPPING_TABLE_SIZE 10
#define TALYN_FW_MAPPING_TABLE_SIZE 13
#define MAX_FW_MAPPING_TABLE_SIZE 13
struct fw_map {
u32 from; /* linker address - from, inclusive */
u32 to; /* linker address - to, exclusive */
@ -324,7 +354,10 @@ struct fw_map {
};
/* array size should be in sync with actual definition in the wmi.c */
extern const struct fw_map fw_mapping[10];
extern const struct fw_map sparrow_fw_mapping[SPARROW_FW_MAPPING_TABLE_SIZE];
extern const struct fw_map sparrow_d0_mac_rgf_ext;
extern const struct fw_map talyn_fw_mapping[TALYN_FW_MAPPING_TABLE_SIZE];
extern struct fw_map fw_mapping[MAX_FW_MAPPING_TABLE_SIZE];
/**
* mk_cidxtid - construct @cidxtid field
@ -570,7 +603,8 @@ enum {
};
enum {
hw_capability_last
hw_capa_no_flash,
hw_capa_last
};
struct wil_probe_client_req {
@ -646,7 +680,10 @@ struct wil6210_priv {
u8 chip_revision;
const char *hw_name;
const char *wil_fw_name;
DECLARE_BITMAP(hw_capabilities, hw_capability_last);
char *board_file;
u32 brd_file_addr;
u32 brd_file_max_size;
DECLARE_BITMAP(hw_capa, hw_capa_last);
DECLARE_BITMAP(fw_capabilities, WMI_FW_CAPABILITY_MAX);
DECLARE_BITMAP(platform_capa, WIL_PLATFORM_CAPA_MAX);
u8 n_mids; /* number of additional MIDs as reported by FW */
@ -720,7 +757,7 @@ struct wil6210_priv {
atomic_t isr_count_rx, isr_count_tx;
/* debugfs */
struct dentry *debug;
struct wil_blob_wrapper blobs[ARRAY_SIZE(fw_mapping)];
struct wil_blob_wrapper blobs[MAX_FW_MAPPING_TABLE_SIZE];
u8 discovery_mode;
u8 abft_len;
u8 wakeup_trigger;
@ -755,6 +792,10 @@ struct wil6210_priv {
bool suspend_resp_comp;
u32 bus_request_kbps;
u32 bus_request_kbps_pre_suspend;
u32 rgf_fw_assert_code_addr;
u32 rgf_ucode_assert_code_addr;
u32 iccm_base;
};
#define wil_to_wiphy(i) (i->wdev->wiphy)
@ -880,6 +921,7 @@ void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
void wil_set_ethtoolops(struct net_device *ndev);
struct fw_map *wil_find_fw_mapping(const char *section);
void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr, u32 size);
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
@ -1013,6 +1055,7 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type);
int wil_request_firmware(struct wil6210_priv *wil, const char *name,
bool load);
int wil_request_board(struct wil6210_priv *wil, const char *name);
bool wil_fw_verify_file_exists(struct wil6210_priv *wil, const char *name);
void wil_pm_runtime_allow(struct wil6210_priv *wil);

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -70,23 +71,23 @@ MODULE_PARM_DESC(led_id,
* On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing
* AHB addresses starting from 0x880000
*
* Internally, firmware uses addresses that allows faster access but
* Internally, firmware uses addresses that allow faster access but
* are invisible from the host. To read from these addresses, alternative
* AHB address must be used.
*
* Memory mapping
* Linker address PCI/Host address
* 0x880000 .. 0xa80000 2Mb BAR0
* 0x800000 .. 0x807000 0x900000 .. 0x907000 28k DCCM
* 0x840000 .. 0x857000 0x908000 .. 0x91f000 92k PERIPH
*/
/**
* @fw_mapping provides memory remapping table
* @sparrow_fw_mapping provides memory remapping table for sparrow
*
* array size should be in sync with the declaration in the wil6210.h
*
* Sparrow memory mapping:
* Linker address PCI/Host address
* 0x880000 .. 0xa80000 2Mb BAR0
* 0x800000 .. 0x808000 0x900000 .. 0x908000 32k DCCM
* 0x840000 .. 0x860000 0x908000 .. 0x928000 128k PERIPH
*/
const struct fw_map fw_mapping[] = {
const struct fw_map sparrow_fw_mapping[] = {
/* FW code RAM 256k */
{0x000000, 0x040000, 0x8c0000, "fw_code", true},
/* FW data RAM 32k */
@ -112,6 +113,59 @@ const struct fw_map fw_mapping[] = {
{0x800000, 0x804000, 0x940000, "uc_data", false},
};
/**
* @sparrow_d0_mac_rgf_ext - mac_rgf_ext section for Sparrow D0
* it is a bit larger to support extra features
*/
const struct fw_map sparrow_d0_mac_rgf_ext = {
0x88c000, 0x88c500, 0x88c000, "mac_rgf_ext", true
};
/**
* @talyn_fw_mapping provides memory remapping table for Talyn
*
* array size should be in sync with the declaration in the wil6210.h
*
* Talyn memory mapping:
* Linker address PCI/Host address
* 0x880000 .. 0xc80000 4Mb BAR0
* 0x800000 .. 0x820000 0xa00000 .. 0xa20000 128k DCCM
* 0x840000 .. 0x858000 0xa20000 .. 0xa38000 96k PERIPH
*/
const struct fw_map talyn_fw_mapping[] = {
/* FW code RAM 1M */
{0x000000, 0x100000, 0x900000, "fw_code", true},
/* FW data RAM 128k */
{0x800000, 0x820000, 0xa00000, "fw_data", true},
/* periph. data RAM 96k */
{0x840000, 0x858000, 0xa20000, "fw_peri", true},
/* various RGF 40k */
{0x880000, 0x88a000, 0x880000, "rgf", true},
/* AGC table 4k */
{0x88a000, 0x88b000, 0x88a000, "AGC_tbl", true},
/* Pcie_ext_rgf 4k */
{0x88b000, 0x88c000, 0x88b000, "rgf_ext", true},
/* mac_ext_rgf 1344b */
{0x88c000, 0x88c540, 0x88c000, "mac_rgf_ext", true},
/* ext USER RGF 4k */
{0x88d000, 0x88e000, 0x88d000, "ext_user_rgf", true},
/* OTP 4k */
{0x8a0000, 0x8a1000, 0x8a0000, "otp", true},
/* DMA EXT RGF 64k */
{0x8b0000, 0x8c0000, 0x8b0000, "dma_ext_rgf", true},
/* upper area 1536k */
{0x900000, 0xa80000, 0x900000, "upper", true},
/* UCODE areas - accessible by debugfs blobs but not by
* wmi_addr_remap. UCODE areas MUST be added AFTER FW areas!
*/
/* ucode code RAM 256k */
{0x000000, 0x040000, 0xa38000, "uc_code", false},
/* ucode data RAM 32k */
{0x800000, 0x808000, 0xa78000, "uc_data", false},
};
struct fw_map fw_mapping[MAX_FW_MAPPING_TABLE_SIZE];
struct blink_on_off_time led_blink_time[] = {
{WIL_LED_BLINK_ON_SLOW_MS, WIL_LED_BLINK_OFF_SLOW_MS},
{WIL_LED_BLINK_ON_MED_MS, WIL_LED_BLINK_OFF_MED_MS},
@ -138,6 +192,24 @@ static u32 wmi_addr_remap(u32 x)
return 0;
}
/**
* find fw_mapping entry by section name
* @section - section name
*
* Return pointer to section or NULL if not found
*/
struct fw_map *wil_find_fw_mapping(const char *section)
{
int i;
for (i = 0; i < ARRAY_SIZE(fw_mapping); i++)
if (fw_mapping[i].name &&
!strcmp(section, fw_mapping[i].name))
return &fw_mapping[i];
return NULL;
}
/**
* Check address validity for WMI buffer; remap if needed
* @ptr - internal (linker) fw/ucode address

View File

@ -165,7 +165,7 @@ static int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
static int
brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
void *buf, uint len)
void *buf, uint len, int *fwerr)
{
struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
@ -175,6 +175,7 @@ brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
*fwerr = 0;
ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false);
if (ret < 0) {
brcmf_err("brcmf_proto_bcdc_msg failed w/status %d\n",
@ -211,25 +212,27 @@ retry:
memcpy(buf, info, len);
}
ret = 0;
/* Check the ERROR flag */
if (flags & BCDC_DCMD_ERROR)
ret = le32_to_cpu(msg->status);
*fwerr = le32_to_cpu(msg->status);
done:
return ret;
}
static int
brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
void *buf, uint len)
void *buf, uint len, int *fwerr)
{
struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
int ret = 0;
int ret;
u32 flags, id;
brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
*fwerr = 0;
ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true);
if (ret < 0)
goto done;
@ -249,9 +252,11 @@ brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
goto done;
}
ret = 0;
/* Check the ERROR flag */
if (flags & BCDC_DCMD_ERROR)
ret = le32_to_cpu(msg->status);
*fwerr = le32_to_cpu(msg->status);
done:
return ret;

View File

@ -107,7 +107,7 @@ static s32
brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
{
struct brcmf_pub *drvr = ifp->drvr;
s32 err;
s32 err, fwerr;
if (drvr->bus_if->state != BRCMF_BUS_UP) {
brcmf_err("bus is down. we have nothing to do.\n");
@ -117,16 +117,20 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
if (data != NULL)
len = min_t(uint, len, BRCMF_DCMD_MAXLEN);
if (set)
err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd, data, len);
err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd,
data, len, &fwerr);
else
err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, data, len);
if (err >= 0)
return 0;
brcmf_dbg(FIL, "Failed: %s (%d)\n",
brcmf_fil_get_errstr((u32)(-err)), err);
err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd,
data, len, &fwerr);
if (err) {
brcmf_dbg(FIL, "Failed: %s (%d)\n",
brcmf_fil_get_errstr((u32)(-err)), err);
} else if (fwerr < 0) {
brcmf_dbg(FIL, "Firmware error: %s (%d)\n",
brcmf_fil_get_errstr((u32)(-fwerr)), fwerr);
err = -EBADE;
}
return err;
}

View File

@ -477,7 +477,7 @@ static void brcmf_msgbuf_ioctl_resp_wake(struct brcmf_msgbuf *msgbuf)
static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx,
uint cmd, void *buf, uint len)
uint cmd, void *buf, uint len, int *fwerr)
{
struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
struct sk_buff *skb = NULL;
@ -485,6 +485,7 @@ static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx,
int err;
brcmf_dbg(MSGBUF, "ifidx=%d, cmd=%d, len=%d\n", ifidx, cmd, len);
*fwerr = 0;
msgbuf->ctl_completed = false;
err = brcmf_msgbuf_tx_ioctl(drvr, ifidx, cmd, buf, len);
if (err)
@ -508,14 +509,15 @@ static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx,
}
brcmu_pkt_buf_free_skb(skb);
return msgbuf->ioctl_resp_status;
*fwerr = msgbuf->ioctl_resp_status;
return 0;
}
static int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx,
uint cmd, void *buf, uint len)
uint cmd, void *buf, uint len, int *fwerr)
{
return brcmf_msgbuf_query_dcmd(drvr, ifidx, cmd, buf, len);
return brcmf_msgbuf_query_dcmd(drvr, ifidx, cmd, buf, len, fwerr);
}

View File

@ -30,9 +30,9 @@ struct brcmf_proto {
int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws,
struct sk_buff *skb, struct brcmf_if **ifp);
int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd,
void *buf, uint len);
void *buf, uint len, int *fwerr);
int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
uint len);
uint len, int *fwerr);
int (*tx_queue_data)(struct brcmf_pub *drvr, int ifidx,
struct sk_buff *skb);
int (*txdata)(struct brcmf_pub *drvr, int ifidx, u8 offset,
@ -71,14 +71,16 @@ static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws,
return drvr->proto->hdrpull(drvr, do_fws, skb, ifp);
}
static inline int brcmf_proto_query_dcmd(struct brcmf_pub *drvr, int ifidx,
uint cmd, void *buf, uint len)
uint cmd, void *buf, uint len,
int *fwerr)
{
return drvr->proto->query_dcmd(drvr, ifidx, cmd, buf, len);
return drvr->proto->query_dcmd(drvr, ifidx, cmd, buf, len,fwerr);
}
static inline int brcmf_proto_set_dcmd(struct brcmf_pub *drvr, int ifidx,
uint cmd, void *buf, uint len)
uint cmd, void *buf, uint len,
int *fwerr)
{
return drvr->proto->set_dcmd(drvr, ifidx, cmd, buf, len);
return drvr->proto->set_dcmd(drvr, ifidx, cmd, buf, len, fwerr);
}
static inline int brcmf_proto_tx_queue_data(struct brcmf_pub *drvr, int ifidx,

View File

@ -2,7 +2,7 @@ obj-$(CONFIG_MT76_CORE) += mt76.o
obj-$(CONFIG_MT76x2E) += mt76x2e.o
mt76-y := \
mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o tx.o
mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o tx.o agg-rx.o
CFLAGS_trace.o := -I$(src)

View File

@ -0,0 +1,258 @@
/*
* Copyright (C) 2018 Felix Fietkau <nbd@nbd.name>
*
* 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 "mt76.h"
#define REORDER_TIMEOUT (HZ / 10)
static void
mt76_aggr_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames, int idx)
{
struct sk_buff *skb;
tid->head = ieee80211_sn_inc(tid->head);
skb = tid->reorder_buf[idx];
if (!skb)
return;
tid->reorder_buf[idx] = NULL;
tid->nframes--;
__skb_queue_tail(frames, skb);
}
static void
mt76_rx_aggr_release_frames(struct mt76_rx_tid *tid, struct sk_buff_head *frames,
u16 head)
{
int idx;
while (ieee80211_sn_less(tid->head, head)) {
idx = tid->head % tid->size;
mt76_aggr_release(tid, frames, idx);
}
}
static void
mt76_rx_aggr_release_head(struct mt76_rx_tid *tid, struct sk_buff_head *frames)
{
int idx = tid->head % tid->size;
while (tid->reorder_buf[idx]) {
mt76_aggr_release(tid, frames, idx);
idx = tid->head % tid->size;
}
}
static void
mt76_rx_aggr_check_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames)
{
struct mt76_rx_status *status;
struct sk_buff *skb;
int start, idx, nframes;
if (!tid->nframes)
return;
mt76_rx_aggr_release_head(tid, frames);
start = tid->head % tid->size;
nframes = tid->nframes;
for (idx = (tid->head + 1) % tid->size;
idx != start && nframes;
idx = (idx + 1) % tid->size) {
skb = tid->reorder_buf[idx];
if (!skb)
continue;
nframes--;
status = (struct mt76_rx_status *) skb->cb;
if (!time_after(jiffies, status->reorder_time +
REORDER_TIMEOUT))
continue;
mt76_rx_aggr_release_frames(tid, frames, status->seqno);
}
mt76_rx_aggr_release_head(tid, frames);
}
static void
mt76_rx_aggr_reorder_work(struct work_struct *work)
{
struct mt76_rx_tid *tid = container_of(work, struct mt76_rx_tid,
reorder_work.work);
struct mt76_dev *dev = tid->dev;
struct sk_buff_head frames;
__skb_queue_head_init(&frames);
local_bh_disable();
spin_lock(&tid->lock);
mt76_rx_aggr_check_release(tid, &frames);
spin_unlock(&tid->lock);
ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work, REORDER_TIMEOUT);
mt76_rx_complete(dev, &frames, -1);
local_bh_enable();
}
void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames)
{
struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
struct mt76_wcid *wcid = status->wcid;
struct ieee80211_sta *sta;
struct mt76_rx_tid *tid;
bool sn_less;
u16 seqno, head, size;
u8 idx;
__skb_queue_tail(frames, skb);
sta = wcid_to_sta(wcid);
if (!sta || !status->aggr)
return;
tid = rcu_dereference(wcid->aggr[status->tid]);
if (!tid)
return;
spin_lock_bh(&tid->lock);
if (tid->stopped)
goto out;
head = tid->head;
seqno = status->seqno;
size = tid->size;
sn_less = ieee80211_sn_less(seqno, head);
if (!tid->started) {
if (sn_less)
goto out;
tid->started = true;
}
if (sn_less) {
__skb_unlink(skb, frames);
dev_kfree_skb(skb);
goto out;
}
if (seqno == head) {
tid->head = ieee80211_sn_inc(head);
if (tid->nframes)
mt76_rx_aggr_release_head(tid, frames);
goto out;
}
__skb_unlink(skb, frames);
/*
* Frame sequence number exceeds buffering window, free up some space
* by releasing previous frames
*/
if (!ieee80211_sn_less(seqno, head + size)) {
head = ieee80211_sn_inc(ieee80211_sn_sub(seqno, size));
mt76_rx_aggr_release_frames(tid, frames, head);
}
idx = seqno % size;
/* Discard if the current slot is already in use */
if (tid->reorder_buf[idx]) {
dev_kfree_skb(skb);
goto out;
}
status->reorder_time = jiffies;
tid->reorder_buf[idx] = skb;
tid->nframes++;
mt76_rx_aggr_release_head(tid, frames);
ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work, REORDER_TIMEOUT);
out:
spin_unlock_bh(&tid->lock);
}
int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno,
u16 ssn, u8 size)
{
struct mt76_rx_tid *tid;
mt76_rx_aggr_stop(dev, wcid, tidno);
tid = kzalloc(sizeof(*tid) + size * sizeof(tid->reorder_buf[0]),
GFP_KERNEL);
if (!tid)
return -ENOMEM;
tid->dev = dev;
tid->head = ssn;
tid->size = size;
INIT_DELAYED_WORK(&tid->reorder_work, mt76_rx_aggr_reorder_work);
spin_lock_init(&tid->lock);
rcu_assign_pointer(wcid->aggr[tidno], tid);
return 0;
}
EXPORT_SYMBOL_GPL(mt76_rx_aggr_start);
static void mt76_rx_aggr_shutdown(struct mt76_dev *dev, struct mt76_rx_tid *tid)
{
u8 size = tid->size;
int i;
spin_lock_bh(&tid->lock);
tid->stopped = true;
for (i = 0; tid->nframes && i < size; i++) {
struct sk_buff *skb = tid->reorder_buf[i];
if (!skb)
continue;
tid->nframes--;
dev_kfree_skb(skb);
}
spin_unlock_bh(&tid->lock);
cancel_delayed_work_sync(&tid->reorder_work);
}
void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno)
{
struct mt76_rx_tid *tid;
rcu_read_lock();
tid = rcu_dereference(wcid->aggr[tidno]);
if (tid) {
rcu_assign_pointer(wcid->aggr[tidno], NULL);
mt76_rx_aggr_shutdown(dev, tid);
kfree_rcu(tid, rcu_head);
}
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(mt76_rx_aggr_stop);

View File

@ -387,17 +387,25 @@ static int
mt76_dma_rx_poll(struct napi_struct *napi, int budget)
{
struct mt76_dev *dev;
int qid, done;
int qid, done = 0, cur;
dev = container_of(napi->dev, struct mt76_dev, napi_dev);
qid = napi - dev->napi;
done = mt76_dma_rx_process(dev, &dev->q_rx[qid], budget);
rcu_read_lock();
do {
cur = mt76_dma_rx_process(dev, &dev->q_rx[qid], budget - done);
mt76_rx_poll_complete(dev, qid);
done += cur;
} while (cur && done < budget);
rcu_read_unlock();
if (done < budget) {
napi_complete(napi);
dev->drv->rx_poll_complete(dev, qid);
}
mt76_rx_complete(dev, qid);
return done;
}

View File

@ -384,10 +384,122 @@ int mt76_get_survey(struct ieee80211_hw *hw, int idx,
}
EXPORT_SYMBOL_GPL(mt76_get_survey);
void mt76_rx_complete(struct mt76_dev *dev, enum mt76_rxq_id q)
void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
struct ieee80211_key_conf *key)
{
struct ieee80211_key_seq seq;
int i;
wcid->rx_check_pn = false;
if (!key)
return;
if (key->cipher == WLAN_CIPHER_SUITE_CCMP)
wcid->rx_check_pn = true;
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
ieee80211_get_key_rx_seq(key, i, &seq);
memcpy(wcid->rx_key_pn[i], seq.ccmp.pn, sizeof(seq.ccmp.pn));
}
}
EXPORT_SYMBOL(mt76_wcid_key_setup);
static struct ieee80211_sta *mt76_rx_convert(struct sk_buff *skb)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct mt76_rx_status mstat;
mstat = *((struct mt76_rx_status *) skb->cb);
memset(status, 0, sizeof(*status));
status->flag = mstat.flag;
status->freq = mstat.freq;
status->enc_flags = mstat.enc_flags;
status->encoding = mstat.encoding;
status->bw = mstat.bw;
status->rate_idx = mstat.rate_idx;
status->nss = mstat.nss;
status->band = mstat.band;
status->signal = mstat.signal;
status->chains = mstat.chains;
BUILD_BUG_ON(sizeof(mstat) > sizeof(skb->cb));
BUILD_BUG_ON(sizeof(status->chain_signal) != sizeof(mstat.chain_signal));
memcpy(status->chain_signal, mstat.chain_signal, sizeof(mstat.chain_signal));
return wcid_to_sta(mstat.wcid);
}
static int
mt76_check_ccmp_pn(struct sk_buff *skb)
{
struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
struct mt76_wcid *wcid = status->wcid;
struct ieee80211_hdr *hdr;
int ret;
if (!(status->flag & RX_FLAG_DECRYPTED))
return 0;
if (!wcid || !wcid->rx_check_pn)
return 0;
if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
/*
* Validate the first fragment both here and in mac80211
* All further fragments will be validated by mac80211 only.
*/
hdr = (struct ieee80211_hdr *) skb->data;
if (ieee80211_is_frag(hdr) &&
!ieee80211_is_first_frag(hdr->frame_control))
return 0;
}
BUILD_BUG_ON(sizeof(status->iv) != sizeof(wcid->rx_key_pn[0]));
ret = memcmp(status->iv, wcid->rx_key_pn[status->tid],
sizeof(status->iv));
if (ret <= 0)
return -EINVAL; /* replay */
memcpy(wcid->rx_key_pn[status->tid], status->iv, sizeof(status->iv));
if (status->flag & RX_FLAG_IV_STRIPPED)
status->flag |= RX_FLAG_PN_VALIDATED;
return 0;
}
void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
int queue)
{
struct napi_struct *napi = NULL;
struct ieee80211_sta *sta;
struct sk_buff *skb;
while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL)
ieee80211_rx_napi(dev->hw, NULL, skb, &dev->napi[q]);
if (queue >= 0)
napi = &dev->napi[queue];
while ((skb = __skb_dequeue(frames)) != NULL) {
if (mt76_check_ccmp_pn(skb)) {
dev_kfree_skb(skb);
continue;
}
sta = mt76_rx_convert(skb);
ieee80211_rx_napi(dev->hw, sta, skb, napi);
}
}
void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q)
{
struct sk_buff_head frames;
struct sk_buff *skb;
__skb_queue_head_init(&frames);
while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL)
mt76_rx_aggr_reorder(skb, &frames);
mt76_rx_complete(dev, &frames, q);
}

View File

@ -122,13 +122,23 @@ struct mt76_queue_ops {
};
struct mt76_wcid {
struct mt76_rx_tid __rcu *aggr[IEEE80211_NUM_TIDS];
struct work_struct aggr_work;
u8 idx;
u8 hw_key_idx;
u8 sta:1;
u8 rx_check_pn;
u8 rx_key_pn[IEEE80211_NUM_TIDS][6];
__le16 tx_rate;
bool tx_rate_set;
u8 tx_rate_nss;
s8 max_txpwr_adj;
bool sw_iv;
};
struct mt76_txq {
@ -149,6 +159,24 @@ struct mt76_txwi_cache {
struct list_head list;
};
struct mt76_rx_tid {
struct rcu_head rcu_head;
struct mt76_dev *dev;
spinlock_t lock;
struct delayed_work reorder_work;
u16 head;
u8 size;
u8 nframes;
u8 started:1, stopped:1, timer_pending:1;
struct sk_buff *reorder_buf[];
};
enum {
MT76_STATE_INITIALIZED,
MT76_STATE_RUNNING,
@ -249,6 +277,29 @@ struct mt76_rate_power {
};
};
struct mt76_rx_status {
struct mt76_wcid *wcid;
unsigned long reorder_time;
u8 iv[6];
u8 aggr:1;
u8 tid;
u16 seqno;
u16 freq;
u32 flag;
u8 enc_flags;
u8 encoding:2, bw:3;
u8 rate_idx;
u8 nss;
u8 band;
u8 signal;
u8 chains;
s8 chain_signal[IEEE80211_MAX_CHAINS];
};
#define mt76_rr(dev, ...) (dev)->mt76.bus->rr(&((dev)->mt76), __VA_ARGS__)
#define mt76_wr(dev, ...) (dev)->mt76.bus->wr(&((dev)->mt76), __VA_ARGS__)
#define mt76_rmw(dev, ...) (dev)->mt76.bus->rmw(&((dev)->mt76), __VA_ARGS__)
@ -329,6 +380,17 @@ mtxq_to_txq(struct mt76_txq *mtxq)
return container_of(ptr, struct ieee80211_txq, drv_priv);
}
static inline struct ieee80211_sta *
wcid_to_sta(struct mt76_wcid *wcid)
{
void *ptr = wcid;
if (!wcid || !wcid->sta)
return NULL;
return container_of(ptr, struct ieee80211_sta, drv_priv);
}
int mt76_tx_queue_skb(struct mt76_dev *dev, struct mt76_queue *q,
struct sk_buff *skb, struct mt76_wcid *wcid,
struct ieee80211_sta *sta);
@ -352,9 +414,19 @@ void mt76_set_channel(struct mt76_dev *dev);
int mt76_get_survey(struct ieee80211_hw *hw, int idx,
struct survey_info *survey);
int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid,
u16 ssn, u8 size);
void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid);
void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
struct ieee80211_key_conf *key);
/* internal */
void mt76_tx_free(struct mt76_dev *dev);
void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t);
void mt76_rx_complete(struct mt76_dev *dev, enum mt76_rxq_id q);
void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
int queue);
void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q);
void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames);
#endif

View File

@ -144,6 +144,7 @@ struct mt76x2_vif {
struct mt76x2_sta {
struct mt76_wcid wcid; /* must be first */
struct mt76x2_vif *vif;
struct mt76x2_tx_status status;
int n_frames;
};

View File

@ -460,8 +460,8 @@ void mt76x2_dfs_init_params(struct mt76x2_dev *dev)
{
struct cfg80211_chan_def *chandef = &dev->mt76.chandef;
tasklet_kill(&dev->dfs_pd.dfs_tasklet);
if (chandef->chan->flags & IEEE80211_CHAN_RADAR) {
if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) &&
dev->dfs_pd.region != NL80211_DFS_UNSET) {
mt76x2_dfs_set_bbp_params(dev);
/* enable debug mode */
mt76x2_dfs_set_capture_mode_ctrl(dev, true);
@ -491,3 +491,16 @@ void mt76x2_dfs_init_detector(struct mt76x2_dev *dev)
(unsigned long)dev);
}
void mt76x2_dfs_set_domain(struct mt76x2_dev *dev,
enum nl80211_dfs_regions region)
{
struct mt76x2_dfs_pattern_detector *dfs_pd = &dev->dfs_pd;
if (dfs_pd->region != region) {
tasklet_disable(&dfs_pd->dfs_tasklet);
dfs_pd->region = region;
mt76x2_dfs_init_params(dev);
tasklet_enable(&dfs_pd->dfs_tasklet);
}
}

View File

@ -76,5 +76,7 @@ struct mt76x2_dfs_pattern_detector {
void mt76x2_dfs_init_params(struct mt76x2_dev *dev);
void mt76x2_dfs_init_detector(struct mt76x2_dev *dev);
void mt76x2_dfs_adjust_agc(struct mt76x2_dev *dev);
void mt76x2_dfs_set_domain(struct mt76x2_dev *dev,
enum nl80211_dfs_regions region);
#endif /* __MT76x2_DFS_H */

View File

@ -55,6 +55,7 @@ mt76x2_init_tx_queue(struct mt76x2_dev *dev, struct mt76_queue *q,
q->regs = dev->mt76.regs + MT_TX_RING_BASE + idx * MT_RING_SIZE;
q->ndesc = n_desc;
q->hw_idx = idx;
ret = mt76_queue_alloc(dev, q);
if (ret)

View File

@ -131,7 +131,7 @@ mt76_write_mac_initvals(struct mt76x2_dev *dev)
{ MT_RX_FILTR_CFG, 0x00015f97 },
{ MT_LEGACY_BASIC_RATE, 0x0000017f },
{ MT_HT_BASIC_RATE, 0x00004003 },
{ MT_PN_PAD_MODE, 0x00000002 },
{ MT_PN_PAD_MODE, 0x00000003 },
{ MT_TXOP_HLDR_ET, 0x00000002 },
{ 0xa44, 0x00000000 },
{ MT_HEADER_TRANS_CTRL_REG, 0x00000000 },
@ -614,6 +614,8 @@ void mt76x2_stop_hardware(struct mt76x2_dev *dev)
void mt76x2_cleanup(struct mt76x2_dev *dev)
{
tasklet_disable(&dev->dfs_pd.dfs_tasklet);
tasklet_disable(&dev->pre_tbtt_tasklet);
mt76x2_stop_hardware(dev);
mt76x2_dma_cleanup(dev);
mt76x2_mcu_cleanup(dev);
@ -652,7 +654,7 @@ static void mt76x2_regd_notifier(struct wiphy *wiphy,
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct mt76x2_dev *dev = hw->priv;
dev->dfs_pd.region = request->dfs_region;
mt76x2_dfs_set_domain(dev, request->dfs_region);
}
#define CCK_RATE(_idx, _rate) { \
@ -840,6 +842,8 @@ int mt76x2_register_device(struct mt76x2_dev *dev)
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
ieee80211_hw_set(hw, SUPPORTS_HT_CCK_RATES);
ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
INIT_DELAYED_WORK(&dev->cal_work, mt76x2_phy_calibrate);
INIT_DELAYED_WORK(&dev->mac_work, mt76x2_mac_work);

View File

@ -29,7 +29,7 @@ void mt76x2_mac_set_bssid(struct mt76x2_dev *dev, u8 idx, const u8 *addr)
}
static int
mt76x2_mac_process_rate(struct ieee80211_rx_status *status, u16 rate)
mt76x2_mac_process_rate(struct mt76_rx_status *status, u16 rate)
{
u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate);
@ -171,10 +171,13 @@ void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi,
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *rate = &info->control.rates[0];
struct ieee80211_key_conf *key = info->control.hw_key;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
u16 rate_ht_mask = FIELD_PREP(MT_RXWI_RATE_PHY, BIT(1) | BIT(2));
u16 txwi_flags = 0;
u8 nss;
s8 txpwr_adj, max_txpwr_adj;
u8 ccmp_pn[8];
memset(txwi, 0, sizeof(*txwi));
@ -185,6 +188,20 @@ void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi,
txwi->pktid = 1;
if (wcid && wcid->sw_iv && key) {
u64 pn = atomic64_inc_return(&key->tx_pn);
ccmp_pn[0] = pn;
ccmp_pn[1] = pn >> 8;
ccmp_pn[2] = 0;
ccmp_pn[3] = 0x20 | (key->keyidx << 6);
ccmp_pn[4] = pn >> 16;
ccmp_pn[5] = pn >> 24;
ccmp_pn[6] = pn >> 32;
ccmp_pn[7] = pn >> 40;
txwi->iv = *((u32 *) &ccmp_pn[0]);
txwi->eiv = *((u32 *) &ccmp_pn[1]);
}
spin_lock_bh(&dev->mt76.lock);
if (wcid && (rate->idx < 0 || !rate->count)) {
txwi->rate = wcid->tx_rate;
@ -232,36 +249,101 @@ void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi,
sta->ht_cap.ampdu_density);
}
if (ieee80211_is_probe_resp(hdr->frame_control) ||
ieee80211_is_beacon(hdr->frame_control))
txwi_flags |= MT_TXWI_FLAGS_TS;
txwi->flags |= cpu_to_le16(txwi_flags);
txwi->len_ctl = cpu_to_le16(skb->len);
}
static void mt76x2_remove_hdr_pad(struct sk_buff *skb)
static void mt76x2_remove_hdr_pad(struct sk_buff *skb, int len)
{
int len = ieee80211_get_hdrlen_from_skb(skb);
int hdrlen;
memmove(skb->data + 2, skb->data, len);
skb_pull(skb, 2);
if (!len)
return;
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
memmove(skb->data + len, skb->data, hdrlen);
skb_pull(skb, len);
}
static struct mt76_wcid *
mt76x2_rx_get_sta_wcid(struct mt76x2_dev *dev, u8 idx, bool unicast)
{
struct mt76x2_sta *sta;
struct mt76_wcid *wcid;
if (idx >= ARRAY_SIZE(dev->wcid))
return NULL;
wcid = rcu_dereference(dev->wcid[idx]);
if (unicast || !wcid)
return wcid;
sta = container_of(wcid, struct mt76x2_sta, wcid);
return &sta->vif->group_wcid;
}
int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
void *rxi)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
struct mt76x2_rxwi *rxwi = rxi;
u32 rxinfo = le32_to_cpu(rxwi->rxinfo);
u32 ctl = le32_to_cpu(rxwi->ctl);
u16 rate = le16_to_cpu(rxwi->rate);
u16 tid_sn = le16_to_cpu(rxwi->tid_sn);
bool unicast = rxwi->rxinfo & cpu_to_le32(MT_RXINFO_UNICAST);
int pad_len = 0;
u8 pn_len;
u8 wcid;
int len;
if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD))
mt76x2_remove_hdr_pad(skb);
if (rxinfo & MT_RXINFO_L2PAD)
pad_len += 2;
if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_DECRYPT)) {
if (rxinfo & MT_RXINFO_DECRYPT) {
status->flag |= RX_FLAG_DECRYPTED;
status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED;
status->flag |= RX_FLAG_MMIC_STRIPPED;
status->flag |= RX_FLAG_MIC_STRIPPED;
status->flag |= RX_FLAG_IV_STRIPPED;
}
wcid = FIELD_GET(MT_RXWI_CTL_WCID, ctl);
status->wcid = mt76x2_rx_get_sta_wcid(dev, wcid, unicast);
len = FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl);
pn_len = FIELD_GET(MT_RXINFO_PN_LEN, rxinfo);
if (pn_len) {
int offset = ieee80211_get_hdrlen_from_skb(skb) + pad_len;
u8 *data = skb->data + offset;
status->iv[0] = data[7];
status->iv[1] = data[6];
status->iv[2] = data[5];
status->iv[3] = data[4];
status->iv[4] = data[1];
status->iv[5] = data[0];
/*
* Driver CCMP validation can't deal with fragments.
* Let mac80211 take care of it.
*/
if (rxinfo & MT_RXINFO_FRAG) {
status->flag &= ~RX_FLAG_IV_STRIPPED;
} else {
pad_len += pn_len << 2;
len -= pn_len << 2;
}
}
mt76x2_remove_hdr_pad(skb, pad_len);
if (rxinfo & MT_RXINFO_BA)
status->aggr = true;
if (WARN_ON_ONCE(len > skb->len))
return -EINVAL;
@ -273,6 +355,9 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
status->freq = dev->mt76.chandef.chan->center_freq;
status->band = dev->mt76.chandef.chan->band;
status->tid = FIELD_GET(MT_RXWI_TID, tid_sn);
status->seqno = FIELD_GET(MT_RXWI_SN, tid_sn);
return mt76x2_mac_process_rate(status, rate);
}
@ -618,7 +703,6 @@ mt76_write_beacon(struct mt76x2_dev *dev, int offset, struct sk_buff *skb)
return -ENOSPC;
mt76x2_mac_write_txwi(dev, &txwi, skb, NULL, NULL);
txwi.flags |= cpu_to_le16(MT_TXWI_FLAGS_TS);
mt76_wr_copy(dev, offset, &txwi, sizeof(txwi));
offset += sizeof(txwi);

View File

@ -127,6 +127,7 @@ mt76x2_set_channel(struct mt76x2_dev *dev, struct cfg80211_chan_def *chandef)
mt76_set_channel(&dev->mt76);
tasklet_disable(&dev->pre_tbtt_tasklet);
tasklet_disable(&dev->dfs_pd.dfs_tasklet);
cancel_delayed_work_sync(&dev->cal_work);
mt76x2_mac_stop(dev, true);
@ -139,6 +140,7 @@ mt76x2_set_channel(struct mt76x2_dev *dev, struct cfg80211_chan_def *chandef)
mt76x2_dfs_init_params(dev);
mt76x2_mac_resume(dev);
tasklet_enable(&dev->dfs_pd.dfs_tasklet);
tasklet_enable(&dev->pre_tbtt_tasklet);
return ret;
@ -271,6 +273,8 @@ mt76x2_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
goto out;
}
msta->vif = mvif;
msta->wcid.sta = 1;
msta->wcid.idx = idx;
msta->wcid.hw_key_idx = -1;
mt76x2_mac_wcid_setup(dev, idx, mvif->idx, sta->addr);
@ -355,12 +359,19 @@ mt76x2_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
if (cmd == SET_KEY) {
key->hw_key_idx = wcid->idx;
wcid->hw_key_idx = idx;
if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) {
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
wcid->sw_iv = true;
}
} else {
if (idx == wcid->hw_key_idx)
if (idx == wcid->hw_key_idx) {
wcid->hw_key_idx = -1;
wcid->sw_iv = true;
}
key = NULL;
}
mt76_wcid_key_setup(&dev->mt76, wcid, key);
if (!msta) {
if (key || wcid->hw_key_idx == idx) {
@ -380,9 +391,11 @@ mt76x2_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct mt76x2_dev *dev = hw->priv;
u8 cw_min = 5, cw_max = 10;
u8 cw_min = 5, cw_max = 10, qid;
u32 val;
qid = dev->mt76.q_tx[queue].hw_idx;
if (params->cw_min)
cw_min = fls(params->cw_min);
if (params->cw_max)
@ -392,26 +405,26 @@ mt76x2_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
FIELD_PREP(MT_EDCA_CFG_AIFSN, params->aifs) |
FIELD_PREP(MT_EDCA_CFG_CWMIN, cw_min) |
FIELD_PREP(MT_EDCA_CFG_CWMAX, cw_max);
mt76_wr(dev, MT_EDCA_CFG_AC(queue), val);
mt76_wr(dev, MT_EDCA_CFG_AC(qid), val);
val = mt76_rr(dev, MT_WMM_TXOP(queue));
val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(queue));
val |= params->txop << MT_WMM_TXOP_SHIFT(queue);
mt76_wr(dev, MT_WMM_TXOP(queue), val);
val = mt76_rr(dev, MT_WMM_TXOP(qid));
val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(qid));
val |= params->txop << MT_WMM_TXOP_SHIFT(qid);
mt76_wr(dev, MT_WMM_TXOP(qid), val);
val = mt76_rr(dev, MT_WMM_AIFSN);
val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(queue));
val |= params->aifs << MT_WMM_AIFSN_SHIFT(queue);
val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(qid));
val |= params->aifs << MT_WMM_AIFSN_SHIFT(qid);
mt76_wr(dev, MT_WMM_AIFSN, val);
val = mt76_rr(dev, MT_WMM_CWMIN);
val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(queue));
val |= cw_min << MT_WMM_CWMIN_SHIFT(queue);
val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(qid));
val |= cw_min << MT_WMM_CWMIN_SHIFT(qid);
mt76_wr(dev, MT_WMM_CWMIN, val);
val = mt76_rr(dev, MT_WMM_CWMAX);
val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(queue));
val |= cw_max << MT_WMM_CWMAX_SHIFT(queue);
val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(qid));
val |= cw_max << MT_WMM_CWMAX_SHIFT(qid);
mt76_wr(dev, MT_WMM_CWMAX, val);
return 0;
@ -476,9 +489,11 @@ mt76x2_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
switch (action) {
case IEEE80211_AMPDU_RX_START:
mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, *ssn, params->buf_size);
mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid));
break;
case IEEE80211_AMPDU_RX_STOP:
mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid);
mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4,
BIT(16 + tid));
break;

View File

@ -36,7 +36,9 @@ void mt76x2_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
msta = (struct mt76x2_sta *) control->sta->drv_priv;
wcid = &msta->wcid;
} else if (vif) {
}
if (vif || (!info->control.hw_key && wcid->hw_key_idx != -1)) {
struct mt76x2_vif *mvif;
mvif = (struct mt76x2_vif *) vif->drv_priv;
@ -166,7 +168,7 @@ int mt76x2_tx_prepare_skb(struct mt76_dev *mdev, void *txwi,
*tx_info = FIELD_PREP(MT_TXD_INFO_QSEL, qsel) |
MT_TXD_INFO_80211;
if (!wcid || wcid->hw_key_idx == 0xff)
if (!wcid || wcid->hw_key_idx == 0xff || wcid->sw_iv)
*tx_info |= MT_TXD_INFO_WIV;
return 0;

View File

@ -80,6 +80,41 @@ qtnf_mgmt_stypes[NUM_NL80211_IFTYPES] = {
},
};
static int
qtnf_validate_iface_combinations(struct wiphy *wiphy,
struct qtnf_vif *change_vif,
enum nl80211_iftype new_type)
{
struct qtnf_wmac *mac;
struct qtnf_vif *vif;
int i;
int ret = 0;
struct iface_combination_params params = {
.num_different_channels = 1,
};
mac = wiphy_priv(wiphy);
if (!mac)
return -EFAULT;
for (i = 0; i < QTNF_MAX_INTF; i++) {
vif = &mac->iflist[i];
if (vif->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED)
params.iftype_num[vif->wdev.iftype]++;
}
if (change_vif) {
params.iftype_num[new_type]++;
params.iftype_num[change_vif->wdev.iftype]--;
} else {
params.iftype_num[new_type]++;
}
ret = cfg80211_check_combinations(wiphy, &params);
return ret;
}
static int
qtnf_change_virtual_intf(struct wiphy *wiphy,
struct net_device *dev,
@ -90,6 +125,13 @@ qtnf_change_virtual_intf(struct wiphy *wiphy,
u8 *mac_addr;
int ret;
ret = qtnf_validate_iface_combinations(wiphy, vif, type);
if (ret) {
pr_err("VIF%u.%u combination check: failed to set type %d\n",
vif->mac->macid, vif->vifid, type);
return ret;
}
if (params)
mac_addr = params->macaddr;
else
@ -120,10 +162,6 @@ int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
qtnf_scan_done(vif->mac, true);
if (qtnf_cmd_send_del_intf(vif))
pr_err("VIF%u.%u: failed to delete VIF\n", vif->mac->macid,
vif->vifid);
/* Stop data */
netif_tx_stop_all_queues(netdev);
if (netif_carrier_ok(netdev))
@ -132,6 +170,10 @@ int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
if (netdev->reg_state == NETREG_REGISTERED)
unregister_netdevice(netdev);
if (qtnf_cmd_send_del_intf(vif))
pr_err("VIF%u.%u: failed to delete VIF\n", vif->mac->macid,
vif->vifid);
vif->netdev->ieee80211_ptr = NULL;
vif->netdev = NULL;
vif->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
@ -150,12 +192,20 @@ static struct wireless_dev *qtnf_add_virtual_intf(struct wiphy *wiphy,
struct qtnf_wmac *mac;
struct qtnf_vif *vif;
u8 *mac_addr = NULL;
int ret;
mac = wiphy_priv(wiphy);
if (!mac)
return ERR_PTR(-EFAULT);
ret = qtnf_validate_iface_combinations(wiphy, NULL, type);
if (ret) {
pr_err("MAC%u invalid combination: failed to add type %d\n",
mac->macid, type);
return ERR_PTR(ret);
}
switch (type) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_AP:
@ -545,19 +595,13 @@ qtnf_del_station(struct wiphy *wiphy, struct net_device *dev,
return ret;
}
static void qtnf_scan_timeout(struct timer_list *t)
{
struct qtnf_wmac *mac = from_timer(mac, t, scan_timeout);
pr_warn("mac%d scan timed out\n", mac->macid);
qtnf_scan_done(mac, true);
}
static int
qtnf_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
{
struct qtnf_wmac *mac = wiphy_priv(wiphy);
cancel_delayed_work_sync(&mac->scan_timeout);
mac->scan_req = request;
if (qtnf_cmd_send_scan(mac)) {
@ -566,9 +610,8 @@ qtnf_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
return -EFAULT;
}
mac->scan_timeout.function = qtnf_scan_timeout;
mod_timer(&mac->scan_timeout,
jiffies + QTNF_SCAN_TIMEOUT_SEC * HZ);
queue_delayed_work(mac->bus->workqueue, &mac->scan_timeout,
QTNF_SCAN_TIMEOUT_SEC * HZ);
return 0;
}
@ -629,7 +672,6 @@ qtnf_disconnect(struct wiphy *wiphy, struct net_device *dev,
return ret;
}
vif->sta_state = QTNF_STA_DISCONNECTED;
return 0;
}
@ -876,29 +918,29 @@ struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus)
return wiphy;
}
static int qtnf_wiphy_setup_if_comb(struct wiphy *wiphy,
struct ieee80211_iface_combination *if_comb,
const struct qtnf_mac_info *mac_info)
static int
qtnf_wiphy_setup_if_comb(struct wiphy *wiphy, struct qtnf_mac_info *mac_info)
{
size_t max_interfaces = 0;
struct ieee80211_iface_combination *if_comb;
size_t n_if_comb;
u16 interface_modes = 0;
size_t i;
size_t i, j;
if (unlikely(!mac_info->limits || !mac_info->n_limits))
if_comb = mac_info->if_comb;
n_if_comb = mac_info->n_if_comb;
if (!if_comb || !n_if_comb)
return -ENOENT;
if_comb->limits = mac_info->limits;
if_comb->n_limits = mac_info->n_limits;
for (i = 0; i < n_if_comb; i++) {
if_comb[i].radar_detect_widths = mac_info->radar_detect_widths;
for (i = 0; i < mac_info->n_limits; i++) {
max_interfaces += mac_info->limits[i].max;
interface_modes |= mac_info->limits[i].types;
for (j = 0; j < if_comb[i].n_limits; j++)
interface_modes |= if_comb[i].limits[j].types;
}
if_comb->num_different_channels = 1;
if_comb->beacon_int_infra_match = true;
if_comb->max_interfaces = max_interfaces;
if_comb->radar_detect_widths = mac_info->radar_detect_widths;
wiphy->iface_combinations = if_comb;
wiphy->n_iface_combinations = n_if_comb;
wiphy->interface_modes = interface_modes;
return 0;
@ -907,7 +949,6 @@ static int qtnf_wiphy_setup_if_comb(struct wiphy *wiphy,
int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
{
struct wiphy *wiphy = priv_to_wiphy(mac);
struct ieee80211_iface_combination *iface_comb = NULL;
int ret;
if (!wiphy) {
@ -915,14 +956,6 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
return -EFAULT;
}
iface_comb = kzalloc(sizeof(*iface_comb), GFP_KERNEL);
if (!iface_comb)
return -ENOMEM;
ret = qtnf_wiphy_setup_if_comb(wiphy, iface_comb, &mac->macinfo);
if (ret)
goto out;
wiphy->frag_threshold = mac->macinfo.frag_thr;
wiphy->rts_threshold = mac->macinfo.rts_thr;
wiphy->retry_short = mac->macinfo.sretry_limit;
@ -934,11 +967,12 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
wiphy->mgmt_stypes = qtnf_mgmt_stypes;
wiphy->max_remain_on_channel_duration = 5000;
wiphy->max_acl_mac_addrs = mac->macinfo.max_acl_mac_addrs;
wiphy->iface_combinations = iface_comb;
wiphy->n_iface_combinations = 1;
wiphy->max_num_csa_counters = 2;
ret = qtnf_wiphy_setup_if_comb(wiphy, &mac->macinfo);
if (ret)
goto out;
/* Initialize cipher suits */
wiphy->cipher_suites = qtnf_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(qtnf_cipher_suites);
@ -972,6 +1006,10 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
}
strlcpy(wiphy->fw_version, hw_info->fw_version,
sizeof(wiphy->fw_version));
wiphy->hw_version = hw_info->hw_version;
ret = wiphy_register(wiphy);
if (ret < 0)
goto out;
@ -983,12 +1021,7 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
ret = regulatory_hint(wiphy, hw_info->rd->alpha2);
out:
if (ret) {
kfree(iface_comb);
return ret;
}
return 0;
return ret;
}
void qtnf_netdev_updown(struct net_device *ndev, bool up)

View File

@ -28,23 +28,4 @@ void qtnf_band_init_rates(struct ieee80211_supported_band *band);
void qtnf_band_setup_htvht_caps(struct qtnf_mac_info *macinfo,
struct ieee80211_supported_band *band);
static inline void qtnf_scan_done(struct qtnf_wmac *mac, bool aborted)
{
struct cfg80211_scan_info info = {
.aborted = aborted,
};
if (timer_pending(&mac->scan_timeout))
del_timer_sync(&mac->scan_timeout);
mutex_lock(&mac->mac_lock);
if (mac->scan_req) {
cfg80211_scan_done(mac->scan_req, &info);
mac->scan_req = NULL;
}
mutex_unlock(&mac->mac_lock);
}
#endif /* _QTN_FMAC_CFG80211_H_ */

View File

@ -187,7 +187,8 @@ static bool qtnf_cmd_start_ap_can_fit(const struct qtnf_vif *vif,
len += sizeof(struct qlink_tlv_chandef);
if (s->acl)
len += qtnf_cmd_acl_data_size(s->acl);
len += sizeof(struct qlink_tlv_hdr) +
qtnf_cmd_acl_data_size(s->acl);
if (len > (sizeof(struct qlink_cmd) + QTNF_MAX_CMD_BUF_SIZE)) {
pr_err("VIF%u.%u: can not fit AP settings: %u\n",
@ -214,7 +215,7 @@ int qtnf_cmd_send_start_ap(struct qtnf_vif *vif,
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_START_AP,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
cmd = (struct qlink_cmd_start_ap *)cmd_skb->data;
@ -334,7 +335,7 @@ int qtnf_cmd_send_stop_ap(struct qtnf_vif *vif)
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_STOP_AP,
sizeof(struct qlink_cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(vif->mac->bus);
@ -368,7 +369,7 @@ int qtnf_cmd_send_register_mgmt(struct qtnf_vif *vif, u16 frame_type, bool reg)
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_REGISTER_MGMT,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(vif->mac->bus);
@ -411,7 +412,7 @@ int qtnf_cmd_send_mgmt_frame(struct qtnf_vif *vif, u32 cookie, u16 flags,
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_SEND_MGMT_FRAME,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(vif->mac->bus);
@ -457,7 +458,7 @@ int qtnf_cmd_send_mgmt_set_appie(struct qtnf_vif *vif, u8 frame_type,
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_MGMT_SET_APPIE,
sizeof(struct qlink_cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_cmd_tlv_ie_set_add(cmd_skb, frame_type, buf, len);
@ -712,8 +713,7 @@ int qtnf_cmd_get_sta_info(struct qtnf_vif *vif, const u8 *sta_mac,
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_GET_STA_INFO,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(vif->mac->bus);
@ -778,7 +778,7 @@ static int qtnf_cmd_send_add_change_intf(struct qtnf_vif *vif,
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
cmd_type,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(vif->mac->bus);
@ -851,7 +851,7 @@ int qtnf_cmd_send_del_intf(struct qtnf_vif *vif)
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_DEL_INTF,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(vif->mac->bus);
@ -948,6 +948,16 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
struct qtnf_hw_info *hwinfo = &bus->hw_info;
const struct qlink_tlv_hdr *tlv;
const struct qlink_tlv_reg_rule *tlv_rule;
const char *bld_name = NULL;
const char *bld_rev = NULL;
const char *bld_type = NULL;
const char *bld_label = NULL;
u32 bld_tmstamp = 0;
u32 plat_id = 0;
const char *hw_id = NULL;
const char *calibration_ver = NULL;
const char *uboot_ver = NULL;
u32 hw_ver = 0;
struct ieee80211_reg_rule *rule;
u16 tlv_type;
u16 tlv_value_len;
@ -974,6 +984,10 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
hwinfo->rd->alpha2[0] = resp->alpha2[0];
hwinfo->rd->alpha2[1] = resp->alpha2[1];
bld_tmstamp = le32_to_cpu(resp->bld_tmstamp);
plat_id = le32_to_cpu(resp->plat_id);
hw_ver = le32_to_cpu(resp->hw_ver);
switch (resp->dfs_region) {
case QLINK_DFS_FCC:
hwinfo->rd->dfs_region = NL80211_DFS_FCC;
@ -1034,6 +1048,27 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
rule->flags = qtnf_cmd_resp_reg_rule_flags_parse(
le32_to_cpu(tlv_rule->flags));
break;
case QTN_TLV_ID_BUILD_NAME:
bld_name = (const void *)tlv->val;
break;
case QTN_TLV_ID_BUILD_REV:
bld_rev = (const void *)tlv->val;
break;
case QTN_TLV_ID_BUILD_TYPE:
bld_type = (const void *)tlv->val;
break;
case QTN_TLV_ID_BUILD_LABEL:
bld_label = (const void *)tlv->val;
break;
case QTN_TLV_ID_HW_ID:
hw_id = (const void *)tlv->val;
break;
case QTN_TLV_ID_CALIBRATION_VER:
calibration_ver = (const void *)tlv->val;
break;
case QTN_TLV_ID_UBOOT_VER:
uboot_ver = (const void *)tlv->val;
break;
default:
break;
}
@ -1056,25 +1091,46 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
hwinfo->total_tx_chain, hwinfo->total_rx_chain,
hwinfo->hw_capab);
pr_info("\nBuild name: %s" \
"\nBuild revision: %s" \
"\nBuild type: %s" \
"\nBuild label: %s" \
"\nBuild timestamp: %lu" \
"\nPlatform ID: %lu" \
"\nHardware ID: %s" \
"\nCalibration version: %s" \
"\nU-Boot version: %s" \
"\nHardware version: 0x%08x",
bld_name, bld_rev, bld_type, bld_label,
(unsigned long)bld_tmstamp,
(unsigned long)plat_id,
hw_id, calibration_ver, uboot_ver, hw_ver);
strlcpy(hwinfo->fw_version, bld_label, sizeof(hwinfo->fw_version));
hwinfo->hw_version = hw_ver;
return 0;
}
static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
const u8 *tlv_buf, size_t tlv_buf_size)
{
struct ieee80211_iface_limit *limits = NULL;
const struct qlink_iface_limit *limit_record;
size_t record_count = 0, rec = 0;
u16 tlv_type, tlv_value_len;
struct qlink_iface_comb_num *comb;
struct ieee80211_iface_combination *comb = NULL;
size_t n_comb = 0;
struct ieee80211_iface_limit *limits;
const struct qlink_iface_comb_num *comb_num;
const struct qlink_iface_limit_record *rec;
const struct qlink_iface_limit *lim;
u16 rec_len;
u16 tlv_type;
u16 tlv_value_len;
size_t tlv_full_len;
const struct qlink_tlv_hdr *tlv;
u8 *ext_capa = NULL;
u8 *ext_capa_mask = NULL;
u8 ext_capa_len = 0;
u8 ext_capa_mask_len = 0;
mac->macinfo.n_limits = 0;
int i = 0;
tlv = (const struct qlink_tlv_hdr *)tlv_buf;
while (tlv_buf_size >= sizeof(struct qlink_tlv_hdr)) {
@ -1089,52 +1145,77 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
switch (tlv_type) {
case QTN_TLV_ID_NUM_IFACE_COMB:
if (unlikely(tlv_value_len != sizeof(*comb)))
if (tlv_value_len != sizeof(*comb_num))
return -EINVAL;
comb = (void *)tlv->val;
record_count = le16_to_cpu(comb->iface_comb_num);
comb_num = (void *)tlv->val;
mac->macinfo.n_limits = record_count;
/* free earlier iface limits memory */
kfree(mac->macinfo.limits);
mac->macinfo.limits =
kzalloc(sizeof(*mac->macinfo.limits) *
record_count, GFP_KERNEL);
/* free earlier iface comb memory */
qtnf_mac_iface_comb_free(mac);
if (unlikely(!mac->macinfo.limits))
mac->macinfo.n_if_comb =
le32_to_cpu(comb_num->iface_comb_num);
mac->macinfo.if_comb =
kcalloc(mac->macinfo.n_if_comb,
sizeof(*mac->macinfo.if_comb),
GFP_KERNEL);
if (!mac->macinfo.if_comb)
return -ENOMEM;
limits = mac->macinfo.limits;
comb = mac->macinfo.if_comb;
pr_debug("MAC%u: %zu iface combinations\n",
mac->macid, mac->macinfo.n_if_comb);
break;
case QTN_TLV_ID_IFACE_LIMIT:
if (unlikely(!limits)) {
pr_warn("MAC%u: limits are not inited\n",
if (unlikely(!comb)) {
pr_warn("MAC%u: no combinations advertised\n",
mac->macid);
return -EINVAL;
}
if (unlikely(tlv_value_len != sizeof(*limit_record))) {
pr_warn("MAC%u: record size mismatch\n",
if (n_comb >= mac->macinfo.n_if_comb) {
pr_warn("MAC%u: combinations count exceeded\n",
mac->macid);
n_comb++;
break;
}
rec = (void *)tlv->val;
rec_len = sizeof(*rec) + rec->n_limits * sizeof(*lim);
if (unlikely(tlv_value_len != rec_len)) {
pr_warn("MAC%u: record %zu size mismatch\n",
mac->macid, n_comb);
return -EINVAL;
}
limit_record = (void *)tlv->val;
limits[rec].max = le16_to_cpu(limit_record->max_num);
limits[rec].types = qlink_iface_type_to_nl_mask(
le16_to_cpu(limit_record->type));
limits = kzalloc(sizeof(*limits) * rec->n_limits,
GFP_KERNEL);
if (!limits)
return -ENOMEM;
/* supported modes: STA, AP */
limits[rec].types &= BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_AP_VLAN) |
BIT(NL80211_IFTYPE_STATION);
comb[n_comb].num_different_channels =
rec->num_different_channels;
comb[n_comb].max_interfaces =
le16_to_cpu(rec->max_interfaces);
comb[n_comb].n_limits = rec->n_limits;
comb[n_comb].limits = limits;
pr_debug("MAC%u: MAX: %u; TYPES: %.4X\n", mac->macid,
limits[rec].max, limits[rec].types);
for (i = 0; i < rec->n_limits; i++) {
lim = &rec->limits[i];
limits[i].max = le16_to_cpu(lim->max_num);
limits[i].types =
qlink_iface_type_to_nl_mask(le16_to_cpu(lim->type));
pr_debug("MAC%u: comb[%zu]: MAX:%u TYPES:%.4X\n",
mac->macid, n_comb,
limits[i].max, limits[i].types);
}
if (limits[rec].types)
rec++;
n_comb++;
break;
case WLAN_EID_EXT_CAPABILITY:
if (unlikely(tlv_value_len > U8_MAX))
@ -1162,9 +1243,9 @@ static int qtnf_parse_variable_mac_info(struct qtnf_wmac *mac,
return -EINVAL;
}
if (mac->macinfo.n_limits != rec) {
if (mac->macinfo.n_if_comb != n_comb) {
pr_err("MAC%u: combination mismatch: reported=%zu parsed=%zu\n",
mac->macid, mac->macinfo.n_limits, rec);
mac->macid, mac->macinfo.n_if_comb, n_comb);
return -EINVAL;
}
@ -1575,7 +1656,7 @@ int qtnf_cmd_get_mac_info(struct qtnf_wmac *mac)
cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
QLINK_CMD_MAC_INFO,
sizeof(struct qlink_cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(mac->bus);
@ -1613,7 +1694,7 @@ int qtnf_cmd_get_hw_info(struct qtnf_bus *bus)
cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD,
QLINK_CMD_GET_HW_INFO,
sizeof(struct qlink_cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(bus);
@ -1793,7 +1874,7 @@ int qtnf_cmd_send_init_fw(struct qtnf_bus *bus)
cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD,
QLINK_CMD_FW_INIT,
sizeof(struct qlink_cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(bus);
@ -1842,7 +1923,7 @@ int qtnf_cmd_send_add_key(struct qtnf_vif *vif, u8 key_index, bool pairwise,
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_ADD_KEY,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(vif->mac->bus);
@ -1895,7 +1976,7 @@ int qtnf_cmd_send_del_key(struct qtnf_vif *vif, u8 key_index, bool pairwise,
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_DEL_KEY,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(vif->mac->bus);
@ -1936,7 +2017,7 @@ int qtnf_cmd_send_set_default_key(struct qtnf_vif *vif, u8 key_index,
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_SET_DEFAULT_KEY,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(vif->mac->bus);
@ -1971,7 +2052,7 @@ int qtnf_cmd_send_set_default_mgmt_key(struct qtnf_vif *vif, u8 key_index)
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_SET_DEFAULT_MGMT_KEY,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(vif->mac->bus);
@ -2026,7 +2107,7 @@ int qtnf_cmd_send_change_sta(struct qtnf_vif *vif, const u8 *mac,
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_CHANGE_STA,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(vif->mac->bus);
@ -2078,7 +2159,7 @@ int qtnf_cmd_send_del_sta(struct qtnf_vif *vif,
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_DEL_STA,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(vif->mac->bus);
@ -2148,7 +2229,7 @@ int qtnf_cmd_send_scan(struct qtnf_wmac *mac)
cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, QLINK_VIFID_RSVD,
QLINK_CMD_SCAN,
sizeof(struct qlink_cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(mac->bus);
@ -2218,7 +2299,7 @@ int qtnf_cmd_send_connect(struct qtnf_vif *vif,
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_CONNECT,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
cmd = (struct qlink_cmd_connect *)cmd_skb->data;
@ -2320,7 +2401,7 @@ int qtnf_cmd_send_disconnect(struct qtnf_vif *vif, u16 reason_code)
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_DISCONNECT,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(vif->mac->bus);
@ -2354,7 +2435,7 @@ int qtnf_cmd_send_updown_intf(struct qtnf_vif *vif, bool up)
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_UPDOWN_INTF,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
cmd = (struct qlink_cmd_updown *)cmd_skb->data;
@ -2515,8 +2596,7 @@ int qtnf_cmd_send_chan_switch(struct qtnf_vif *vif,
cmd_skb = qtnf_cmd_alloc_new_cmdskb(mac->macid, vif->vifid,
QLINK_CMD_CHAN_SWITCH,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(mac->bus);
@ -2568,7 +2648,7 @@ int qtnf_cmd_get_channel(struct qtnf_vif *vif, struct cfg80211_chan_def *chdef)
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_CHAN_GET,
sizeof(struct qlink_cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
qtnf_bus_lock(bus);
@ -2607,7 +2687,7 @@ int qtnf_cmd_start_cac(const struct qtnf_vif *vif,
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_START_CAC,
sizeof(*cmd));
if (unlikely(!cmd_skb))
if (!cmd_skb)
return -ENOMEM;
cmd = (struct qlink_cmd_start_cac *)cmd_skb->data;
@ -2637,19 +2717,21 @@ int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif,
{
struct qtnf_bus *bus = vif->mac->bus;
struct sk_buff *cmd_skb;
struct qlink_cmd_set_mac_acl *cmd;
struct qlink_tlv_hdr *tlv;
size_t acl_size = qtnf_cmd_acl_data_size(params);
u16 res_code;
int ret;
cmd_skb = qtnf_cmd_alloc_new_cmdskb(vif->mac->macid, vif->vifid,
QLINK_CMD_SET_MAC_ACL,
sizeof(*cmd) +
qtnf_cmd_acl_data_size(params));
if (unlikely(!cmd_skb))
sizeof(struct qlink_cmd));
if (!cmd_skb)
return -ENOMEM;
cmd = (struct qlink_cmd_set_mac_acl *)cmd_skb->data;
qlink_acl_data_cfg2q(params, &cmd->acl);
tlv = skb_put(cmd_skb, sizeof(*tlv) + acl_size);
tlv->type = cpu_to_le16(QTN_TLV_ID_ACL_DATA);
tlv->len = cpu_to_le16(acl_size);
qlink_acl_data_cfg2q(params, (struct qlink_acl_data *)tlv->val);
qtnf_bus_lock(bus);
ret = qtnf_cmd_send(bus, cmd_skb, &res_code);

View File

@ -119,9 +119,38 @@ qtnf_netdev_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
/* Netdev handler for getting stats.
*/
static struct net_device_stats *qtnf_netdev_get_stats(struct net_device *dev)
static void qtnf_netdev_get_stats64(struct net_device *ndev,
struct rtnl_link_stats64 *stats)
{
return &dev->stats;
struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev);
unsigned int start;
int cpu;
netdev_stats_to_stats64(stats, &ndev->stats);
if (!vif->stats64)
return;
for_each_possible_cpu(cpu) {
struct pcpu_sw_netstats *stats64;
u64 rx_packets, rx_bytes;
u64 tx_packets, tx_bytes;
stats64 = per_cpu_ptr(vif->stats64, cpu);
do {
start = u64_stats_fetch_begin_irq(&stats64->syncp);
rx_packets = stats64->rx_packets;
rx_bytes = stats64->rx_bytes;
tx_packets = stats64->tx_packets;
tx_bytes = stats64->tx_bytes;
} while (u64_stats_fetch_retry_irq(&stats64->syncp, start));
stats->rx_packets += rx_packets;
stats->rx_bytes += rx_bytes;
stats->tx_packets += tx_packets;
stats->tx_bytes += tx_bytes;
}
}
/* Netdev handler for transmission timeout.
@ -156,7 +185,7 @@ const struct net_device_ops qtnf_netdev_ops = {
.ndo_stop = qtnf_netdev_close,
.ndo_start_xmit = qtnf_netdev_hard_start_xmit,
.ndo_tx_timeout = qtnf_netdev_tx_timeout,
.ndo_get_stats = qtnf_netdev_get_stats,
.ndo_get_stats64 = qtnf_netdev_get_stats64,
};
static int qtnf_mac_init_single_band(struct wiphy *wiphy,
@ -233,6 +262,23 @@ struct qtnf_vif *qtnf_mac_get_base_vif(struct qtnf_wmac *mac)
return vif;
}
void qtnf_mac_iface_comb_free(struct qtnf_wmac *mac)
{
struct ieee80211_iface_combination *comb;
int i;
if (mac->macinfo.if_comb) {
for (i = 0; i < mac->macinfo.n_if_comb; i++) {
comb = &mac->macinfo.if_comb[i];
kfree(comb->limits);
comb->limits = NULL;
}
kfree(mac->macinfo.if_comb);
mac->macinfo.if_comb = NULL;
}
}
static void qtnf_vif_reset_handler(struct work_struct *work)
{
struct qtnf_vif *vif = container_of(work, struct qtnf_vif, reset_work);
@ -265,6 +311,37 @@ static void qtnf_mac_init_primary_intf(struct qtnf_wmac *mac)
vif->cons_tx_timeout_cnt = 0;
}
static void qtnf_mac_scan_finish(struct qtnf_wmac *mac, bool aborted)
{
struct cfg80211_scan_info info = {
.aborted = aborted,
};
mutex_lock(&mac->mac_lock);
if (mac->scan_req) {
cfg80211_scan_done(mac->scan_req, &info);
mac->scan_req = NULL;
}
mutex_unlock(&mac->mac_lock);
}
void qtnf_scan_done(struct qtnf_wmac *mac, bool aborted)
{
cancel_delayed_work_sync(&mac->scan_timeout);
qtnf_mac_scan_finish(mac, aborted);
}
static void qtnf_mac_scan_timeout(struct work_struct *work)
{
struct qtnf_wmac *mac =
container_of(work, struct qtnf_wmac, scan_timeout.work);
pr_warn("MAC%d: scan timed out\n", mac->macid);
qtnf_mac_scan_finish(mac, true);
}
static struct qtnf_wmac *qtnf_core_mac_alloc(struct qtnf_bus *bus,
unsigned int macid)
{
@ -288,7 +365,12 @@ static struct qtnf_wmac *qtnf_core_mac_alloc(struct qtnf_bus *bus,
mac->iflist[i].vifid = i;
qtnf_sta_list_init(&mac->iflist[i].sta_list);
mutex_init(&mac->mac_lock);
timer_setup(&mac->scan_timeout, NULL, 0);
INIT_DELAYED_WORK(&mac->scan_timeout, qtnf_mac_scan_timeout);
mac->iflist[i].stats64 =
netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
if (!mac->iflist[i].stats64)
pr_warn("VIF%u.%u: per cpu stats allocation failed\n",
macid, i);
}
qtnf_mac_init_primary_intf(mac);
@ -297,6 +379,10 @@ static struct qtnf_wmac *qtnf_core_mac_alloc(struct qtnf_bus *bus,
return mac;
}
static const struct ethtool_ops qtnf_ethtool_ops = {
.get_drvinfo = cfg80211_get_drvinfo,
};
int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *vif,
const char *name, unsigned char name_assign_type)
{
@ -324,6 +410,7 @@ int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *vif,
dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
dev->watchdog_timeo = QTNF_DEF_WDOG_TIMEOUT;
dev->tx_queue_len = 100;
dev->ethtool_ops = &qtnf_ethtool_ops;
qdev_vif = netdev_priv(dev);
*((void **)qdev_vif) = vif;
@ -364,6 +451,7 @@ static void qtnf_core_mac_detach(struct qtnf_bus *bus, unsigned int macid)
}
rtnl_unlock();
qtnf_sta_list_free(&vif->sta_list);
free_percpu(vif->stats64);
}
if (mac->wiphy_registered)
@ -380,10 +468,9 @@ static void qtnf_core_mac_detach(struct qtnf_bus *bus, unsigned int macid)
wiphy->bands[band] = NULL;
}
kfree(mac->macinfo.limits);
qtnf_mac_iface_comb_free(mac);
kfree(mac->macinfo.extended_capabilities);
kfree(mac->macinfo.extended_capabilities_mask);
kfree(wiphy->iface_combinations);
wiphy_free(wiphy);
bus->mac[macid] = NULL;
}
@ -643,6 +730,46 @@ void qtnf_wake_all_queues(struct net_device *ndev)
}
EXPORT_SYMBOL_GPL(qtnf_wake_all_queues);
void qtnf_update_rx_stats(struct net_device *ndev, const struct sk_buff *skb)
{
struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev);
struct pcpu_sw_netstats *stats64;
if (unlikely(!vif || !vif->stats64)) {
ndev->stats.rx_packets++;
ndev->stats.rx_bytes += skb->len;
return;
}
stats64 = this_cpu_ptr(vif->stats64);
u64_stats_update_begin(&stats64->syncp);
stats64->rx_packets++;
stats64->rx_bytes += skb->len;
u64_stats_update_end(&stats64->syncp);
}
EXPORT_SYMBOL_GPL(qtnf_update_rx_stats);
void qtnf_update_tx_stats(struct net_device *ndev, const struct sk_buff *skb)
{
struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev);
struct pcpu_sw_netstats *stats64;
if (unlikely(!vif || !vif->stats64)) {
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += skb->len;
return;
}
stats64 = this_cpu_ptr(vif->stats64);
u64_stats_update_begin(&stats64->syncp);
stats64->tx_packets++;
stats64->tx_bytes += skb->len;
u64_stats_update_end(&stats64->syncp);
}
EXPORT_SYMBOL_GPL(qtnf_update_tx_stats);
MODULE_AUTHOR("Quantenna Communications");
MODULE_DESCRIPTION("Quantenna 802.11 wireless LAN FullMAC driver.");
MODULE_LICENSE("GPL");

View File

@ -89,6 +89,8 @@ struct qtnf_vif {
struct qtnf_sta_list sta_list;
unsigned long cons_tx_timeout_cnt;
int generation;
struct pcpu_sw_netstats __percpu *stats64;
};
struct qtnf_mac_info {
@ -106,8 +108,8 @@ struct qtnf_mac_info {
u32 max_acl_mac_addrs;
struct ieee80211_ht_cap ht_cap_mod_mask;
struct ieee80211_vht_cap vht_cap_mod_mask;
struct ieee80211_iface_limit *limits;
size_t n_limits;
struct ieee80211_iface_combination *if_comb;
size_t n_if_comb;
u8 *extended_capabilities;
u8 *extended_capabilities_mask;
u8 extended_capabilities_len;
@ -131,7 +133,7 @@ struct qtnf_wmac {
struct qtnf_vif iflist[QTNF_MAX_INTF];
struct cfg80211_scan_request *scan_req;
struct mutex mac_lock; /* lock during wmac speicific ops */
struct timer_list scan_timeout;
struct delayed_work scan_timeout;
};
struct qtnf_hw_info {
@ -143,10 +145,13 @@ struct qtnf_hw_info {
struct ieee80211_regdomain *rd;
u8 total_tx_chain;
u8 total_rx_chain;
char fw_version[ETHTOOL_FWVERS_LEN];
u32 hw_version;
};
struct qtnf_vif *qtnf_mac_get_free_vif(struct qtnf_wmac *mac);
struct qtnf_vif *qtnf_mac_get_base_vif(struct qtnf_wmac *mac);
void qtnf_mac_iface_comb_free(struct qtnf_wmac *mac);
struct wiphy *qtnf_wiphy_allocate(struct qtnf_bus *bus);
int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *priv,
const char *name, unsigned char name_assign_type);
@ -157,9 +162,13 @@ int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac);
struct qtnf_wmac *qtnf_core_get_mac(const struct qtnf_bus *bus, u8 macid);
struct net_device *qtnf_classify_skb(struct qtnf_bus *bus, struct sk_buff *skb);
void qtnf_wake_all_queues(struct net_device *ndev);
void qtnf_update_rx_stats(struct net_device *ndev, const struct sk_buff *skb);
void qtnf_update_tx_stats(struct net_device *ndev, const struct sk_buff *skb);
void qtnf_virtual_intf_cleanup(struct net_device *ndev);
void qtnf_netdev_updown(struct net_device *ndev, bool up);
void qtnf_scan_done(struct qtnf_wmac *mac, bool aborted);
static inline struct qtnf_vif *qtnf_netdev_get_priv(struct net_device *dev)
{

View File

@ -541,9 +541,9 @@ static int qtnf_event_process_skb(struct qtnf_bus *bus,
if (unlikely(!mac))
return -ENXIO;
qtnf_bus_lock(bus);
rtnl_lock();
res = qtnf_event_parse(mac, skb);
qtnf_bus_unlock(bus);
rtnl_unlock();
return res;
}

View File

@ -615,8 +615,7 @@ static void qtnf_pcie_data_tx_reclaim(struct qtnf_pcie_bus_priv *priv)
PCI_DMA_TODEVICE);
if (skb->dev) {
skb->dev->stats.tx_packets++;
skb->dev->stats.tx_bytes += skb->len;
qtnf_update_tx_stats(skb->dev, skb);
if (unlikely(priv->tx_stopped)) {
qtnf_wake_all_queues(skb->dev);
priv->tx_stopped = 0;
@ -855,9 +854,7 @@ static int qtnf_rx_poll(struct napi_struct *napi, int budget)
skb_put(skb, psize);
ndev = qtnf_classify_skb(bus, skb);
if (likely(ndev)) {
ndev->stats.rx_packets++;
ndev->stats.rx_bytes += skb->len;
qtnf_update_rx_stats(ndev, skb);
skb->protocol = eth_type_trans(skb, ndev);
napi_gro_receive(napi, skb);
} else {

View File

@ -19,7 +19,7 @@
#include <linux/ieee80211.h>
#define QLINK_PROTO_VER 10
#define QLINK_PROTO_VER 11
#define QLINK_MACID_RSVD 0xFF
#define QLINK_VIFID_RSVD 0xFF
@ -663,16 +663,6 @@ struct qlink_acl_data {
struct qlink_mac_address mac_addrs[0];
} __packed;
/**
* struct qlink_cmd_set_mac_acl - data for QLINK_CMD_SET_MAC_ACL command
*
* @acl: ACL data.
*/
struct qlink_cmd_set_mac_acl {
struct qlink_cmd chdr;
struct qlink_acl_data acl;
} __packed;
/* QLINK Command Responses messages related definitions
*/
@ -774,6 +764,9 @@ struct qlink_resp_get_hw_info {
struct qlink_resp rhdr;
__le32 fw_ver;
__le32 hw_capab;
__le32 bld_tmstamp;
__le32 plat_id;
__le32 hw_ver;
__le16 ql_proto_ver;
u8 num_mac;
u8 mac_bitmap;
@ -1084,6 +1077,13 @@ enum qlink_tlv_id {
QTN_TLV_ID_IE_SET = 0x0305,
QTN_TLV_ID_EXT_CAPABILITY_MASK = 0x0306,
QTN_TLV_ID_ACL_DATA = 0x0307,
QTN_TLV_ID_BUILD_NAME = 0x0401,
QTN_TLV_ID_BUILD_REV = 0x0402,
QTN_TLV_ID_BUILD_TYPE = 0x0403,
QTN_TLV_ID_BUILD_LABEL = 0x0404,
QTN_TLV_ID_HW_ID = 0x0405,
QTN_TLV_ID_CALIBRATION_VER = 0x0406,
QTN_TLV_ID_UBOOT_VER = 0x0407,
};
struct qlink_tlv_hdr {
@ -1092,13 +1092,20 @@ struct qlink_tlv_hdr {
u8 val[0];
} __packed;
struct qlink_iface_comb_num {
__le32 iface_comb_num;
} __packed;
struct qlink_iface_limit {
__le16 max_num;
__le16 type;
} __packed;
struct qlink_iface_comb_num {
__le16 iface_comb_num;
struct qlink_iface_limit_record {
__le16 max_interfaces;
u8 num_different_channels;
u8 n_limits;
struct qlink_iface_limit limits[0];
} __packed;
#define QLINK_RSSI_OFFSET 120

View File

@ -1172,7 +1172,7 @@ struct rtl8723bu_c2h {
u8 basic_rate:1;
u8 bt_has_reset:1;
u8 dummy4_1:1;;
u8 dummy4_1:1;
u8 ignore_wlan:1;
u8 auto_report:1;
u8 dummy4_2:3;

View File

@ -244,7 +244,8 @@ static void _rtl_init_hw_vht_capab(struct ieee80211_hw *hw,
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE ||
rtlhal->hw_type == HARDWARE_TYPE_RTL8822BE) {
u16 mcs_map;
vht_cap->vht_supported = true;
@ -697,14 +698,94 @@ static void _rtl_query_protection_mode(struct ieee80211_hw *hw,
}
}
u8 rtl_mrate_idx_to_arfr_id(struct ieee80211_hw *hw, u8 rate_index,
enum wireless_mode wirelessmode)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_phy *rtlphy = &rtlpriv->phy;
u8 ret = 0;
switch (rate_index) {
case RATR_INX_WIRELESS_NGB:
if (rtlphy->rf_type == RF_1T1R)
ret = RATEID_IDX_BGN_40M_1SS;
else
ret = RATEID_IDX_BGN_40M_2SS;
; break;
case RATR_INX_WIRELESS_N:
case RATR_INX_WIRELESS_NG:
if (rtlphy->rf_type == RF_1T1R)
ret = RATEID_IDX_GN_N1SS;
else
ret = RATEID_IDX_GN_N2SS;
; break;
case RATR_INX_WIRELESS_NB:
if (rtlphy->rf_type == RF_1T1R)
ret = RATEID_IDX_BGN_20M_1SS_BN;
else
ret = RATEID_IDX_BGN_20M_2SS_BN;
; break;
case RATR_INX_WIRELESS_GB:
ret = RATEID_IDX_BG;
break;
case RATR_INX_WIRELESS_G:
ret = RATEID_IDX_G;
break;
case RATR_INX_WIRELESS_B:
ret = RATEID_IDX_B;
break;
case RATR_INX_WIRELESS_MC:
if (wirelessmode == WIRELESS_MODE_B ||
wirelessmode == WIRELESS_MODE_G ||
wirelessmode == WIRELESS_MODE_N_24G ||
wirelessmode == WIRELESS_MODE_AC_24G)
ret = RATEID_IDX_BG;
else
ret = RATEID_IDX_G;
break;
case RATR_INX_WIRELESS_AC_5N:
if (rtlphy->rf_type == RF_1T1R)
ret = RATEID_IDX_VHT_1SS;
else
ret = RATEID_IDX_VHT_2SS;
break;
case RATR_INX_WIRELESS_AC_24N:
if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_80) {
if (rtlphy->rf_type == RF_1T1R)
ret = RATEID_IDX_VHT_1SS;
else
ret = RATEID_IDX_VHT_2SS;
} else {
if (rtlphy->rf_type == RF_1T1R)
ret = RATEID_IDX_MIX1;
else
ret = RATEID_IDX_MIX2;
}
break;
default:
ret = RATEID_IDX_BGN_40M_2SS;
break;
}
return ret;
}
EXPORT_SYMBOL(rtl_mrate_idx_to_arfr_id);
static void _rtl_txrate_selectmode(struct ieee80211_hw *hw,
struct ieee80211_sta *sta,
struct rtl_tcb_desc *tcb_desc)
{
#define SET_RATE_ID(rate_id) \
({typeof(rate_id) _id = rate_id; \
((rtlpriv->cfg->spec_ver & RTL_SPEC_NEW_RATEID) ? \
rtl_mrate_idx_to_arfr_id(hw, _id, \
(sta_entry ? sta_entry->wireless_mode : \
WIRELESS_MODE_G)) : \
_id); })
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
struct rtl_sta_info *sta_entry = NULL;
u8 ratr_index = 7;
u8 ratr_index = SET_RATE_ID(RATR_INX_WIRELESS_MC);
if (sta) {
sta_entry = (struct rtl_sta_info *) sta->drv_priv;
@ -719,7 +800,8 @@ static void _rtl_txrate_selectmode(struct ieee80211_hw *hw,
tcb_desc->hw_rate =
rtlpriv->cfg->maps[RTL_RC_CCK_RATE2M];
tcb_desc->use_driver_rate = 1;
tcb_desc->ratr_index = RATR_INX_WIRELESS_MC;
tcb_desc->ratr_index =
SET_RATE_ID(RATR_INX_WIRELESS_MC);
} else {
tcb_desc->ratr_index = ratr_index;
}
@ -735,22 +817,30 @@ static void _rtl_txrate_selectmode(struct ieee80211_hw *hw,
mac->opmode == NL80211_IFTYPE_MESH_POINT) {
tcb_desc->mac_id = 0;
if (mac->mode == WIRELESS_MODE_AC_5G)
if (sta &&
(rtlpriv->cfg->spec_ver & RTL_SPEC_NEW_RATEID))
; /* use sta_entry->ratr_index */
else if (mac->mode == WIRELESS_MODE_AC_5G)
tcb_desc->ratr_index =
RATR_INX_WIRELESS_AC_5N;
SET_RATE_ID(RATR_INX_WIRELESS_AC_5N);
else if (mac->mode == WIRELESS_MODE_AC_24G)
tcb_desc->ratr_index =
RATR_INX_WIRELESS_AC_24N;
SET_RATE_ID(RATR_INX_WIRELESS_AC_24N);
else if (mac->mode == WIRELESS_MODE_N_24G)
tcb_desc->ratr_index = RATR_INX_WIRELESS_NGB;
tcb_desc->ratr_index =
SET_RATE_ID(RATR_INX_WIRELESS_NGB);
else if (mac->mode == WIRELESS_MODE_N_5G)
tcb_desc->ratr_index = RATR_INX_WIRELESS_NG;
tcb_desc->ratr_index =
SET_RATE_ID(RATR_INX_WIRELESS_NG);
else if (mac->mode & WIRELESS_MODE_G)
tcb_desc->ratr_index = RATR_INX_WIRELESS_GB;
tcb_desc->ratr_index =
SET_RATE_ID(RATR_INX_WIRELESS_GB);
else if (mac->mode & WIRELESS_MODE_B)
tcb_desc->ratr_index = RATR_INX_WIRELESS_B;
tcb_desc->ratr_index =
SET_RATE_ID(RATR_INX_WIRELESS_B);
else if (mac->mode & WIRELESS_MODE_A)
tcb_desc->ratr_index = RATR_INX_WIRELESS_G;
tcb_desc->ratr_index =
SET_RATE_ID(RATR_INX_WIRELESS_G);
} else if (mac->opmode == NL80211_IFTYPE_AP ||
mac->opmode == NL80211_IFTYPE_ADHOC) {
@ -764,6 +854,7 @@ static void _rtl_txrate_selectmode(struct ieee80211_hw *hw,
}
}
}
#undef SET_RATE_ID
}
static void _rtl_query_bandwidth_mode(struct ieee80211_hw *hw,
@ -1140,9 +1231,19 @@ void rtl_get_tcb_desc(struct ieee80211_hw *hw,
struct ieee80211_sta *sta,
struct sk_buff *skb, struct rtl_tcb_desc *tcb_desc)
{
#define SET_RATE_ID(rate_id) \
({typeof(rate_id) _id = rate_id; \
((rtlpriv->cfg->spec_ver & RTL_SPEC_NEW_RATEID) ? \
rtl_mrate_idx_to_arfr_id(hw, _id, \
(sta_entry ? sta_entry->wireless_mode : \
WIRELESS_MODE_G)) : \
_id); })
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw));
struct ieee80211_hdr *hdr = rtl_get_hdr(skb);
struct rtl_sta_info *sta_entry =
(sta ? (struct rtl_sta_info *)sta->drv_priv : NULL);
__le16 fc = rtl_get_fc(skb);
@ -1165,7 +1266,8 @@ void rtl_get_tcb_desc(struct ieee80211_hw *hw,
if (info->control.rates[0].idx == 0 ||
ieee80211_is_nullfunc(fc)) {
tcb_desc->use_driver_rate = true;
tcb_desc->ratr_index = RATR_INX_WIRELESS_MC;
tcb_desc->ratr_index =
SET_RATE_ID(RATR_INX_WIRELESS_MC);
tcb_desc->disable_ratefallback = 1;
} else {
@ -1207,11 +1309,12 @@ void rtl_get_tcb_desc(struct ieee80211_hw *hw,
_rtl_query_protection_mode(hw, tcb_desc, info);
} else {
tcb_desc->use_driver_rate = true;
tcb_desc->ratr_index = RATR_INX_WIRELESS_MC;
tcb_desc->ratr_index = SET_RATE_ID(RATR_INX_WIRELESS_MC);
tcb_desc->disable_ratefallback = 1;
tcb_desc->mac_id = 0;
tcb_desc->packet_bw = false;
}
#undef SET_RATE_ID
}
EXPORT_SYMBOL(rtl_get_tcb_desc);

View File

@ -162,6 +162,8 @@ void rtl_c2hcmd_wq_callback(void *data);
void rtl_c2hcmd_launcher(struct ieee80211_hw *hw, int exec);
void rtl_c2hcmd_enqueue(struct ieee80211_hw *hw, u8 tag, u8 len, u8 *val);
u8 rtl_mrate_idx_to_arfr_id(struct ieee80211_hw *hw, u8 rate_index,
enum wireless_mode wirelessmode);
void rtl_get_tcb_desc(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,

View File

@ -29,7 +29,7 @@
* Debug related function
***************************************************/
const char *const gl_btc_wifi_bw_string[] = {
static const char *const gl_btc_wifi_bw_string[] = {
"11bg",
"HT20",
"HT40",
@ -37,7 +37,7 @@ const char *const gl_btc_wifi_bw_string[] = {
"HT160"
};
const char *const gl_btc_wifi_freq_string[] = {
static const char *const gl_btc_wifi_freq_string[] = {
"2.4G",
"5G"
};
@ -95,21 +95,6 @@ static bool is_any_client_connect_to_ap(struct btc_coexist *btcoexist)
return false;
}
static bool halbtc_is_bt40(struct rtl_priv *adapter)
{
struct rtl_priv *rtlpriv = adapter;
struct rtl_phy *rtlphy = &(rtlpriv->phy);
bool is_ht40 = true;
enum ht_channel_width bw = rtlphy->current_chan_bw;
if (bw == HT_CHANNEL_WIDTH_20)
is_ht40 = false;
else if (bw == HT_CHANNEL_WIDTH_20_40)
is_ht40 = true;
return is_ht40;
}
static bool halbtc_legacy(struct rtl_priv *adapter)
{
struct rtl_priv *rtlpriv = adapter;
@ -135,18 +120,26 @@ bool halbtc_is_wifi_uplink(struct rtl_priv *adapter)
static u32 halbtc_get_wifi_bw(struct btc_coexist *btcoexist)
{
struct rtl_priv *rtlpriv =
(struct rtl_priv *)btcoexist->adapter;
struct rtl_priv *rtlpriv = btcoexist->adapter;
struct rtl_phy *rtlphy = &rtlpriv->phy;
u32 wifi_bw = BTC_WIFI_BW_HT20;
if (halbtc_is_bt40(rtlpriv)) {
wifi_bw = BTC_WIFI_BW_HT40;
if (halbtc_legacy(rtlpriv)) {
wifi_bw = BTC_WIFI_BW_LEGACY;
} else {
if (halbtc_legacy(rtlpriv))
wifi_bw = BTC_WIFI_BW_LEGACY;
else
switch (rtlphy->current_chan_bw) {
case HT_CHANNEL_WIDTH_20:
wifi_bw = BTC_WIFI_BW_HT20;
break;
case HT_CHANNEL_WIDTH_20_40:
wifi_bw = BTC_WIFI_BW_HT40;
break;
case HT_CHANNEL_WIDTH_80:
wifi_bw = BTC_WIFI_BW_HT80;
break;
}
}
return wifi_bw;
}
@ -163,7 +156,7 @@ static u8 halbtc_get_wifi_central_chnl(struct btc_coexist *btcoexist)
return chnl;
}
u8 rtl_get_hwpg_single_ant_path(struct rtl_priv *rtlpriv)
static u8 rtl_get_hwpg_single_ant_path(struct rtl_priv *rtlpriv)
{
struct rtl_mod_params *mod_params = rtlpriv->cfg->mod_params;
@ -178,12 +171,12 @@ u8 rtl_get_hwpg_single_ant_path(struct rtl_priv *rtlpriv)
return rtlpriv->btcoexist.btc_info.single_ant_path;
}
u8 rtl_get_hwpg_bt_type(struct rtl_priv *rtlpriv)
static u8 rtl_get_hwpg_bt_type(struct rtl_priv *rtlpriv)
{
return rtlpriv->btcoexist.btc_info.bt_type;
}
u8 rtl_get_hwpg_ant_num(struct rtl_priv *rtlpriv)
static u8 rtl_get_hwpg_ant_num(struct rtl_priv *rtlpriv)
{
struct rtl_mod_params *mod_params = rtlpriv->cfg->mod_params;
u8 num;
@ -200,7 +193,7 @@ u8 rtl_get_hwpg_ant_num(struct rtl_priv *rtlpriv)
return num;
}
u8 rtl_get_hwpg_package_type(struct rtl_priv *rtlpriv)
static u8 rtl_get_hwpg_package_type(struct rtl_priv *rtlpriv)
{
struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
@ -511,7 +504,7 @@ static u32 halbtc_get_bt_forbidden_slot_val(void *btc_context)
return btcoexist->bt_info.bt_forb_slot_val;
}
u32 halbtc_get_wifi_link_status(struct btc_coexist *btcoexist)
static u32 halbtc_get_wifi_link_status(struct btc_coexist *btcoexist)
{
/* return value:
* [31:16] => connected port number
@ -987,7 +980,8 @@ static void halbtc_write_4byte(void *bt_context, u32 reg_addr, u32 data)
rtl_write_dword(rtlpriv, reg_addr, data);
}
void halbtc_write_local_reg_1byte(void *btc_context, u32 reg_addr, u8 data)
static void halbtc_write_local_reg_1byte(void *btc_context, u32 reg_addr,
u8 data)
{
struct btc_coexist *btcoexist = (struct btc_coexist *)btc_context;
struct rtl_priv *rtlpriv = btcoexist->adapter;
@ -1000,22 +994,6 @@ void halbtc_write_local_reg_1byte(void *btc_context, u32 reg_addr, u8 data)
rtl_write_byte(rtlpriv, reg_addr, data);
}
void halbtc_set_macreg(void *btc_context, u32 reg_addr, u32 bit_mask, u32 data)
{
struct btc_coexist *btcoexist = (struct btc_coexist *)btc_context;
struct rtl_priv *rtlpriv = btcoexist->adapter;
rtl_set_bbreg(rtlpriv->mac80211.hw, reg_addr, bit_mask, data);
}
u32 halbtc_get_macreg(void *btc_context, u32 reg_addr, u32 bit_mask)
{
struct btc_coexist *btcoexist = (struct btc_coexist *)btc_context;
struct rtl_priv *rtlpriv = btcoexist->adapter;
return rtl_get_bbreg(rtlpriv->mac80211.hw, reg_addr, bit_mask);
}
static void halbtc_set_bbreg(void *bt_context, u32 reg_addr, u32 bit_mask,
u32 data)
{
@ -1061,6 +1039,7 @@ static void halbtc_fill_h2c_cmd(void *bt_context, u8 element_id,
cmd_len, cmd_buf);
}
static
void halbtc_set_bt_reg(void *btc_context, u8 reg_type, u32 offset, u32 set_val)
{
struct btc_coexist *btcoexist = (struct btc_coexist *)btc_context;
@ -1100,7 +1079,7 @@ static void halbtc_display_dbg_msg(void *bt_context, u8 disp_type,
}
}
bool halbtc_under_ips(struct btc_coexist *btcoexist)
static bool halbtc_under_ips(struct btc_coexist *btcoexist)
{
struct rtl_priv *rtlpriv = btcoexist->adapter;
struct rtl_ps_ctl *ppsc = rtl_psc(rtlpriv);
@ -1254,6 +1233,40 @@ bool exhalbtc_initlize_variables(struct rtl_priv *rtlpriv)
return true;
}
bool exhalbtc_initlize_variables_wifi_only(struct rtl_priv *rtlpriv)
{
struct wifi_only_cfg *wifionly_cfg = rtl_btc_wifi_only(rtlpriv);
struct wifi_only_haldata *wifionly_haldata;
if (!wifionly_cfg)
return false;
wifionly_cfg->adapter = rtlpriv;
switch (rtlpriv->rtlhal.interface) {
case INTF_PCI:
wifionly_cfg->chip_interface = BTC_INTF_PCI;
break;
case INTF_USB:
wifionly_cfg->chip_interface = BTC_INTF_USB;
break;
default:
wifionly_cfg->chip_interface = BTC_INTF_UNKNOWN;
break;
}
wifionly_haldata = &wifionly_cfg->haldata_info;
wifionly_haldata->customer_id = CUSTOMER_NORMAL;
wifionly_haldata->efuse_pg_antnum = rtl_get_hwpg_ant_num(rtlpriv);
wifionly_haldata->efuse_pg_antpath =
rtl_get_hwpg_single_ant_path(rtlpriv);
wifionly_haldata->rfe_type = rtl_get_hwpg_rfe_type(rtlpriv);
wifionly_haldata->ant_div_cfg = 0;
return true;
}
bool exhalbtc_bind_bt_coex_withadapter(void *adapter)
{
struct rtl_priv *rtlpriv = adapter;
@ -1317,6 +1330,7 @@ bool exhalbtc_bind_bt_coex_withadapter(void *adapter)
"[BTCoex], Package Type = Non-TFBGA\n");
btcoexist->board_info.rfe_type = rtl_get_hwpg_rfe_type(rtlpriv);
btcoexist->board_info.ant_div_cfg = 0;
return true;
}
@ -1374,6 +1388,10 @@ void exhalbtc_init_hw_config(struct btc_coexist *btcoexist, bool wifi_only)
}
}
void exhalbtc_init_hw_config_wifi_only(struct wifi_only_cfg *wifionly_cfg)
{
}
void exhalbtc_init_coex_dm(struct btc_coexist *btcoexist)
{
if (!halbtc_is_bt_coexist_available(btcoexist))
@ -1500,6 +1518,11 @@ void exhalbtc_scan_notify(struct btc_coexist *btcoexist, u8 type)
halbtc_normal_low_power(btcoexist);
}
void exhalbtc_scan_notify_wifi_only(struct wifi_only_cfg *wifionly_cfg,
u8 is_5g)
{
}
void exhalbtc_connect_notify(struct btc_coexist *btcoexist, u8 action)
{
u8 asso_type;
@ -1917,3 +1940,21 @@ void exhalbtc_display_bt_coex_info(struct btc_coexist *btcoexist,
halbtc_normal_low_power(btcoexist);
}
void exhalbtc_switch_band_notify(struct btc_coexist *btcoexist, u8 type)
{
if (!halbtc_is_bt_coexist_available(btcoexist))
return;
if (btcoexist->manual_control)
return;
halbtc_leave_low_power(btcoexist);
halbtc_normal_low_power(btcoexist);
}
void exhalbtc_switch_band_notify_wifi_only(struct wifi_only_cfg *wifionly_cfg,
u8 is_5g)
{
}

View File

@ -153,6 +153,7 @@ struct btc_board_info {
bool tfbga_package;
u8 rfe_type;
u8 ant_div_cfg;
};
enum btc_dbg_opcode {
@ -190,6 +191,7 @@ enum btc_wifi_bw_mode {
BTC_WIFI_BW_LEGACY = 0x0,
BTC_WIFI_BW_HT20 = 0x1,
BTC_WIFI_BW_HT40 = 0x2,
BTC_WIFI_BW_HT80 = 0x3,
BTC_WIFI_BW_MAX
};
@ -384,6 +386,14 @@ enum btc_notify_type_scan {
BTC_SCAN_MAX
};
enum btc_notify_type_switchband {
BTC_NOT_SWITCH = 0x0,
BTC_SWITCH_TO_24G = 0x1,
BTC_SWITCH_TO_5G = 0x2,
BTC_SWITCH_TO_24G_NOFORSCAN = 0x3,
BTC_SWITCH_MAX
};
enum btc_notify_type_associate {
BTC_ASSOCIATE_FINISH = 0x0,
BTC_ASSOCIATE_START = 0x1,
@ -573,6 +583,8 @@ struct btc_coexist {
*/
bool auto_report_1ant;
bool auto_report_2ant;
bool dbg_mode_1ant;
bool dbg_mode_2ant;
bool initilized;
bool stop_coex_dm;
bool manual_control;
@ -626,21 +638,31 @@ bool halbtc_is_wifi_uplink(struct rtl_priv *adapter);
#define rtl_btc_coexist(rtlpriv) \
((struct btc_coexist *)((rtlpriv)->btcoexist.btc_context))
#define rtl_btc_wifi_only(rtlpriv) \
((struct wifi_only_cfg *)((rtlpriv)->btcoexist.wifi_only_context))
struct wifi_only_cfg;
bool exhalbtc_initlize_variables(struct rtl_priv *rtlpriv);
bool exhalbtc_initlize_variables_wifi_only(struct rtl_priv *rtlpriv);
bool exhalbtc_bind_bt_coex_withadapter(void *adapter);
void exhalbtc_power_on_setting(struct btc_coexist *btcoexist);
void exhalbtc_pre_load_firmware(struct btc_coexist *btcoexist);
void exhalbtc_init_hw_config(struct btc_coexist *btcoexist, bool wifi_only);
void exhalbtc_init_hw_config_wifi_only(struct wifi_only_cfg *wifionly_cfg);
void exhalbtc_init_coex_dm(struct btc_coexist *btcoexist);
void exhalbtc_ips_notify(struct btc_coexist *btcoexist, u8 type);
void exhalbtc_lps_notify(struct btc_coexist *btcoexist, u8 type);
void exhalbtc_scan_notify(struct btc_coexist *btcoexist, u8 type);
void exhalbtc_scan_notify_wifi_only(struct wifi_only_cfg *wifionly_cfg,
u8 is_5g);
void exhalbtc_connect_notify(struct btc_coexist *btcoexist, u8 action);
void exhalbtc_mediastatus_notify(struct btc_coexist *btcoexist,
enum rt_media_status media_status);
void exhalbtc_special_packet_notify(struct btc_coexist *btcoexist, u8 pkt_type);
void exhalbtc_bt_info_notify(struct btc_coexist *btcoexist, u8 *tmp_buf,
u8 length);
void exhalbtc_rf_status_notify(struct btc_coexist *btcoexist, u8 type);
void exhalbtc_stack_operation_notify(struct btc_coexist *btcoexist, u8 type);
void exhalbtc_halt_notify(struct btc_coexist *btcoexist);
void exhalbtc_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state);
@ -648,6 +670,8 @@ void exhalbtc_coex_dm_switch(struct btc_coexist *btcoexist);
void exhalbtc_periodical(struct btc_coexist *btcoexist);
void exhalbtc_dbg_control(struct btc_coexist *btcoexist, u8 code, u8 len,
u8 *data);
void exhalbtc_antenna_detection(struct btc_coexist *btcoexist, u32 cent_freq,
u32 offset, u32 span, u32 seconds);
void exhalbtc_stack_update_profile_info(void);
void exhalbtc_set_hci_version(struct btc_coexist *btcoexist, u16 hci_version);
void exhalbtc_set_bt_patch_version(struct btc_coexist *btcoexist,
@ -658,6 +682,9 @@ void exhalbtc_set_chip_type(struct btc_coexist *btcoexist, u8 chip_type);
void exhalbtc_set_ant_num(struct rtl_priv *rtlpriv, u8 type, u8 ant_num);
void exhalbtc_display_bt_coex_info(struct btc_coexist *btcoexist,
struct seq_file *m);
void exhalbtc_switch_band_notify(struct btc_coexist *btcoexist, u8 type);
void exhalbtc_switch_band_notify_wifi_only(struct wifi_only_cfg *wifionly_cfg,
u8 is_5g);
void exhalbtc_signal_compensation(struct btc_coexist *btcoexist,
u8 *rssi_wifi, u8 *rssi_bt);
void exhalbtc_lps_leave(struct btc_coexist *btcoexist);
@ -665,4 +692,41 @@ void exhalbtc_low_wifi_traffic_notify(struct btc_coexist *btcoexist);
void exhalbtc_set_single_ant_path(struct btc_coexist *btcoexist,
u8 single_ant_path);
/* The following are used by wifi_only case */
enum wifionly_chip_interface {
WIFIONLY_INTF_UNKNOWN = 0,
WIFIONLY_INTF_PCI = 1,
WIFIONLY_INTF_USB = 2,
WIFIONLY_INTF_SDIO = 3,
WIFIONLY_INTF_MAX
};
enum wifionly_customer_id {
CUSTOMER_NORMAL = 0,
CUSTOMER_HP_1 = 1,
};
struct wifi_only_haldata {
u16 customer_id;
u8 efuse_pg_antnum;
u8 efuse_pg_antpath;
u8 rfe_type;
u8 ant_div_cfg;
};
struct wifi_only_cfg {
void *adapter;
struct wifi_only_haldata haldata_info;
enum wifionly_chip_interface chip_interface;
};
static inline
void halwifionly_phy_set_bb_reg(struct wifi_only_cfg *wifi_conly_cfg,
u32 regaddr, u32 bitmask, u32 data)
{
struct rtl_priv *rtlpriv = (struct rtl_priv *)wifi_conly_cfg->adapter;
rtl_set_bbreg(rtlpriv->hw, regaddr, bitmask, data);
}
#endif

View File

@ -31,13 +31,16 @@
static struct rtl_btc_ops rtl_btc_operation = {
.btc_init_variables = rtl_btc_init_variables,
.btc_init_variables_wifi_only = rtl_btc_init_variables_wifi_only,
.btc_deinit_variables = rtl_btc_deinit_variables,
.btc_init_hal_vars = rtl_btc_init_hal_vars,
.btc_power_on_setting = rtl_btc_power_on_setting,
.btc_init_hw_config = rtl_btc_init_hw_config,
.btc_init_hw_config_wifi_only = rtl_btc_init_hw_config_wifi_only,
.btc_ips_notify = rtl_btc_ips_notify,
.btc_lps_notify = rtl_btc_lps_notify,
.btc_scan_notify = rtl_btc_scan_notify,
.btc_scan_notify_wifi_only = rtl_btc_scan_notify_wifi_only,
.btc_connect_notify = rtl_btc_connect_notify,
.btc_mediastatus_notify = rtl_btc_mediastatus_notify,
.btc_periodical = rtl_btc_periodical,
@ -48,6 +51,8 @@ static struct rtl_btc_ops rtl_btc_operation = {
.btc_is_disable_edca_turbo = rtl_btc_is_disable_edca_turbo,
.btc_is_bt_disabled = rtl_btc_is_bt_disabled,
.btc_special_packet_notify = rtl_btc_special_packet_notify,
.btc_switch_band_notify = rtl_btc_switch_band_notify,
.btc_switch_band_notify_wifi_only = rtl_btc_switch_band_notify_wifionly,
.btc_record_pwr_mode = rtl_btc_record_pwr_mode,
.btc_get_lps_val = rtl_btc_get_lps_val,
.btc_get_rpwm_val = rtl_btc_get_rpwm_val,
@ -146,7 +151,11 @@ void rtl_btc_get_ampdu_cfg(struct rtl_priv *rtlpriv, u8 *reject_agg,
static void rtl_btc_alloc_variable(struct rtl_priv *rtlpriv, bool wifi_only)
{
rtlpriv->btcoexist.btc_context =
if (wifi_only)
rtlpriv->btcoexist.wifi_only_context =
kzalloc(sizeof(struct wifi_only_cfg), GFP_KERNEL);
else
rtlpriv->btcoexist.btc_context =
kzalloc(sizeof(struct btc_coexist), GFP_KERNEL);
}
@ -154,6 +163,9 @@ static void rtl_btc_free_variable(struct rtl_priv *rtlpriv)
{
kfree(rtlpriv->btcoexist.btc_context);
rtlpriv->btcoexist.btc_context = NULL;
kfree(rtlpriv->btcoexist.wifi_only_context);
rtlpriv->btcoexist.wifi_only_context = NULL;
}
void rtl_btc_init_variables(struct rtl_priv *rtlpriv)
@ -164,6 +176,13 @@ void rtl_btc_init_variables(struct rtl_priv *rtlpriv)
exhalbtc_bind_bt_coex_withadapter(rtlpriv);
}
void rtl_btc_init_variables_wifi_only(struct rtl_priv *rtlpriv)
{
rtl_btc_alloc_variable(rtlpriv, true);
exhalbtc_initlize_variables_wifi_only(rtlpriv);
}
void rtl_btc_deinit_variables(struct rtl_priv *rtlpriv)
{
rtl_btc_free_variable(rtlpriv);
@ -203,6 +222,16 @@ void rtl_btc_init_hw_config(struct rtl_priv *rtlpriv)
exhalbtc_init_coex_dm(btcoexist);
}
void rtl_btc_init_hw_config_wifi_only(struct rtl_priv *rtlpriv)
{
struct wifi_only_cfg *wifionly_cfg = rtl_btc_wifi_only(rtlpriv);
if (!wifionly_cfg)
return;
exhalbtc_init_hw_config_wifi_only(wifionly_cfg);
}
void rtl_btc_ips_notify(struct rtl_priv *rtlpriv, u8 type)
{
struct btc_coexist *btcoexist = rtl_btc_coexist(rtlpriv);
@ -211,6 +240,14 @@ void rtl_btc_ips_notify(struct rtl_priv *rtlpriv, u8 type)
return;
exhalbtc_ips_notify(btcoexist, type);
if (type == ERFON) {
/* In some situation, it doesn't scan after leaving IPS, and
* this will cause btcoex in wrong state.
*/
exhalbtc_scan_notify(btcoexist, 1);
exhalbtc_scan_notify(btcoexist, 0);
}
}
void rtl_btc_lps_notify(struct rtl_priv *rtlpriv, u8 type)
@ -233,6 +270,18 @@ void rtl_btc_scan_notify(struct rtl_priv *rtlpriv, u8 scantype)
exhalbtc_scan_notify(btcoexist, scantype);
}
void rtl_btc_scan_notify_wifi_only(struct rtl_priv *rtlpriv, u8 scantype)
{
struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
struct wifi_only_cfg *wifionly_cfg = rtl_btc_wifi_only(rtlpriv);
u8 is_5g = (rtlhal->current_bandtype == BAND_ON_5G);
if (!wifionly_cfg)
return;
exhalbtc_scan_notify_wifi_only(wifionly_cfg, is_5g);
}
void rtl_btc_connect_notify(struct rtl_priv *rtlpriv, u8 action)
{
struct btc_coexist *btcoexist = rtl_btc_coexist(rtlpriv);
@ -423,6 +472,44 @@ void rtl_btc_special_packet_notify(struct rtl_priv *rtlpriv, u8 pkt_type)
return exhalbtc_special_packet_notify(btcoexist, pkt_type);
}
void rtl_btc_switch_band_notify(struct rtl_priv *rtlpriv, u8 band_type,
bool scanning)
{
struct btc_coexist *btcoexist = rtl_btc_coexist(rtlpriv);
u8 type = BTC_NOT_SWITCH;
if (!btcoexist)
return;
switch (band_type) {
case BAND_ON_2_4G:
if (scanning)
type = BTC_SWITCH_TO_24G;
else
type = BTC_SWITCH_TO_24G_NOFORSCAN;
break;
case BAND_ON_5G:
type = BTC_SWITCH_TO_5G;
break;
}
if (type != BTC_NOT_SWITCH)
exhalbtc_switch_band_notify(btcoexist, type);
}
void rtl_btc_switch_band_notify_wifionly(struct rtl_priv *rtlpriv, u8 band_type,
bool scanning)
{
struct wifi_only_cfg *wifionly_cfg = rtl_btc_wifi_only(rtlpriv);
u8 is_5g = (band_type == BAND_ON_5G);
if (!wifionly_cfg)
return;
exhalbtc_switch_band_notify_wifi_only(wifionly_cfg, is_5g);
}
struct rtl_btc_ops *rtl_btc_get_ops_pointer(void)
{
return &rtl_btc_operation;

View File

@ -28,13 +28,16 @@
#include "halbt_precomp.h"
void rtl_btc_init_variables(struct rtl_priv *rtlpriv);
void rtl_btc_init_variables_wifi_only(struct rtl_priv *rtlpriv);
void rtl_btc_deinit_variables(struct rtl_priv *rtlpriv);
void rtl_btc_init_hal_vars(struct rtl_priv *rtlpriv);
void rtl_btc_power_on_setting(struct rtl_priv *rtlpriv);
void rtl_btc_init_hw_config(struct rtl_priv *rtlpriv);
void rtl_btc_init_hw_config_wifi_only(struct rtl_priv *rtlpriv);
void rtl_btc_ips_notify(struct rtl_priv *rtlpriv, u8 type);
void rtl_btc_lps_notify(struct rtl_priv *rtlpriv, u8 type);
void rtl_btc_scan_notify(struct rtl_priv *rtlpriv, u8 scantype);
void rtl_btc_scan_notify_wifi_only(struct rtl_priv *rtlpriv, u8 scantype);
void rtl_btc_connect_notify(struct rtl_priv *rtlpriv, u8 action);
void rtl_btc_mediastatus_notify(struct rtl_priv *rtlpriv,
enum rt_media_status mstatus);
@ -46,6 +49,10 @@ bool rtl_btc_is_limited_dig(struct rtl_priv *rtlpriv);
bool rtl_btc_is_disable_edca_turbo(struct rtl_priv *rtlpriv);
bool rtl_btc_is_bt_disabled(struct rtl_priv *rtlpriv);
void rtl_btc_special_packet_notify(struct rtl_priv *rtlpriv, u8 pkt_type);
void rtl_btc_switch_band_notify(struct rtl_priv *rtlpriv, u8 band_type,
bool scanning);
void rtl_btc_switch_band_notify_wifionly(struct rtl_priv *rtlpriv, u8 band_type,
bool scanning);
void rtl_btc_display_bt_coex_info(struct rtl_priv *rtlpriv, struct seq_file *m);
void rtl_btc_record_pwr_mode(struct rtl_priv *rtlpriv, u8 *buf, u8 len);
u8 rtl_btc_get_lps_val(struct rtl_priv *rtlpriv);

View File

@ -1453,6 +1453,9 @@ static void rtl_op_sw_scan_start(struct ieee80211_hw *hw,
if (rtlpriv->cfg->ops->get_btc_status())
rtlpriv->btcoexist.btc_ops->btc_scan_notify(rtlpriv, 1);
else if (rtlpriv->btcoexist.btc_ops)
rtlpriv->btcoexist.btc_ops->btc_scan_notify_wifi_only(rtlpriv,
1);
if (rtlpriv->dm.supp_phymode_switch) {
if (rtlpriv->cfg->ops->chk_switch_dmdp)
@ -1508,6 +1511,9 @@ static void rtl_op_sw_scan_complete(struct ieee80211_hw *hw,
rtlpriv->cfg->ops->scan_operation_backup(hw, SCAN_OPT_RESTORE);
if (rtlpriv->cfg->ops->get_btc_status())
rtlpriv->btcoexist.btc_ops->btc_scan_notify(rtlpriv, 0);
else if (rtlpriv->btcoexist.btc_ops)
rtlpriv->btcoexist.btc_ops->btc_scan_notify_wifi_only(rtlpriv,
0);
}
static int rtl_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,

View File

@ -1794,6 +1794,7 @@ static int rtl_pci_start(struct ieee80211_hw *hw)
struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw));
struct rtl_btc_ops *btc_ops = rtlpriv->btcoexist.btc_ops;
int err;
@ -1803,9 +1804,12 @@ static int rtl_pci_start(struct ieee80211_hw *hw)
if (rtlpriv->cfg->ops->get_btc_status &&
rtlpriv->cfg->ops->get_btc_status()) {
rtlpriv->btcoexist.btc_info.ap_num = 36;
rtlpriv->btcoexist.btc_ops->btc_init_variables(rtlpriv);
rtlpriv->btcoexist.btc_ops->btc_init_hal_vars(rtlpriv);
btc_ops->btc_init_variables(rtlpriv);
btc_ops->btc_init_hal_vars(rtlpriv);
} else if (btc_ops) {
btc_ops->btc_init_variables_wifi_only(rtlpriv);
}
err = rtlpriv->cfg->ops->hw_init(hw);
if (err) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,

View File

@ -873,6 +873,24 @@ enum ratr_table_mode {
RATR_INX_WIRELESS_AC_24N = 9,
};
enum ratr_table_mode_new {
RATEID_IDX_BGN_40M_2SS = 0,
RATEID_IDX_BGN_40M_1SS = 1,
RATEID_IDX_BGN_20M_2SS_BN = 2,
RATEID_IDX_BGN_20M_1SS_BN = 3,
RATEID_IDX_GN_N2SS = 4,
RATEID_IDX_GN_N1SS = 5,
RATEID_IDX_BG = 6,
RATEID_IDX_G = 7,
RATEID_IDX_B = 8,
RATEID_IDX_VHT_2SS = 9,
RATEID_IDX_VHT_1SS = 10,
RATEID_IDX_MIX1 = 11,
RATEID_IDX_MIX2 = 12,
RATEID_IDX_VHT_3SS = 13,
RATEID_IDX_BGN_3SS = 14,
};
enum rtl_link_state {
MAC80211_NOLINK = 0,
MAC80211_LINKING = 1,
@ -931,6 +949,10 @@ enum package_type {
PACKAGE_TFBGA79
};
enum rtl_spec_ver {
RTL_SPEC_NEW_RATEID = BIT(0), /* use ratr_table_mode_new */
};
struct octet_string {
u8 *octet;
u16 length;
@ -2315,6 +2337,7 @@ struct rtl_hal_cfg {
struct rtl_hal_ops *ops;
struct rtl_mod_params *mod_params;
struct rtl_hal_usbint_cfg *usb_interface_cfg;
enum rtl_spec_ver spec_ver;
/*this map used for some registers or vars
defined int HAL but used in MAIN */
@ -2502,6 +2525,7 @@ struct bt_coexist_info {
struct rtl_btc_info btc_info;
/* btc context */
void *btc_context;
void *wifi_only_context;
/* EEPROM BT info. */
u8 eeprom_bt_coexist;
u8 eeprom_bt_type;
@ -2558,13 +2582,17 @@ struct bt_coexist_info {
struct rtl_btc_ops {
void (*btc_init_variables) (struct rtl_priv *rtlpriv);
void (*btc_init_variables_wifi_only)(struct rtl_priv *rtlpriv);
void (*btc_deinit_variables)(struct rtl_priv *rtlpriv);
void (*btc_init_hal_vars) (struct rtl_priv *rtlpriv);
void (*btc_power_on_setting)(struct rtl_priv *rtlpriv);
void (*btc_init_hw_config) (struct rtl_priv *rtlpriv);
void (*btc_init_hw_config_wifi_only)(struct rtl_priv *rtlpriv);
void (*btc_ips_notify) (struct rtl_priv *rtlpriv, u8 type);
void (*btc_lps_notify)(struct rtl_priv *rtlpriv, u8 type);
void (*btc_scan_notify) (struct rtl_priv *rtlpriv, u8 scantype);
void (*btc_scan_notify_wifi_only)(struct rtl_priv *rtlpriv,
u8 scantype);
void (*btc_connect_notify) (struct rtl_priv *rtlpriv, u8 action);
void (*btc_mediastatus_notify) (struct rtl_priv *rtlpriv,
enum rt_media_status mstatus);
@ -2579,6 +2607,10 @@ struct rtl_btc_ops {
bool (*btc_is_bt_disabled) (struct rtl_priv *rtlpriv);
void (*btc_special_packet_notify)(struct rtl_priv *rtlpriv,
u8 pkt_type);
void (*btc_switch_band_notify)(struct rtl_priv *rtlpriv, u8 type,
bool scanning);
void (*btc_switch_band_notify_wifi_only)(struct rtl_priv *rtlpriv,
u8 type, bool scanning);
void (*btc_display_bt_coex_info)(struct rtl_priv *rtlpriv,
struct seq_file *m);
void (*btc_record_pwr_mode)(struct rtl_priv *rtlpriv, u8 *buf, u8 len);