18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> 48c2ecf20Sopenharmony_ci * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> 58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "mt76x02.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_cistatic void mt76x02_set_beacon_offsets(struct mt76x02_dev *dev) 118c2ecf20Sopenharmony_ci{ 128c2ecf20Sopenharmony_ci u32 regs[4] = {}; 138c2ecf20Sopenharmony_ci u16 val; 148c2ecf20Sopenharmony_ci int i; 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci for (i = 0; i < dev->beacon_ops->nslots; i++) { 178c2ecf20Sopenharmony_ci val = i * dev->beacon_ops->slot_size; 188c2ecf20Sopenharmony_ci regs[i / 4] |= (val / 64) << (8 * (i % 4)); 198c2ecf20Sopenharmony_ci } 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 228c2ecf20Sopenharmony_ci mt76_wr(dev, MT_BCN_OFFSET(i), regs[i]); 238c2ecf20Sopenharmony_ci} 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int 268c2ecf20Sopenharmony_cimt76x02_write_beacon(struct mt76x02_dev *dev, int offset, struct sk_buff *skb) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci int beacon_len = dev->beacon_ops->slot_size; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(beacon_len < skb->len + sizeof(struct mt76x02_txwi))) 318c2ecf20Sopenharmony_ci return -ENOSPC; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci /* USB devices already reserve enough skb headroom for txwi's. This 348c2ecf20Sopenharmony_ci * helps to save slow copies over USB. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci if (mt76_is_usb(&dev->mt76)) { 378c2ecf20Sopenharmony_ci struct mt76x02_txwi *txwi; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci txwi = (struct mt76x02_txwi *)(skb->data - sizeof(*txwi)); 408c2ecf20Sopenharmony_ci mt76x02_mac_write_txwi(dev, txwi, skb, NULL, NULL, skb->len); 418c2ecf20Sopenharmony_ci skb_push(skb, sizeof(*txwi)); 428c2ecf20Sopenharmony_ci } else { 438c2ecf20Sopenharmony_ci struct mt76x02_txwi txwi; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci mt76x02_mac_write_txwi(dev, &txwi, skb, NULL, NULL, skb->len); 468c2ecf20Sopenharmony_ci mt76_wr_copy(dev, offset, &txwi, sizeof(txwi)); 478c2ecf20Sopenharmony_ci offset += sizeof(txwi); 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci mt76_wr_copy(dev, offset, skb->data, skb->len); 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_civoid mt76x02_mac_set_beacon(struct mt76x02_dev *dev, 558c2ecf20Sopenharmony_ci struct sk_buff *skb) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci int bcn_len = dev->beacon_ops->slot_size; 588c2ecf20Sopenharmony_ci int bcn_addr = MT_BEACON_BASE + (bcn_len * dev->beacon_data_count); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (!mt76x02_write_beacon(dev, bcn_addr, skb)) 618c2ecf20Sopenharmony_ci dev->beacon_data_count++; 628c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_mac_set_beacon); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_civoid mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev, 678c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, bool enable) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; 708c2ecf20Sopenharmony_ci u8 old_mask = dev->mt76.beacon_mask; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci mt76x02_pre_tbtt_enable(dev, false); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (!dev->mt76.beacon_mask) 758c2ecf20Sopenharmony_ci dev->tbtt_count = 0; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (enable) { 788c2ecf20Sopenharmony_ci dev->mt76.beacon_mask |= BIT(mvif->idx); 798c2ecf20Sopenharmony_ci } else { 808c2ecf20Sopenharmony_ci dev->mt76.beacon_mask &= ~BIT(mvif->idx); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (!!old_mask == !!dev->mt76.beacon_mask) 848c2ecf20Sopenharmony_ci goto out; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (dev->mt76.beacon_mask) 878c2ecf20Sopenharmony_ci mt76_set(dev, MT_BEACON_TIME_CFG, 888c2ecf20Sopenharmony_ci MT_BEACON_TIME_CFG_BEACON_TX | 898c2ecf20Sopenharmony_ci MT_BEACON_TIME_CFG_TBTT_EN | 908c2ecf20Sopenharmony_ci MT_BEACON_TIME_CFG_TIMER_EN); 918c2ecf20Sopenharmony_ci else 928c2ecf20Sopenharmony_ci mt76_clear(dev, MT_BEACON_TIME_CFG, 938c2ecf20Sopenharmony_ci MT_BEACON_TIME_CFG_BEACON_TX | 948c2ecf20Sopenharmony_ci MT_BEACON_TIME_CFG_TBTT_EN | 958c2ecf20Sopenharmony_ci MT_BEACON_TIME_CFG_TIMER_EN); 968c2ecf20Sopenharmony_ci mt76x02_beacon_enable(dev, !!dev->mt76.beacon_mask); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ciout: 998c2ecf20Sopenharmony_ci mt76x02_pre_tbtt_enable(dev, true); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_civoid 1038c2ecf20Sopenharmony_cimt76x02_resync_beacon_timer(struct mt76x02_dev *dev) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci u32 timer_val = dev->mt76.beacon_int << 4; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci dev->tbtt_count++; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * Beacon timer drifts by 1us every tick, the timer is configured 1118c2ecf20Sopenharmony_ci * in 1/16 TU (64us) units. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci if (dev->tbtt_count < 63) 1148c2ecf20Sopenharmony_ci return; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* 1178c2ecf20Sopenharmony_ci * The updated beacon interval takes effect after two TBTT, because 1188c2ecf20Sopenharmony_ci * at this point the original interval has already been loaded into 1198c2ecf20Sopenharmony_ci * the next TBTT_TIMER value 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_ci if (dev->tbtt_count == 63) 1228c2ecf20Sopenharmony_ci timer_val -= 1; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci mt76_rmw_field(dev, MT_BEACON_TIME_CFG, 1258c2ecf20Sopenharmony_ci MT_BEACON_TIME_CFG_INTVAL, timer_val); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (dev->tbtt_count >= 64) 1288c2ecf20Sopenharmony_ci dev->tbtt_count = 0; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_resync_beacon_timer); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_civoid 1338c2ecf20Sopenharmony_cimt76x02_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct mt76x02_dev *dev = (struct mt76x02_dev *)priv; 1368c2ecf20Sopenharmony_ci struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; 1378c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (!(dev->mt76.beacon_mask & BIT(mvif->idx))) 1408c2ecf20Sopenharmony_ci return; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci skb = ieee80211_beacon_get(mt76_hw(dev), vif); 1438c2ecf20Sopenharmony_ci if (!skb) 1448c2ecf20Sopenharmony_ci return; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci mt76x02_mac_set_beacon(dev, skb); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_update_beacon_iter); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void 1518c2ecf20Sopenharmony_cimt76x02_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct beacon_bc_data *data = priv; 1548c2ecf20Sopenharmony_ci struct mt76x02_dev *dev = data->dev; 1558c2ecf20Sopenharmony_ci struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; 1568c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info; 1578c2ecf20Sopenharmony_ci struct sk_buff *skb; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (!(dev->mt76.beacon_mask & BIT(mvif->idx))) 1608c2ecf20Sopenharmony_ci return; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci skb = ieee80211_get_buffered_bc(mt76_hw(dev), vif); 1638c2ecf20Sopenharmony_ci if (!skb) 1648c2ecf20Sopenharmony_ci return; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 1678c2ecf20Sopenharmony_ci info->control.vif = vif; 1688c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; 1698c2ecf20Sopenharmony_ci mt76_skb_set_moredata(skb, true); 1708c2ecf20Sopenharmony_ci __skb_queue_tail(&data->q, skb); 1718c2ecf20Sopenharmony_ci data->tail[mvif->idx] = skb; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_civoid 1758c2ecf20Sopenharmony_cimt76x02_enqueue_buffered_bc(struct mt76x02_dev *dev, 1768c2ecf20Sopenharmony_ci struct beacon_bc_data *data, 1778c2ecf20Sopenharmony_ci int max_nframes) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci int i, nframes; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci data->dev = dev; 1828c2ecf20Sopenharmony_ci __skb_queue_head_init(&data->q); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci do { 1858c2ecf20Sopenharmony_ci nframes = skb_queue_len(&data->q); 1868c2ecf20Sopenharmony_ci ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), 1878c2ecf20Sopenharmony_ci IEEE80211_IFACE_ITER_RESUME_ALL, 1888c2ecf20Sopenharmony_ci mt76x02_add_buffered_bc, data); 1898c2ecf20Sopenharmony_ci } while (nframes != skb_queue_len(&data->q) && 1908c2ecf20Sopenharmony_ci skb_queue_len(&data->q) < max_nframes); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (!skb_queue_len(&data->q)) 1938c2ecf20Sopenharmony_ci return; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->tail); i++) { 1968c2ecf20Sopenharmony_ci if (!data->tail[i]) 1978c2ecf20Sopenharmony_ci continue; 1988c2ecf20Sopenharmony_ci mt76_skb_set_moredata(data->tail[i], false); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_enqueue_buffered_bc); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_civoid mt76x02_init_beacon_config(struct mt76x02_dev *dev) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci mt76_clear(dev, MT_BEACON_TIME_CFG, (MT_BEACON_TIME_CFG_TIMER_EN | 2068c2ecf20Sopenharmony_ci MT_BEACON_TIME_CFG_TBTT_EN | 2078c2ecf20Sopenharmony_ci MT_BEACON_TIME_CFG_BEACON_TX)); 2088c2ecf20Sopenharmony_ci mt76_set(dev, MT_BEACON_TIME_CFG, MT_BEACON_TIME_CFG_SYNC_MODE); 2098c2ecf20Sopenharmony_ci mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xffff); 2108c2ecf20Sopenharmony_ci mt76x02_set_beacon_offsets(dev); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76x02_init_beacon_config); 2138c2ecf20Sopenharmony_ci 214