]>
Commit | Line | Data |
---|---|---|
0e3d6777 | 1 | // SPDX-License-Identifier: ISC |
8d71aef9 SG |
2 | /* |
3 | * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> | |
4 | * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> | |
5 | * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl> | |
8d71aef9 SG |
6 | */ |
7 | ||
8 | #include "mt76x02.h" | |
9 | ||
8d71aef9 SG |
10 | static void mt76x02_set_beacon_offsets(struct mt76x02_dev *dev) |
11 | { | |
8d71aef9 | 12 | u32 regs[4] = {}; |
f2276c29 | 13 | u16 val; |
8d71aef9 SG |
14 | int i; |
15 | ||
f2276c29 SG |
16 | for (i = 0; i < dev->beacon_ops->nslots; i++) { |
17 | val = i * dev->beacon_ops->slot_size; | |
8d71aef9 SG |
18 | regs[i / 4] |= (val / 64) << (8 * (i % 4)); |
19 | } | |
20 | ||
21 | for (i = 0; i < 4; i++) | |
22 | mt76_wr(dev, MT_BCN_OFFSET(i), regs[i]); | |
23 | } | |
24 | ||
25 | static int | |
26 | mt76x02_write_beacon(struct mt76x02_dev *dev, int offset, struct sk_buff *skb) | |
27 | { | |
f2276c29 | 28 | int beacon_len = dev->beacon_ops->slot_size; |
8d71aef9 SG |
29 | struct mt76x02_txwi txwi; |
30 | ||
31 | if (WARN_ON_ONCE(beacon_len < skb->len + sizeof(struct mt76x02_txwi))) | |
32 | return -ENOSPC; | |
33 | ||
34 | mt76x02_mac_write_txwi(dev, &txwi, skb, NULL, NULL, skb->len); | |
35 | ||
36 | mt76_wr_copy(dev, offset, &txwi, sizeof(txwi)); | |
37 | offset += sizeof(txwi); | |
38 | ||
39 | mt76_wr_copy(dev, offset, skb->data, skb->len); | |
40 | return 0; | |
41 | } | |
42 | ||
43 | static int | |
44 | __mt76x02_mac_set_beacon(struct mt76x02_dev *dev, u8 bcn_idx, | |
45 | struct sk_buff *skb) | |
46 | { | |
f2276c29 SG |
47 | int beacon_len = dev->beacon_ops->slot_size; |
48 | int beacon_addr = MT_BEACON_BASE + (beacon_len * bcn_idx); | |
8d71aef9 SG |
49 | int ret = 0; |
50 | int i; | |
51 | ||
52 | /* Prevent corrupt transmissions during update */ | |
53 | mt76_set(dev, MT_BCN_BYPASS_MASK, BIT(bcn_idx)); | |
54 | ||
55 | if (skb) { | |
56 | ret = mt76x02_write_beacon(dev, beacon_addr, skb); | |
57 | if (!ret) | |
58 | dev->beacon_data_mask |= BIT(bcn_idx); | |
59 | } else { | |
60 | dev->beacon_data_mask &= ~BIT(bcn_idx); | |
61 | for (i = 0; i < beacon_len; i += 4) | |
62 | mt76_wr(dev, beacon_addr + i, 0); | |
63 | } | |
64 | ||
65 | mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xff00 | ~dev->beacon_data_mask); | |
66 | ||
67 | return ret; | |
68 | } | |
69 | ||
70 | int mt76x02_mac_set_beacon(struct mt76x02_dev *dev, u8 vif_idx, | |
71 | struct sk_buff *skb) | |
72 | { | |
73 | bool force_update = false; | |
74 | int bcn_idx = 0; | |
75 | int i; | |
76 | ||
77 | for (i = 0; i < ARRAY_SIZE(dev->beacons); i++) { | |
78 | if (vif_idx == i) { | |
79 | force_update = !!dev->beacons[i] ^ !!skb; | |
868ad214 | 80 | dev_kfree_skb(dev->beacons[i]); |
8d71aef9 SG |
81 | dev->beacons[i] = skb; |
82 | __mt76x02_mac_set_beacon(dev, bcn_idx, skb); | |
83 | } else if (force_update && dev->beacons[i]) { | |
84 | __mt76x02_mac_set_beacon(dev, bcn_idx, | |
85 | dev->beacons[i]); | |
86 | } | |
87 | ||
88 | bcn_idx += !!dev->beacons[i]; | |
89 | } | |
90 | ||
91 | for (i = bcn_idx; i < ARRAY_SIZE(dev->beacons); i++) { | |
92 | if (!(dev->beacon_data_mask & BIT(i))) | |
93 | break; | |
94 | ||
95 | __mt76x02_mac_set_beacon(dev, i, NULL); | |
96 | } | |
97 | ||
98 | mt76_rmw_field(dev, MT_MAC_BSSID_DW1, MT_MAC_BSSID_DW1_MBEACON_N, | |
99 | bcn_idx - 1); | |
100 | return 0; | |
101 | } | |
7e07c27d | 102 | EXPORT_SYMBOL_GPL(mt76x02_mac_set_beacon); |
8d71aef9 | 103 | |
8d71aef9 | 104 | void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev, |
d5160d86 | 105 | struct ieee80211_vif *vif, bool enable) |
8d71aef9 | 106 | { |
d5160d86 LB |
107 | struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; |
108 | u8 old_mask = dev->mt76.beacon_mask; | |
8d71aef9 | 109 | |
1920a0cc | 110 | mt76x02_pre_tbtt_enable(dev, false); |
c004b881 | 111 | |
c8a04d98 | 112 | if (!dev->mt76.beacon_mask) |
8d71aef9 SG |
113 | dev->tbtt_count = 0; |
114 | ||
d5160d86 LB |
115 | if (enable) { |
116 | dev->mt76.beacon_mask |= BIT(mvif->idx); | |
117 | } else { | |
118 | dev->mt76.beacon_mask &= ~BIT(mvif->idx); | |
119 | mt76x02_mac_set_beacon(dev, mvif->idx, NULL); | |
120 | } | |
8d71aef9 | 121 | |
d5160d86 LB |
122 | if (!!old_mask == !!dev->mt76.beacon_mask) |
123 | goto out; | |
124 | ||
125 | if (dev->mt76.beacon_mask) | |
126 | mt76_set(dev, MT_BEACON_TIME_CFG, | |
127 | MT_BEACON_TIME_CFG_BEACON_TX | | |
128 | MT_BEACON_TIME_CFG_TBTT_EN | | |
129 | MT_BEACON_TIME_CFG_TIMER_EN); | |
130 | else | |
131 | mt76_clear(dev, MT_BEACON_TIME_CFG, | |
132 | MT_BEACON_TIME_CFG_BEACON_TX | | |
133 | MT_BEACON_TIME_CFG_TBTT_EN | | |
134 | MT_BEACON_TIME_CFG_TIMER_EN); | |
135 | mt76x02_beacon_enable(dev, !!dev->mt76.beacon_mask); | |
136 | ||
137 | out: | |
1920a0cc | 138 | mt76x02_pre_tbtt_enable(dev, true); |
8d71aef9 SG |
139 | } |
140 | ||
31cdd442 SG |
141 | void |
142 | mt76x02_resync_beacon_timer(struct mt76x02_dev *dev) | |
143 | { | |
3041c445 | 144 | u32 timer_val = dev->mt76.beacon_int << 4; |
31cdd442 SG |
145 | |
146 | dev->tbtt_count++; | |
147 | ||
148 | /* | |
149 | * Beacon timer drifts by 1us every tick, the timer is configured | |
150 | * in 1/16 TU (64us) units. | |
151 | */ | |
152 | if (dev->tbtt_count < 63) | |
153 | return; | |
154 | ||
155 | /* | |
156 | * The updated beacon interval takes effect after two TBTT, because | |
157 | * at this point the original interval has already been loaded into | |
158 | * the next TBTT_TIMER value | |
159 | */ | |
160 | if (dev->tbtt_count == 63) | |
161 | timer_val -= 1; | |
162 | ||
163 | mt76_rmw_field(dev, MT_BEACON_TIME_CFG, | |
164 | MT_BEACON_TIME_CFG_INTVAL, timer_val); | |
165 | ||
c15b0f7c | 166 | if (dev->tbtt_count >= 64) |
31cdd442 | 167 | dev->tbtt_count = 0; |
31cdd442 SG |
168 | } |
169 | EXPORT_SYMBOL_GPL(mt76x02_resync_beacon_timer); | |
170 | ||
171 | void | |
172 | mt76x02_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) | |
173 | { | |
174 | struct mt76x02_dev *dev = (struct mt76x02_dev *)priv; | |
175 | struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; | |
176 | struct sk_buff *skb = NULL; | |
177 | ||
c8a04d98 | 178 | if (!(dev->mt76.beacon_mask & BIT(mvif->idx))) |
31cdd442 SG |
179 | return; |
180 | ||
181 | skb = ieee80211_beacon_get(mt76_hw(dev), vif); | |
182 | if (!skb) | |
183 | return; | |
184 | ||
185 | mt76x02_mac_set_beacon(dev, mvif->idx, skb); | |
186 | } | |
187 | EXPORT_SYMBOL_GPL(mt76x02_update_beacon_iter); | |
188 | ||
189 | static void | |
190 | mt76x02_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif) | |
191 | { | |
192 | struct beacon_bc_data *data = priv; | |
193 | struct mt76x02_dev *dev = data->dev; | |
194 | struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; | |
195 | struct ieee80211_tx_info *info; | |
196 | struct sk_buff *skb; | |
197 | ||
c8a04d98 | 198 | if (!(dev->mt76.beacon_mask & BIT(mvif->idx))) |
31cdd442 SG |
199 | return; |
200 | ||
201 | skb = ieee80211_get_buffered_bc(mt76_hw(dev), vif); | |
202 | if (!skb) | |
203 | return; | |
204 | ||
205 | info = IEEE80211_SKB_CB(skb); | |
206 | info->control.vif = vif; | |
207 | info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; | |
208 | mt76_skb_set_moredata(skb, true); | |
209 | __skb_queue_tail(&data->q, skb); | |
210 | data->tail[mvif->idx] = skb; | |
211 | } | |
212 | ||
213 | void | |
ff97c52a RL |
214 | mt76x02_enqueue_buffered_bc(struct mt76x02_dev *dev, |
215 | struct beacon_bc_data *data, | |
31cdd442 SG |
216 | int max_nframes) |
217 | { | |
218 | int i, nframes; | |
219 | ||
220 | data->dev = dev; | |
221 | __skb_queue_head_init(&data->q); | |
222 | ||
223 | do { | |
224 | nframes = skb_queue_len(&data->q); | |
225 | ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), | |
226 | IEEE80211_IFACE_ITER_RESUME_ALL, | |
227 | mt76x02_add_buffered_bc, data); | |
228 | } while (nframes != skb_queue_len(&data->q) && | |
229 | skb_queue_len(&data->q) < max_nframes); | |
230 | ||
231 | if (!skb_queue_len(&data->q)) | |
232 | return; | |
233 | ||
234 | for (i = 0; i < ARRAY_SIZE(data->tail); i++) { | |
235 | if (!data->tail[i]) | |
236 | continue; | |
237 | mt76_skb_set_moredata(data->tail[i], false); | |
238 | } | |
239 | } | |
240 | EXPORT_SYMBOL_GPL(mt76x02_enqueue_buffered_bc); | |
241 | ||
8d71aef9 SG |
242 | void mt76x02_init_beacon_config(struct mt76x02_dev *dev) |
243 | { | |
244 | int i; | |
245 | ||
246 | mt76_clear(dev, MT_BEACON_TIME_CFG, (MT_BEACON_TIME_CFG_TIMER_EN | | |
247 | MT_BEACON_TIME_CFG_TBTT_EN | | |
248 | MT_BEACON_TIME_CFG_BEACON_TX)); | |
249 | mt76_set(dev, MT_BEACON_TIME_CFG, MT_BEACON_TIME_CFG_SYNC_MODE); | |
250 | mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xffff); | |
251 | ||
252 | for (i = 0; i < 8; i++) | |
253 | mt76x02_mac_set_beacon(dev, i, NULL); | |
254 | ||
255 | mt76x02_set_beacon_offsets(dev); | |
256 | } | |
257 | EXPORT_SYMBOL_GPL(mt76x02_init_beacon_config); | |
258 |