mac80211: fix a crash in the netlink backport
[oweals/openwrt.git] / package / mac80211 / patches / 526-ath9k_survey_channel_stats.patch
1 --- a/drivers/net/wireless/ath/ath9k/ath9k.h
2 +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
3 @@ -594,6 +594,8 @@ struct ath_softc {
4         struct delayed_work wiphy_work;
5         unsigned long wiphy_scheduler_int;
6         int wiphy_scheduler_index;
7 +       struct survey_info *cur_survey;
8 +       struct survey_info survey[ATH9K_NUM_CHANNELS];
9  
10         struct tasklet_struct intr_tq;
11         struct tasklet_struct bcon_tasklet;
12 --- a/drivers/net/wireless/ath/ath9k/main.c
13 +++ b/drivers/net/wireless/ath/ath9k/main.c
14 @@ -176,6 +176,44 @@ static void ath_start_ani(struct ath_com
15                         msecs_to_jiffies((u32)ah->config.ani_poll_interval));
16  }
17  
18 +static void ath_update_survey_nf(struct ath_softc *sc, int channel)
19 +{
20 +       struct ath_hw *ah = sc->sc_ah;
21 +       struct ath9k_channel *chan = &ah->channels[channel];
22 +       struct survey_info *survey = &sc->survey[channel];
23 +
24 +       if (chan->noisefloor) {
25 +               survey->filled |= SURVEY_INFO_NOISE_DBM;
26 +               survey->noise = chan->noisefloor;
27 +       }
28 +}
29 +
30 +static void ath_update_survey_stats(struct ath_softc *sc)
31 +{
32 +       struct ath_hw *ah = sc->sc_ah;
33 +       struct ath_common *common = ath9k_hw_common(ah);
34 +       int pos = ah->curchan - &ah->channels[0];
35 +       struct survey_info *survey = &sc->survey[pos];
36 +       struct ath_cycle_counters *cc = &common->cc_survey;
37 +       unsigned int div = common->clockrate * 1000;
38 +
39 +       ath_hw_cycle_counters_update(common);
40 +
41 +       if (cc->cycles > 0) {
42 +               survey->filled |= SURVEY_INFO_CHANNEL_TIME |
43 +                       SURVEY_INFO_CHANNEL_TIME_BUSY |
44 +                       SURVEY_INFO_CHANNEL_TIME_RX |
45 +                       SURVEY_INFO_CHANNEL_TIME_TX;
46 +               survey->channel_time += cc->cycles / div;
47 +               survey->channel_time_busy += cc->rx_busy / div;
48 +               survey->channel_time_rx += cc->rx_frame / div;
49 +               survey->channel_time_tx += cc->tx_frame / div;
50 +       }
51 +       memset(cc, 0, sizeof(*cc));
52 +
53 +       ath_update_survey_nf(sc, pos);
54 +}
55 +
56  /*
57   * Set/change channels.  If the channel is really being changed, it's done
58   * by reseting the chip.  To accomplish this we must first cleanup any pending
59 @@ -454,6 +492,7 @@ void ath_ani_calibrate(unsigned long dat
60                 if (aniflag) {
61                         spin_lock_irqsave(&common->cc_lock, flags);
62                         ath9k_hw_ani_monitor(ah, ah->curchan);
63 +                       ath_update_survey_stats(sc);
64                         spin_unlock_irqrestore(&common->cc_lock, flags);
65                 }
66  
67 @@ -1533,7 +1572,8 @@ static int ath9k_config(struct ieee80211
68  {
69         struct ath_wiphy *aphy = hw->priv;
70         struct ath_softc *sc = aphy->sc;
71 -       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
72 +       struct ath_hw *ah = sc->sc_ah;
73 +       struct ath_common *common = ath9k_hw_common(ah);
74         struct ieee80211_conf *conf = &hw->conf;
75         bool disable_radio;
76  
77 @@ -1599,6 +1639,11 @@ static int ath9k_config(struct ieee80211
78         if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
79                 struct ieee80211_channel *curchan = hw->conf.channel;
80                 int pos = curchan->hw_value;
81 +               int old_pos = -1;
82 +               unsigned long flags;
83 +
84 +               if (ah->curchan)
85 +                       old_pos = ah->curchan - &ah->channels[0];
86  
87                 aphy->chan_idx = pos;
88                 aphy->chan_is_ht = conf_is_ht(conf);
89 @@ -1626,12 +1671,45 @@ static int ath9k_config(struct ieee80211
90  
91                 ath_update_chainmask(sc, conf_is_ht(conf));
92  
93 +               /* update survey stats for the old channel before switching */
94 +               spin_lock_irqsave(&common->cc_lock, flags);
95 +               ath_update_survey_stats(sc);
96 +               spin_unlock_irqrestore(&common->cc_lock, flags);
97 +
98 +               /*
99 +                * If the operating channel changes, change the survey in-use flags
100 +                * along with it.
101 +                * Reset the survey data for the new channel, unless we're switching
102 +                * back to the operating channel from an off-channel operation.
103 +                */
104 +               if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) &&
105 +                   sc->cur_survey != &sc->survey[pos]) {
106 +
107 +                       if (sc->cur_survey)
108 +                               sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
109 +
110 +                       sc->cur_survey = &sc->survey[pos];
111 +
112 +                       memset(sc->cur_survey, 0, sizeof(struct survey_info));
113 +                       sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
114 +               } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
115 +                       memset(&sc->survey[pos], 0, sizeof(struct survey_info));
116 +               }
117 +
118                 if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
119                         ath_print(common, ATH_DBG_FATAL,
120                                   "Unable to set channel\n");
121                         mutex_unlock(&sc->mutex);
122                         return -EINVAL;
123                 }
124 +
125 +               /*
126 +                * The most recent snapshot of channel->noisefloor for the old
127 +                * channel is only available after the hardware reset. Copy it to
128 +                * the survey stats now.
129 +                */
130 +               if (old_pos >= 0)
131 +                       ath_update_survey_nf(sc, old_pos);
132         }
133  
134  skip_chan_change:
135 @@ -2001,9 +2079,15 @@ static int ath9k_get_survey(struct ieee8
136  {
137         struct ath_wiphy *aphy = hw->priv;
138         struct ath_softc *sc = aphy->sc;
139 -       struct ath_hw *ah = sc->sc_ah;
140 +       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
141         struct ieee80211_supported_band *sband;
142 -       struct ath9k_channel *chan;
143 +       struct ieee80211_channel *chan;
144 +       unsigned long flags;
145 +       int pos;
146 +
147 +       spin_lock_irqsave(&common->cc_lock, flags);
148 +       if (idx == 0)
149 +               ath_update_survey_stats(sc);
150  
151         sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ];
152         if (sband && idx >= sband->n_channels) {
153 @@ -2014,21 +2098,17 @@ static int ath9k_get_survey(struct ieee8
154         if (!sband)
155                 sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ];
156  
157 -       if (!sband || idx >= sband->n_channels)
158 -           return -ENOENT;
159 -
160 -       survey->channel = &sband->channels[idx];
161 -       chan = &ah->channels[survey->channel->hw_value];
162 -       survey->filled = 0;
163 -
164 -       if (chan == ah->curchan)
165 -               survey->filled |= SURVEY_INFO_IN_USE;
166 -
167 -       if (chan->noisefloor) {
168 -               survey->filled |= SURVEY_INFO_NOISE_DBM;
169 -               survey->noise = chan->noisefloor;
170 +       if (!sband || idx >= sband->n_channels) {
171 +               spin_unlock_irqrestore(&common->cc_lock, flags);
172 +               return -ENOENT;
173         }
174  
175 +       chan = &sband->channels[idx];
176 +       pos = chan->hw_value;
177 +       memcpy(survey, &sc->survey[pos], sizeof(*survey));
178 +       survey->channel = chan;
179 +       spin_unlock_irqrestore(&common->cc_lock, flags);
180 +
181         return 0;
182  }
183