diff options
Diffstat (limited to 'drivers/net/wireless/ath/carl9170')
-rw-r--r-- | drivers/net/wireless/ath/carl9170/carl9170.h | 10 | ||||
-rw-r--r-- | drivers/net/wireless/ath/carl9170/cmd.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath/carl9170/debug.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/carl9170/fwdesc.h | 3 | ||||
-rw-r--r-- | drivers/net/wireless/ath/carl9170/hw.h | 41 | ||||
-rw-r--r-- | drivers/net/wireless/ath/carl9170/led.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/carl9170/mac.c | 129 | ||||
-rw-r--r-- | drivers/net/wireless/ath/carl9170/main.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/carl9170/phy.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/ath/carl9170/tx.c | 290 |
10 files changed, 277 insertions, 212 deletions
diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index f9a4655ea0b8..c5427a72a1e2 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -177,7 +177,7 @@ struct carl9170_tx_queue_stats { struct carl9170_vif { unsigned int id; - struct ieee80211_vif *vif; + struct ieee80211_vif __rcu *vif; }; struct carl9170_vif_info { @@ -311,7 +311,7 @@ struct ar9170 { spinlock_t beacon_lock; unsigned int global_pretbtt; unsigned int global_beacon_int; - struct carl9170_vif_info *beacon_iter; + struct carl9170_vif_info __rcu *beacon_iter; unsigned int beacon_enabled; /* cryptographic engine */ @@ -389,7 +389,7 @@ struct ar9170 { /* tx ampdu */ struct work_struct ampdu_work; spinlock_t tx_ampdu_list_lock; - struct carl9170_sta_tid *tx_ampdu_iter; + struct carl9170_sta_tid __rcu *tx_ampdu_iter; struct list_head tx_ampdu_list; atomic_t tx_ampdu_upload; atomic_t tx_ampdu_scheduler; @@ -456,7 +456,7 @@ struct carl9170_sta_info { bool sleeping; atomic_t pending_frames; unsigned int ampdu_max_len; - struct carl9170_sta_tid *agg[CARL9170_NUM_TID]; + struct carl9170_sta_tid __rcu *agg[CARL9170_NUM_TID]; struct carl9170_ba_stats stats[CARL9170_NUM_TID]; }; @@ -532,7 +532,6 @@ int carl9170_set_ampdu_settings(struct ar9170 *ar); int carl9170_set_slot_time(struct ar9170 *ar); int carl9170_set_mac_rates(struct ar9170 *ar); int carl9170_set_hwretry_limit(struct ar9170 *ar, const u32 max_retry); -int carl9170_update_beacon(struct ar9170 *ar, const bool submit); int carl9170_upload_key(struct ar9170 *ar, const u8 id, const u8 *mac, const u8 ktype, const u8 keyidx, const u8 *keydata, const int keylen); int carl9170_disable_key(struct ar9170 *ar, const u8 id); @@ -553,6 +552,7 @@ void carl9170_tx_drop(struct ar9170 *ar, struct sk_buff *skb); void carl9170_tx_scheduler(struct ar9170 *ar); void carl9170_tx_get_skb(struct sk_buff *skb); int carl9170_tx_put_skb(struct sk_buff *skb); +int carl9170_update_beacon(struct ar9170 *ar, const bool submit); /* LEDs */ #ifdef CONFIG_CARL9170_LEDS diff --git a/drivers/net/wireless/ath/carl9170/cmd.h b/drivers/net/wireless/ath/carl9170/cmd.h index 568174c71b94..d5f95bdc75c1 100644 --- a/drivers/net/wireless/ath/carl9170/cmd.h +++ b/drivers/net/wireless/ath/carl9170/cmd.h @@ -87,7 +87,7 @@ do { \ __ar->cmd_buf[2 * __nreg + 1] = cpu_to_le32(r); \ __ar->cmd_buf[2 * __nreg + 2] = cpu_to_le32(v); \ __nreg++; \ - if ((__nreg >= PAYLOAD_MAX/2)) { \ + if ((__nreg >= PAYLOAD_MAX / 2)) { \ if (IS_ACCEPTING_CMD(__ar)) \ __err = carl9170_exec_cmd(__ar, \ CARL9170_CMD_WREG, 8 * __nreg, \ @@ -160,7 +160,7 @@ do { \ } while (0) #define carl9170_async_regwrite_finish() do { \ -__async_regwrite_out : \ +__async_regwrite_out: \ if (__cmd != NULL && __err == 0) \ carl9170_async_regwrite_flush(); \ kfree(__cmd); \ diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c index 0ac1124c2a0b..de57f90e1d5f 100644 --- a/drivers/net/wireless/ath/carl9170/debug.c +++ b/drivers/net/wireless/ath/carl9170/debug.c @@ -695,7 +695,7 @@ static char *carl9170_debugfs_bug_read(struct ar9170 *ar, char *buf, } __DEBUGFS_DECLARE_RW_FILE(bug, 400, CARL9170_STOPPED); -static const char *erp_modes[] = { +static const char *const erp_modes[] = { [CARL9170_ERP_INVALID] = "INVALID", [CARL9170_ERP_AUTO] = "Automatic", [CARL9170_ERP_MAC80211] = "Set by MAC80211", diff --git a/drivers/net/wireless/ath/carl9170/fwdesc.h b/drivers/net/wireless/ath/carl9170/fwdesc.h index 7ba62bb77054..6d9c0891ce7f 100644 --- a/drivers/net/wireless/ath/carl9170/fwdesc.h +++ b/drivers/net/wireless/ath/carl9170/fwdesc.h @@ -75,6 +75,9 @@ enum carl9170fw_feature_list { /* Firmware supports PSM in the 5GHZ Band */ CARL9170FW_FIXED_5GHZ_PSM, + /* HW (ANI, CCA, MIB) tally counters */ + CARL9170FW_HW_COUNTERS, + /* KEEP LAST */ __CARL9170FW_FEATURE_NUM }; diff --git a/drivers/net/wireless/ath/carl9170/hw.h b/drivers/net/wireless/ath/carl9170/hw.h index 261f89351070..fa834c1460f0 100644 --- a/drivers/net/wireless/ath/carl9170/hw.h +++ b/drivers/net/wireless/ath/carl9170/hw.h @@ -174,6 +174,7 @@ #define AR9170_MAC_SNIFFER_ENABLE_PROMISC BIT(0) #define AR9170_MAC_SNIFFER_DEFAULTS 0x02000000 #define AR9170_MAC_REG_ENCRYPTION (AR9170_MAC_REG_BASE + 0x678) +#define AR9170_MAC_ENCRYPTION_MGMT_RX_SOFTWARE BIT(2) #define AR9170_MAC_ENCRYPTION_RX_SOFTWARE BIT(3) #define AR9170_MAC_ENCRYPTION_DEFAULTS 0x70 @@ -222,6 +223,12 @@ #define AR9170_MAC_REG_TX_BLOCKACKS (AR9170_MAC_REG_BASE + 0x6c0) #define AR9170_MAC_REG_NAV_COUNT (AR9170_MAC_REG_BASE + 0x6c4) #define AR9170_MAC_REG_BACKOFF_STATUS (AR9170_MAC_REG_BASE + 0x6c8) +#define AR9170_MAC_BACKOFF_CCA BIT(24) +#define AR9170_MAC_BACKOFF_TX_PEX BIT(25) +#define AR9170_MAC_BACKOFF_RX_PE BIT(26) +#define AR9170_MAC_BACKOFF_MD_READY BIT(27) +#define AR9170_MAC_BACKOFF_TX_PE BIT(28) + #define AR9170_MAC_REG_TX_RETRY (AR9170_MAC_REG_BASE + 0x6cc) #define AR9170_MAC_REG_TX_COMPLETE (AR9170_MAC_REG_BASE + 0x6d4) @@ -388,10 +395,40 @@ #define AR9170_MAC_REG_BCN_CURR_ADDR (AR9170_MAC_REG_BASE + 0xd98) #define AR9170_MAC_REG_BCN_COUNT (AR9170_MAC_REG_BASE + 0xd9c) - - #define AR9170_MAC_REG_BCN_HT1 (AR9170_MAC_REG_BASE + 0xda0) +#define AR9170_MAC_BCN_HT1_HT_EN BIT(0) +#define AR9170_MAC_BCN_HT1_GF_PMB BIT(1) +#define AR9170_MAC_BCN_HT1_SP_EXP BIT(2) +#define AR9170_MAC_BCN_HT1_TX_BF BIT(3) +#define AR9170_MAC_BCN_HT1_PWR_CTRL_S 4 +#define AR9170_MAC_BCN_HT1_PWR_CTRL 0x70 +#define AR9170_MAC_BCN_HT1_TX_ANT1 BIT(7) +#define AR9170_MAC_BCN_HT1_TX_ANT0 BIT(8) +#define AR9170_MAC_BCN_HT1_NUM_LFT_S 9 +#define AR9170_MAC_BCN_HT1_NUM_LFT 0x600 +#define AR9170_MAC_BCN_HT1_BWC_20M_EXT BIT(16) +#define AR9170_MAC_BCN_HT1_BWC_40M_SHARED BIT(17) +#define AR9170_MAC_BCN_HT1_BWC_40M_DUP (BIT(16) | BIT(17)) +#define AR9170_MAC_BCN_HT1_BF_MCS_S 18 +#define AR9170_MAC_BCN_HT1_BF_MCS 0x1c0000 +#define AR9170_MAC_BCN_HT1_TPC_S 21 +#define AR9170_MAC_BCN_HT1_TPC 0x7e00000 +#define AR9170_MAC_BCN_HT1_CHAIN_MASK_S 27 +#define AR9170_MAC_BCN_HT1_CHAIN_MASK 0x38000000 + #define AR9170_MAC_REG_BCN_HT2 (AR9170_MAC_REG_BASE + 0xda4) +#define AR9170_MAC_BCN_HT2_MCS_S 0 +#define AR9170_MAC_BCN_HT2_MCS 0x7f +#define AR9170_MAC_BCN_HT2_BW40 BIT(8) +#define AR9170_MAC_BCN_HT2_SMOOTHING BIT(9) +#define AR9170_MAC_BCN_HT2_SS BIT(10) +#define AR9170_MAC_BCN_HT2_NSS BIT(11) +#define AR9170_MAC_BCN_HT2_STBC_S 12 +#define AR9170_MAC_BCN_HT2_STBC 0x3000 +#define AR9170_MAC_BCN_HT2_ADV_COD BIT(14) +#define AR9170_MAC_BCN_HT2_SGI BIT(15) +#define AR9170_MAC_BCN_HT2_LEN_S 16 +#define AR9170_MAC_BCN_HT2_LEN 0xffff0000 #define AR9170_MAC_REG_DMA_TXQX_ADDR_CURR (AR9170_MAC_REG_BASE + 0xdc0) diff --git a/drivers/net/wireless/ath/carl9170/led.c b/drivers/net/wireless/ath/carl9170/led.c index 4bb2cbd8bd9b..78dadc797558 100644 --- a/drivers/net/wireless/ath/carl9170/led.c +++ b/drivers/net/wireless/ath/carl9170/led.c @@ -118,7 +118,7 @@ static void carl9170_led_set_brightness(struct led_classdev *led, } if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled)) - ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ/10); + ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ / 10); } static int carl9170_led_register_led(struct ar9170 *ar, int i, char *name, diff --git a/drivers/net/wireless/ath/carl9170/mac.c b/drivers/net/wireless/ath/carl9170/mac.c index 385cf508479b..dfda91970995 100644 --- a/drivers/net/wireless/ath/carl9170/mac.c +++ b/drivers/net/wireless/ath/carl9170/mac.c @@ -455,135 +455,6 @@ int carl9170_set_beacon_timers(struct ar9170 *ar) return carl9170_regwrite_result(); } -int carl9170_update_beacon(struct ar9170 *ar, const bool submit) -{ - struct sk_buff *skb = NULL; - struct carl9170_vif_info *cvif; - struct ieee80211_tx_info *txinfo; - __le32 *data, *old = NULL; - u32 word, off, addr, len; - int i = 0, err = 0; - - rcu_read_lock(); - cvif = rcu_dereference(ar->beacon_iter); -retry: - if (ar->vifs == 0 || !cvif) - goto out_unlock; - - list_for_each_entry_continue_rcu(cvif, &ar->vif_list, list) { - if (cvif->active && cvif->enable_beacon) - goto found; - } - - if (!ar->beacon_enabled || i++) - goto out_unlock; - - goto retry; - -found: - rcu_assign_pointer(ar->beacon_iter, cvif); - - skb = ieee80211_beacon_get_tim(ar->hw, carl9170_get_vif(cvif), - NULL, NULL); - - if (!skb) { - err = -ENOMEM; - goto err_free; - } - - txinfo = IEEE80211_SKB_CB(skb); - if (txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS) { - err = -EINVAL; - goto err_free; - } - - spin_lock_bh(&ar->beacon_lock); - data = (__le32 *)skb->data; - if (cvif->beacon) - old = (__le32 *)cvif->beacon->data; - - off = cvif->id * AR9170_MAC_BCN_LENGTH_MAX; - addr = ar->fw.beacon_addr + off; - len = roundup(skb->len + FCS_LEN, 4); - - if ((off + len) > ar->fw.beacon_max_len) { - if (net_ratelimit()) { - wiphy_err(ar->hw->wiphy, "beacon does not " - "fit into device memory!\n"); - } - err = -EINVAL; - goto err_unlock; - } - - if (len > AR9170_MAC_BCN_LENGTH_MAX) { - if (net_ratelimit()) { - wiphy_err(ar->hw->wiphy, "no support for beacons " - "bigger than %d (yours:%d).\n", - AR9170_MAC_BCN_LENGTH_MAX, len); - } - - err = -EMSGSIZE; - goto err_unlock; - } - - i = txinfo->control.rates[0].idx; - if (txinfo->band != IEEE80211_BAND_2GHZ) - i += 4; - - word = __carl9170_ratetable[i].hw_value & 0xf; - if (i < 4) - word |= ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400; - else - word |= ((skb->len + FCS_LEN) << 16) + 0x0010; - - carl9170_async_regwrite_begin(ar); - carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP, word); - - for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) { - /* - * XXX: This accesses beyond skb data for up - * to the last 3 bytes!! - */ - - if (old && (data[i] == old[i])) - continue; - - word = le32_to_cpu(data[i]); - carl9170_async_regwrite(addr + 4 * i, word); - } - carl9170_async_regwrite_finish(); - - dev_kfree_skb_any(cvif->beacon); - cvif->beacon = NULL; - - err = carl9170_async_regwrite_result(); - if (!err) - cvif->beacon = skb; - spin_unlock_bh(&ar->beacon_lock); - if (err) - goto err_free; - - if (submit) { - err = carl9170_bcn_ctrl(ar, cvif->id, - CARL9170_BCN_CTRL_CAB_TRIGGER, - addr, skb->len + FCS_LEN); - - if (err) - goto err_free; - } -out_unlock: - rcu_read_unlock(); - return 0; - -err_unlock: - spin_unlock_bh(&ar->beacon_lock); - -err_free: - rcu_read_unlock(); - dev_kfree_skb_any(skb); - return err; -} - int carl9170_upload_key(struct ar9170 *ar, const u8 id, const u8 *mac, const u8 ktype, const u8 keyidx, const u8 *keydata, const int keylen) diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index a61cf6781d5e..0122930b14c7 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1630,7 +1630,7 @@ static int carl9170_read_eeprom(struct ar9170 *ar) BUILD_BUG_ON(sizeof(ar->eeprom) % RB); #endif - for (i = 0; i < sizeof(ar->eeprom)/RB; i++) { + for (i = 0; i < sizeof(ar->eeprom) / RB; i++) { for (j = 0; j < RW; j++) offsets[j] = cpu_to_le32(AR9170_EEPROM_START + RB * i + 4 * j); diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c index da1ab962ee48..aa147a9120b6 100644 --- a/drivers/net/wireless/ath/carl9170/phy.c +++ b/drivers/net/wireless/ath/carl9170/phy.c @@ -1098,7 +1098,7 @@ static u8 carl9170_interpolate_u8(u8 x, u8 x1, u8 y1, u8 x2, u8 y2) * Isn't it just DIV_ROUND_UP(y, 1<<SHIFT)? * Can we rely on the compiler to optimise away the div? */ - return (y >> SHIFT) + ((y & (1<<(SHIFT-1))) >> (SHIFT - 1)); + return (y >> SHIFT) + ((y & (1 << (SHIFT - 1))) >> (SHIFT - 1)); #undef SHIFT } @@ -1379,7 +1379,7 @@ static void carl9170_calc_ctl(struct ar9170 *ar, u32 freq, enum carl9170_bw bw) modes[i].max_power = carl9170_get_max_edge_power(ar, - freq+f_off, EDGES(ctl_idx, 1)); + freq + f_off, EDGES(ctl_idx, 1)); /* * TODO: check if the regulatory max. power is @@ -1441,7 +1441,7 @@ static int carl9170_set_power_cal(struct ar9170 *ar, u32 freq, if (freq < 3000) f = freq - 2300; else - f = (freq - 4800)/5; + f = (freq - 4800) / 5; /* * cycle through the various modes diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index e94084fcf6f5..d20946939cd8 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -661,11 +661,67 @@ void carl9170_tx_process_status(struct ar9170 *ar, } } +static void carl9170_tx_rate_tpc_chains(struct ar9170 *ar, + struct ieee80211_tx_info *info, struct ieee80211_tx_rate *txrate, + unsigned int *phyrate, unsigned int *tpc, unsigned int *chains) +{ + struct ieee80211_rate *rate = NULL; + u8 *txpower; + unsigned int idx; + + idx = txrate->idx; + *tpc = 0; + *phyrate = 0; + + if (txrate->flags & IEEE80211_TX_RC_MCS) { + if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { + /* +1 dBm for HT40 */ + *tpc += 2; + + if (info->band == IEEE80211_BAND_2GHZ) + txpower = ar->power_2G_ht40; + else + txpower = ar->power_5G_ht40; + } else { + if (info->band == IEEE80211_BAND_2GHZ) + txpower = ar->power_2G_ht20; + else + txpower = ar->power_5G_ht20; + } + + *phyrate = txrate->idx; + *tpc += txpower[idx & 7]; + } else { + if (info->band == IEEE80211_BAND_2GHZ) { + if (idx < 4) + txpower = ar->power_2G_cck; + else + txpower = ar->power_2G_ofdm; + } else { + txpower = ar->power_5G_leg; + idx += 4; + } + + rate = &__carl9170_ratetable[idx]; + *tpc += txpower[(rate->hw_value & 0x30) >> 4]; + *phyrate = rate->hw_value & 0xf; + } + + if (ar->eeprom.tx_mask == 1) { + *chains = AR9170_TX_PHY_TXCHAIN_1; + } else { + if (!(txrate->flags & IEEE80211_TX_RC_MCS) && + rate && rate->bitrate >= 360) + *chains = AR9170_TX_PHY_TXCHAIN_1; + else + *chains = AR9170_TX_PHY_TXCHAIN_2; + } +} + static __le32 carl9170_tx_physet(struct ar9170 *ar, struct ieee80211_tx_info *info, struct ieee80211_tx_rate *txrate) { - struct ieee80211_rate *rate = NULL; - u32 power, chains; + unsigned int power = 0, chains = 0, phyrate = 0; __le32 tmp; tmp = cpu_to_le32(0); @@ -682,35 +738,12 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar, tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI); if (txrate->flags & IEEE80211_TX_RC_MCS) { - u32 r = txrate->idx; - u8 *txpower; + SET_VAL(AR9170_TX_PHY_MCS, phyrate, txrate->idx); /* heavy clip control */ - tmp |= cpu_to_le32((r & 0x7) << + tmp |= cpu_to_le32((txrate->idx & 0x7) << AR9170_TX_PHY_TX_HEAVY_CLIP_S); - if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { - if (info->band == IEEE80211_BAND_5GHZ) - txpower = ar->power_5G_ht40; - else - txpower = ar->power_2G_ht40; - } else { - if (info->band == IEEE80211_BAND_5GHZ) - txpower = ar->power_5G_ht20; - else - txpower = ar->power_2G_ht20; - } - - power = txpower[r & 7]; - - /* +1 dBm for HT40 */ - if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - power += 2; - - r <<= AR9170_TX_PHY_MCS_S; - BUG_ON(r & ~AR9170_TX_PHY_MCS); - - tmp |= cpu_to_le32(r & AR9170_TX_PHY_MCS); tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_HT); /* @@ -720,34 +753,15 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar, * tmp |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD); */ } else { - u8 *txpower; - u32 mod; - u32 phyrate; - u8 idx = txrate->idx; - - if (info->band != IEEE80211_BAND_2GHZ) { - idx += 4; - txpower = ar->power_5G_leg; - mod = AR9170_TX_PHY_MOD_OFDM; + if (info->band == IEEE80211_BAND_2GHZ) { + if (txrate->idx <= AR9170_TX_PHY_RATE_CCK_11M) + tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_CCK); + else + tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_OFDM); } else { - if (idx < 4) { - txpower = ar->power_2G_cck; - mod = AR9170_TX_PHY_MOD_CCK; - } else { - mod = AR9170_TX_PHY_MOD_OFDM; - txpower = ar->power_2G_ofdm; - } + tmp |= cpu_to_le32(AR9170_TX_PHY_MOD_OFDM); } - rate = &__carl9170_ratetable[idx]; - - phyrate = rate->hw_value & 0xF; - power = txpower[(rate->hw_value & 0x30) >> 4]; - phyrate <<= AR9170_TX_PHY_MCS_S; - - tmp |= cpu_to_le32(mod); - tmp |= cpu_to_le32(phyrate); - /* * short preamble seems to be broken too. * @@ -755,23 +769,12 @@ static __le32 carl9170_tx_physet(struct ar9170 *ar, * tmp |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE); */ } - power <<= AR9170_TX_PHY_TX_PWR_S; - power &= AR9170_TX_PHY_TX_PWR; - tmp |= cpu_to_le32(power); - - /* set TX chains */ - if (ar->eeprom.tx_mask == 1) { - chains = AR9170_TX_PHY_TXCHAIN_1; - } else { - chains = AR9170_TX_PHY_TXCHAIN_2; - - /* >= 36M legacy OFDM - use only one chain */ - if (rate && rate->bitrate >= 360 && - !(txrate->flags & IEEE80211_TX_RC_MCS)) - chains = AR9170_TX_PHY_TXCHAIN_1; - } - tmp |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_S); + carl9170_tx_rate_tpc_chains(ar, info, txrate, + &phyrate, &power, &chains); + tmp |= cpu_to_le32(SET_CONSTVAL(AR9170_TX_PHY_MCS, phyrate)); + tmp |= cpu_to_le32(SET_CONSTVAL(AR9170_TX_PHY_TX_PWR, power)); + tmp |= cpu_to_le32(SET_CONSTVAL(AR9170_TX_PHY_TXCHAIN, chains)); return tmp; } @@ -1438,3 +1441,154 @@ void carl9170_tx_scheduler(struct ar9170 *ar) if (ar->tx_schedule) carl9170_tx(ar); } + +int carl9170_update_beacon(struct ar9170 *ar, const bool submit) +{ + struct sk_buff *skb = NULL; + struct carl9170_vif_info *cvif; + struct ieee80211_tx_info *txinfo; + struct ieee80211_tx_rate *rate; + __le32 *data, *old = NULL; + unsigned int plcp, power, chains; + u32 word, ht1, off, addr, len; + int i = 0, err = 0; + + rcu_read_lock(); + cvif = rcu_dereference(ar->beacon_iter); +retry: + if (ar->vifs == 0 || !cvif) + goto out_unlock; + + list_for_each_entry_continue_rcu(cvif, &ar->vif_list, list) { + if (cvif->active && cvif->enable_beacon) + goto found; + } + + if (!ar->beacon_enabled || i++) + goto out_unlock; + + goto retry; + +found: + rcu_assign_pointer(ar->beacon_iter, cvif); + + skb = ieee80211_beacon_get_tim(ar->hw, carl9170_get_vif(cvif), + NULL, NULL); + + if (!skb) { + err = -ENOMEM; + goto err_free; + } + + txinfo = IEEE80211_SKB_CB(skb); + spin_lock_bh(&ar->beacon_lock); + data = (__le32 *)skb->data; + if (cvif->beacon) + old = (__le32 *)cvif->beacon->data; + + off = cvif->id * AR9170_MAC_BCN_LENGTH_MAX; + addr = ar->fw.beacon_addr + off; + len = roundup(skb->len + FCS_LEN, 4); + + if ((off + len) > ar->fw.beacon_max_len) { + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "beacon does not " + "fit into device memory!\n"); + } + err = -EINVAL; + goto err_unlock; + } + + if (len > AR9170_MAC_BCN_LENGTH_MAX) { + if (net_ratelimit()) { + wiphy_err(ar->hw->wiphy, "no support for beacons " + "bigger than %d (yours:%d).\n", + AR9170_MAC_BCN_LENGTH_MAX, len); + } + + err = -EMSGSIZE; + goto err_unlock; + } + + ht1 = AR9170_MAC_BCN_HT1_TX_ANT0; + rate = &txinfo->control.rates[0]; + carl9170_tx_rate_tpc_chains(ar, txinfo, rate, &plcp, &power, &chains); + if (!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS)) { + if (plcp <= AR9170_TX_PHY_RATE_CCK_11M) + plcp |= ((skb->len + FCS_LEN) << (3 + 16)) + 0x0400; + else + plcp |= ((skb->len + FCS_LEN) << 16) + 0x0010; + } else { + ht1 |= AR9170_MAC_BCN_HT1_HT_EN; + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + plcp |= AR9170_MAC_BCN_HT2_SGI; + + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { + ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_SHARED; + plcp |= AR9170_MAC_BCN_HT2_BW40; + } + if (rate->flags & IEEE80211_TX_RC_DUP_DATA) { + ht1 |= AR9170_MAC_BCN_HT1_BWC_40M_DUP; + plcp |= AR9170_MAC_BCN_HT2_BW40; + } + + SET_VAL(AR9170_MAC_BCN_HT2_LEN, plcp, skb->len + FCS_LEN); + } + + SET_VAL(AR9170_MAC_BCN_HT1_PWR_CTRL, ht1, 7); + SET_VAL(AR9170_MAC_BCN_HT1_TPC, ht1, power); + SET_VAL(AR9170_MAC_BCN_HT1_CHAIN_MASK, ht1, chains); + if (chains == AR9170_TX_PHY_TXCHAIN_2) + ht1 |= AR9170_MAC_BCN_HT1_TX_ANT1; + + carl9170_async_regwrite_begin(ar); + carl9170_async_regwrite(AR9170_MAC_REG_BCN_HT1, ht1); + if (!(txinfo->control.rates[0].flags & IEEE80211_TX_RC_MCS)) + carl9170_async_regwrite(AR9170_MAC_REG_BCN_PLCP, plcp); + else + carl9170_async_regwrite(AR9170_MAC_REG_BCN_HT2, plcp); + + for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) { + /* + * XXX: This accesses beyond skb data for up + * to the last 3 bytes!! + */ + + if (old && (data[i] == old[i])) + continue; + + word = le32_to_cpu(data[i]); + carl9170_async_regwrite(addr + 4 * i, word); + } + carl9170_async_regwrite_finish(); + + dev_kfree_skb_any(cvif->beacon); + cvif->beacon = NULL; + + err = carl9170_async_regwrite_result(); + if (!err) + cvif->beacon = skb; + spin_unlock_bh(&ar->beacon_lock); + if (err) + goto err_free; + + if (submit) { + err = carl9170_bcn_ctrl(ar, cvif->id, + CARL9170_BCN_CTRL_CAB_TRIGGER, + addr, skb->len + FCS_LEN); + + if (err) + goto err_free; + } +out_unlock: + rcu_read_unlock(); + return 0; + +err_unlock: + spin_unlock_bh(&ar->beacon_lock); + +err_free: + rcu_read_unlock(); + dev_kfree_skb_any(skb); + return err; +} |