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