]>
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 | |
30 | if (WARN_ON_ONCE(beacon_len < skb->len + sizeof(struct mt76x02_txwi))) | |
31 | return -ENOSPC; | |
32 | ||
5c48e60e MT |
33 | /* USB devices already reserve enough skb headroom for txwi's. This |
34 | * helps to save slow copies over USB. | |
35 | */ | |
36 | if (mt76_is_usb(&dev->mt76)) { | |
37 | struct mt76x02_txwi *txwi; | |
38 | ||
39 | txwi = (struct mt76x02_txwi *)(skb->data - sizeof(*txwi)); | |
40 | mt76x02_mac_write_txwi(dev, txwi, skb, NULL, NULL, skb->len); | |
41 | skb_push(skb, sizeof(*txwi)); | |
42 | } else { | |
43 | struct mt76x02_txwi txwi; | |
44 | ||
45 | mt76x02_mac_write_txwi(dev, &txwi, skb, NULL, NULL, skb->len); | |
46 | mt76_wr_copy(dev, offset, &txwi, sizeof(txwi)); | |
47 | offset += sizeof(txwi); | |
48 | } | |
8d71aef9 SG |
49 | |
50 | mt76_wr_copy(dev, offset, skb->data, skb->len); | |
51 | return 0; | |
52 | } | |
53 | ||
b74f98b8 SG |
54 | void mt76x02_mac_set_beacon(struct mt76x02_dev *dev, |
55 | struct sk_buff *skb) | |
8d71aef9 | 56 | { |
f27469a9 MT |
57 | int bcn_len = dev->beacon_ops->slot_size; |
58 | int bcn_addr = MT_BEACON_BASE + (bcn_len * dev->beacon_data_count); | |
8d71aef9 | 59 | |
b74f98b8 SG |
60 | if (!mt76x02_write_beacon(dev, bcn_addr, skb)) |
61 | dev->beacon_data_count++; | |
f27469a9 | 62 | dev_kfree_skb(skb); |
8d71aef9 | 63 | } |
7e07c27d | 64 | EXPORT_SYMBOL_GPL(mt76x02_mac_set_beacon); |
8d71aef9 | 65 | |
8d71aef9 | 66 | void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev, |
d5160d86 | 67 | struct ieee80211_vif *vif, bool enable) |
8d71aef9 | 68 | { |
d5160d86 LB |
69 | struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; |
70 | u8 old_mask = dev->mt76.beacon_mask; | |
8d71aef9 | 71 | |
1920a0cc | 72 | mt76x02_pre_tbtt_enable(dev, false); |
c004b881 | 73 | |
c8a04d98 | 74 | if (!dev->mt76.beacon_mask) |
8d71aef9 SG |
75 | dev->tbtt_count = 0; |
76 | ||
d5160d86 LB |
77 | if (enable) { |
78 | dev->mt76.beacon_mask |= BIT(mvif->idx); | |
79 | } else { | |
80 | dev->mt76.beacon_mask &= ~BIT(mvif->idx); | |
d5160d86 | 81 | } |
8d71aef9 | 82 | |
d5160d86 LB |
83 | if (!!old_mask == !!dev->mt76.beacon_mask) |
84 | goto out; | |
85 | ||
86 | if (dev->mt76.beacon_mask) | |
87 | mt76_set(dev, MT_BEACON_TIME_CFG, | |
88 | MT_BEACON_TIME_CFG_BEACON_TX | | |
89 | MT_BEACON_TIME_CFG_TBTT_EN | | |
90 | MT_BEACON_TIME_CFG_TIMER_EN); | |
91 | else | |
92 | mt76_clear(dev, MT_BEACON_TIME_CFG, | |
93 | MT_BEACON_TIME_CFG_BEACON_TX | | |
94 | MT_BEACON_TIME_CFG_TBTT_EN | | |
95 | MT_BEACON_TIME_CFG_TIMER_EN); | |
96 | mt76x02_beacon_enable(dev, !!dev->mt76.beacon_mask); | |
97 | ||
98 | out: | |
1920a0cc | 99 | mt76x02_pre_tbtt_enable(dev, true); |
8d71aef9 SG |
100 | } |
101 | ||
31cdd442 SG |
102 | void |
103 | mt76x02_resync_beacon_timer(struct mt76x02_dev *dev) | |
104 | { | |
3041c445 | 105 | u32 timer_val = dev->mt76.beacon_int << 4; |
31cdd442 SG |
106 | |
107 | dev->tbtt_count++; | |
108 | ||
109 | /* | |
110 | * Beacon timer drifts by 1us every tick, the timer is configured | |
111 | * in 1/16 TU (64us) units. | |
112 | */ | |
113 | if (dev->tbtt_count < 63) | |
114 | return; | |
115 | ||
116 | /* | |
117 | * The updated beacon interval takes effect after two TBTT, because | |
118 | * at this point the original interval has already been loaded into | |
119 | * the next TBTT_TIMER value | |
120 | */ | |
121 | if (dev->tbtt_count == 63) | |
122 | timer_val -= 1; | |
123 | ||
124 | mt76_rmw_field(dev, MT_BEACON_TIME_CFG, | |
125 | MT_BEACON_TIME_CFG_INTVAL, timer_val); | |
126 | ||
c15b0f7c | 127 | if (dev->tbtt_count >= 64) |
31cdd442 | 128 | dev->tbtt_count = 0; |
31cdd442 SG |
129 | } |
130 | EXPORT_SYMBOL_GPL(mt76x02_resync_beacon_timer); | |
131 | ||
132 | void | |
133 | mt76x02_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) | |
134 | { | |
135 | struct mt76x02_dev *dev = (struct mt76x02_dev *)priv; | |
136 | struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; | |
137 | struct sk_buff *skb = NULL; | |
138 | ||
c8a04d98 | 139 | if (!(dev->mt76.beacon_mask & BIT(mvif->idx))) |
31cdd442 SG |
140 | return; |
141 | ||
142 | skb = ieee80211_beacon_get(mt76_hw(dev), vif); | |
143 | if (!skb) | |
144 | return; | |
145 | ||
f27469a9 | 146 | mt76x02_mac_set_beacon(dev, skb); |
31cdd442 SG |
147 | } |
148 | EXPORT_SYMBOL_GPL(mt76x02_update_beacon_iter); | |
149 | ||
150 | static void | |
151 | mt76x02_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif) | |
152 | { | |
153 | struct beacon_bc_data *data = priv; | |
154 | struct mt76x02_dev *dev = data->dev; | |
155 | struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; | |
156 | struct ieee80211_tx_info *info; | |
157 | struct sk_buff *skb; | |
158 | ||
c8a04d98 | 159 | if (!(dev->mt76.beacon_mask & BIT(mvif->idx))) |
31cdd442 SG |
160 | return; |
161 | ||
162 | skb = ieee80211_get_buffered_bc(mt76_hw(dev), vif); | |
163 | if (!skb) | |
164 | return; | |
165 | ||
166 | info = IEEE80211_SKB_CB(skb); | |
167 | info->control.vif = vif; | |
168 | info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; | |
169 | mt76_skb_set_moredata(skb, true); | |
170 | __skb_queue_tail(&data->q, skb); | |
171 | data->tail[mvif->idx] = skb; | |
172 | } | |
173 | ||
174 | void | |
ff97c52a RL |
175 | mt76x02_enqueue_buffered_bc(struct mt76x02_dev *dev, |
176 | struct beacon_bc_data *data, | |
31cdd442 SG |
177 | int max_nframes) |
178 | { | |
179 | int i, nframes; | |
180 | ||
181 | data->dev = dev; | |
182 | __skb_queue_head_init(&data->q); | |
183 | ||
184 | do { | |
185 | nframes = skb_queue_len(&data->q); | |
186 | ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), | |
187 | IEEE80211_IFACE_ITER_RESUME_ALL, | |
188 | mt76x02_add_buffered_bc, data); | |
189 | } while (nframes != skb_queue_len(&data->q) && | |
190 | skb_queue_len(&data->q) < max_nframes); | |
191 | ||
192 | if (!skb_queue_len(&data->q)) | |
193 | return; | |
194 | ||
195 | for (i = 0; i < ARRAY_SIZE(data->tail); i++) { | |
196 | if (!data->tail[i]) | |
197 | continue; | |
198 | mt76_skb_set_moredata(data->tail[i], false); | |
199 | } | |
200 | } | |
201 | EXPORT_SYMBOL_GPL(mt76x02_enqueue_buffered_bc); | |
202 | ||
8d71aef9 SG |
203 | void mt76x02_init_beacon_config(struct mt76x02_dev *dev) |
204 | { | |
8d71aef9 SG |
205 | mt76_clear(dev, MT_BEACON_TIME_CFG, (MT_BEACON_TIME_CFG_TIMER_EN | |
206 | MT_BEACON_TIME_CFG_TBTT_EN | | |
207 | MT_BEACON_TIME_CFG_BEACON_TX)); | |
208 | mt76_set(dev, MT_BEACON_TIME_CFG, MT_BEACON_TIME_CFG_SYNC_MODE); | |
209 | mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xffff); | |
8d71aef9 SG |
210 | mt76x02_set_beacon_offsets(dev); |
211 | } | |
212 | EXPORT_SYMBOL_GPL(mt76x02_init_beacon_config); | |
213 |