18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Copyright (C) 2010 Willow Garage <http://www.willowgarage.com> 48c2ecf20Sopenharmony_ci Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com> 58c2ecf20Sopenharmony_ci <http://rt2x00.serialmonkey.com> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci/* 108c2ecf20Sopenharmony_ci Module: rt2x00lib 118c2ecf20Sopenharmony_ci Abstract: rt2x00 generic device routines. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/log2.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_net.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "rt2x00.h" 228c2ecf20Sopenharmony_ci#include "rt2x00lib.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * Utility functions. 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ciu32 rt2x00lib_get_bssidx(struct rt2x00_dev *rt2x00dev, 288c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci /* 318c2ecf20Sopenharmony_ci * When in STA mode, bssidx is always 0 otherwise local_address[5] 328c2ecf20Sopenharmony_ci * contains the bss number, see BSS_ID_MASK comments for details. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci if (rt2x00dev->intf_sta_count) 358c2ecf20Sopenharmony_ci return 0; 368c2ecf20Sopenharmony_ci return vif->addr[5] & (rt2x00dev->ops->max_ap_intf - 1); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_get_bssidx); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * Radio control handlers. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ciint rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci int status; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* 488c2ecf20Sopenharmony_ci * Don't enable the radio twice. 498c2ecf20Sopenharmony_ci * And check if the hardware button has been disabled. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* 558c2ecf20Sopenharmony_ci * Initialize all data queues. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci rt2x00queue_init_queues(rt2x00dev); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* 608c2ecf20Sopenharmony_ci * Enable radio. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci status = 638c2ecf20Sopenharmony_ci rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_ON); 648c2ecf20Sopenharmony_ci if (status) 658c2ecf20Sopenharmony_ci return status; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_ON); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci rt2x00leds_led_radio(rt2x00dev, true); 708c2ecf20Sopenharmony_ci rt2x00led_led_activity(rt2x00dev, true); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci set_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* 758c2ecf20Sopenharmony_ci * Enable queues. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci rt2x00queue_start_queues(rt2x00dev); 788c2ecf20Sopenharmony_ci rt2x00link_start_tuner(rt2x00dev); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* 818c2ecf20Sopenharmony_ci * Start watchdog monitoring. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci rt2x00link_start_watchdog(rt2x00dev); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_civoid rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci if (!test_and_clear_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 918c2ecf20Sopenharmony_ci return; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * Stop watchdog monitoring. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci rt2x00link_stop_watchdog(rt2x00dev); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* 998c2ecf20Sopenharmony_ci * Stop all queues 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci rt2x00link_stop_tuner(rt2x00dev); 1028c2ecf20Sopenharmony_ci rt2x00queue_stop_queues(rt2x00dev); 1038c2ecf20Sopenharmony_ci rt2x00queue_flush_queues(rt2x00dev, true); 1048c2ecf20Sopenharmony_ci rt2x00queue_stop_queue(rt2x00dev->bcn); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* 1078c2ecf20Sopenharmony_ci * Disable radio. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_OFF); 1108c2ecf20Sopenharmony_ci rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_OFF); 1118c2ecf20Sopenharmony_ci rt2x00led_led_activity(rt2x00dev, false); 1128c2ecf20Sopenharmony_ci rt2x00leds_led_radio(rt2x00dev, false); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac, 1168c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = data; 1198c2ecf20Sopenharmony_ci struct rt2x00_intf *intf = vif_to_intf(vif); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* 1228c2ecf20Sopenharmony_ci * It is possible the radio was disabled while the work had been 1238c2ecf20Sopenharmony_ci * scheduled. If that happens we should return here immediately, 1248c2ecf20Sopenharmony_ci * note that in the spinlock protected area above the delayed_flags 1258c2ecf20Sopenharmony_ci * have been cleared correctly. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 1288c2ecf20Sopenharmony_ci return; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags)) { 1318c2ecf20Sopenharmony_ci mutex_lock(&intf->beacon_skb_mutex); 1328c2ecf20Sopenharmony_ci rt2x00queue_update_beacon(rt2x00dev, vif); 1338c2ecf20Sopenharmony_ci mutex_unlock(&intf->beacon_skb_mutex); 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void rt2x00lib_intf_scheduled(struct work_struct *work) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = 1408c2ecf20Sopenharmony_ci container_of(work, struct rt2x00_dev, intf_work); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * Iterate over each interface and perform the 1448c2ecf20Sopenharmony_ci * requested configurations. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_ci ieee80211_iterate_active_interfaces(rt2x00dev->hw, 1478c2ecf20Sopenharmony_ci IEEE80211_IFACE_ITER_RESUME_ALL, 1488c2ecf20Sopenharmony_ci rt2x00lib_intf_scheduled_iter, 1498c2ecf20Sopenharmony_ci rt2x00dev); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void rt2x00lib_autowakeup(struct work_struct *work) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = 1558c2ecf20Sopenharmony_ci container_of(work, struct rt2x00_dev, autowakeup_work.work); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) 1588c2ecf20Sopenharmony_ci return; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) 1618c2ecf20Sopenharmony_ci rt2x00_err(rt2x00dev, "Device failed to wakeup\n"); 1628c2ecf20Sopenharmony_ci clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* 1668c2ecf20Sopenharmony_ci * Interrupt context handlers. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_cistatic void rt2x00lib_bc_buffer_iter(void *data, u8 *mac, 1698c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct ieee80211_tx_control control = {}; 1728c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = data; 1738c2ecf20Sopenharmony_ci struct sk_buff *skb; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* 1768c2ecf20Sopenharmony_ci * Only AP mode interfaces do broad- and multicast buffering 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_ci if (vif->type != NL80211_IFTYPE_AP) 1798c2ecf20Sopenharmony_ci return; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* 1828c2ecf20Sopenharmony_ci * Send out buffered broad- and multicast frames 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); 1858c2ecf20Sopenharmony_ci while (skb) { 1868c2ecf20Sopenharmony_ci rt2x00mac_tx(rt2x00dev->hw, &control, skb); 1878c2ecf20Sopenharmony_ci skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void rt2x00lib_beaconupdate_iter(void *data, u8 *mac, 1928c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = data; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (vif->type != NL80211_IFTYPE_AP && 1978c2ecf20Sopenharmony_ci vif->type != NL80211_IFTYPE_ADHOC && 1988c2ecf20Sopenharmony_ci vif->type != NL80211_IFTYPE_MESH_POINT && 1998c2ecf20Sopenharmony_ci vif->type != NL80211_IFTYPE_WDS) 2008c2ecf20Sopenharmony_ci return; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* 2038c2ecf20Sopenharmony_ci * Update the beacon without locking. This is safe on PCI devices 2048c2ecf20Sopenharmony_ci * as they only update the beacon periodically here. This should 2058c2ecf20Sopenharmony_ci * never be called for USB devices. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci WARN_ON(rt2x00_is_usb(rt2x00dev)); 2088c2ecf20Sopenharmony_ci rt2x00queue_update_beacon(rt2x00dev, vif); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_civoid rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 2148c2ecf20Sopenharmony_ci return; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* send buffered bc/mc frames out for every bssid */ 2178c2ecf20Sopenharmony_ci ieee80211_iterate_active_interfaces_atomic( 2188c2ecf20Sopenharmony_ci rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, 2198c2ecf20Sopenharmony_ci rt2x00lib_bc_buffer_iter, rt2x00dev); 2208c2ecf20Sopenharmony_ci /* 2218c2ecf20Sopenharmony_ci * Devices with pre tbtt interrupt don't need to update the beacon 2228c2ecf20Sopenharmony_ci * here as they will fetch the next beacon directly prior to 2238c2ecf20Sopenharmony_ci * transmission. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci if (rt2x00_has_cap_pre_tbtt_interrupt(rt2x00dev)) 2268c2ecf20Sopenharmony_ci return; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* fetch next beacon */ 2298c2ecf20Sopenharmony_ci ieee80211_iterate_active_interfaces_atomic( 2308c2ecf20Sopenharmony_ci rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, 2318c2ecf20Sopenharmony_ci rt2x00lib_beaconupdate_iter, rt2x00dev); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_beacondone); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_civoid rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 2388c2ecf20Sopenharmony_ci return; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* fetch next beacon */ 2418c2ecf20Sopenharmony_ci ieee80211_iterate_active_interfaces_atomic( 2428c2ecf20Sopenharmony_ci rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, 2438c2ecf20Sopenharmony_ci rt2x00lib_beaconupdate_iter, rt2x00dev); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_pretbtt); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_civoid rt2x00lib_dmastart(struct queue_entry *entry) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); 2508c2ecf20Sopenharmony_ci rt2x00queue_index_inc(entry, Q_INDEX); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_dmastart); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_civoid rt2x00lib_dmadone(struct queue_entry *entry) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci set_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags); 2578c2ecf20Sopenharmony_ci clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); 2588c2ecf20Sopenharmony_ci rt2x00queue_index_inc(entry, Q_INDEX_DMA_DONE); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_dmadone); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic inline int rt2x00lib_txdone_bar_status(struct queue_entry *entry) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; 2658c2ecf20Sopenharmony_ci struct ieee80211_bar *bar = (void *) entry->skb->data; 2668c2ecf20Sopenharmony_ci struct rt2x00_bar_list_entry *bar_entry; 2678c2ecf20Sopenharmony_ci int ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (likely(!ieee80211_is_back_req(bar->frame_control))) 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* 2738c2ecf20Sopenharmony_ci * Unlike all other frames, the status report for BARs does 2748c2ecf20Sopenharmony_ci * not directly come from the hardware as it is incapable of 2758c2ecf20Sopenharmony_ci * matching a BA to a previously send BAR. The hardware will 2768c2ecf20Sopenharmony_ci * report all BARs as if they weren't acked at all. 2778c2ecf20Sopenharmony_ci * 2788c2ecf20Sopenharmony_ci * Instead the RX-path will scan for incoming BAs and set the 2798c2ecf20Sopenharmony_ci * block_acked flag if it sees one that was likely caused by 2808c2ecf20Sopenharmony_ci * a BAR from us. 2818c2ecf20Sopenharmony_ci * 2828c2ecf20Sopenharmony_ci * Remove remaining BARs here and return their status for 2838c2ecf20Sopenharmony_ci * TX done processing. 2848c2ecf20Sopenharmony_ci */ 2858c2ecf20Sopenharmony_ci ret = 0; 2868c2ecf20Sopenharmony_ci rcu_read_lock(); 2878c2ecf20Sopenharmony_ci list_for_each_entry_rcu(bar_entry, &rt2x00dev->bar_list, list) { 2888c2ecf20Sopenharmony_ci if (bar_entry->entry != entry) 2898c2ecf20Sopenharmony_ci continue; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci spin_lock_bh(&rt2x00dev->bar_list_lock); 2928c2ecf20Sopenharmony_ci /* Return whether this BAR was blockacked or not */ 2938c2ecf20Sopenharmony_ci ret = bar_entry->block_acked; 2948c2ecf20Sopenharmony_ci /* Remove the BAR from our checklist */ 2958c2ecf20Sopenharmony_ci list_del_rcu(&bar_entry->list); 2968c2ecf20Sopenharmony_ci spin_unlock_bh(&rt2x00dev->bar_list_lock); 2978c2ecf20Sopenharmony_ci kfree_rcu(bar_entry, head); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci rcu_read_unlock(); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return ret; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic void rt2x00lib_fill_tx_status(struct rt2x00_dev *rt2x00dev, 3078c2ecf20Sopenharmony_ci struct ieee80211_tx_info *tx_info, 3088c2ecf20Sopenharmony_ci struct skb_frame_desc *skbdesc, 3098c2ecf20Sopenharmony_ci struct txdone_entry_desc *txdesc, 3108c2ecf20Sopenharmony_ci bool success) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci u8 rate_idx, rate_flags, retry_rates; 3138c2ecf20Sopenharmony_ci int i; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci rate_idx = skbdesc->tx_rate_idx; 3168c2ecf20Sopenharmony_ci rate_flags = skbdesc->tx_rate_flags; 3178c2ecf20Sopenharmony_ci retry_rates = test_bit(TXDONE_FALLBACK, &txdesc->flags) ? 3188c2ecf20Sopenharmony_ci (txdesc->retry + 1) : 1; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* 3218c2ecf20Sopenharmony_ci * Initialize TX status 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ci memset(&tx_info->status, 0, sizeof(tx_info->status)); 3248c2ecf20Sopenharmony_ci tx_info->status.ack_signal = 0; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* 3278c2ecf20Sopenharmony_ci * Frame was send with retries, hardware tried 3288c2ecf20Sopenharmony_ci * different rates to send out the frame, at each 3298c2ecf20Sopenharmony_ci * retry it lowered the rate 1 step except when the 3308c2ecf20Sopenharmony_ci * lowest rate was used. 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci for (i = 0; i < retry_rates && i < IEEE80211_TX_MAX_RATES; i++) { 3338c2ecf20Sopenharmony_ci tx_info->status.rates[i].idx = rate_idx - i; 3348c2ecf20Sopenharmony_ci tx_info->status.rates[i].flags = rate_flags; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (rate_idx - i == 0) { 3378c2ecf20Sopenharmony_ci /* 3388c2ecf20Sopenharmony_ci * The lowest rate (index 0) was used until the 3398c2ecf20Sopenharmony_ci * number of max retries was reached. 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci tx_info->status.rates[i].count = retry_rates - i; 3428c2ecf20Sopenharmony_ci i++; 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci tx_info->status.rates[i].count = 1; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci if (i < (IEEE80211_TX_MAX_RATES - 1)) 3488c2ecf20Sopenharmony_ci tx_info->status.rates[i].idx = -1; /* terminate */ 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (test_bit(TXDONE_NO_ACK_REQ, &txdesc->flags)) 3518c2ecf20Sopenharmony_ci tx_info->flags |= IEEE80211_TX_CTL_NO_ACK; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) { 3548c2ecf20Sopenharmony_ci if (success) 3558c2ecf20Sopenharmony_ci tx_info->flags |= IEEE80211_TX_STAT_ACK; 3568c2ecf20Sopenharmony_ci else 3578c2ecf20Sopenharmony_ci rt2x00dev->low_level_stats.dot11ACKFailureCount++; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* 3618c2ecf20Sopenharmony_ci * Every single frame has it's own tx status, hence report 3628c2ecf20Sopenharmony_ci * every frame as ampdu of size 1. 3638c2ecf20Sopenharmony_ci * 3648c2ecf20Sopenharmony_ci * TODO: if we can find out how many frames were aggregated 3658c2ecf20Sopenharmony_ci * by the hw we could provide the real ampdu_len to mac80211 3668c2ecf20Sopenharmony_ci * which would allow the rc algorithm to better decide on 3678c2ecf20Sopenharmony_ci * which rates are suitable. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ci if (test_bit(TXDONE_AMPDU, &txdesc->flags) || 3708c2ecf20Sopenharmony_ci tx_info->flags & IEEE80211_TX_CTL_AMPDU) { 3718c2ecf20Sopenharmony_ci tx_info->flags |= IEEE80211_TX_STAT_AMPDU | 3728c2ecf20Sopenharmony_ci IEEE80211_TX_CTL_AMPDU; 3738c2ecf20Sopenharmony_ci tx_info->status.ampdu_len = 1; 3748c2ecf20Sopenharmony_ci tx_info->status.ampdu_ack_len = success ? 1 : 0; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) { 3788c2ecf20Sopenharmony_ci if (success) 3798c2ecf20Sopenharmony_ci rt2x00dev->low_level_stats.dot11RTSSuccessCount++; 3808c2ecf20Sopenharmony_ci else 3818c2ecf20Sopenharmony_ci rt2x00dev->low_level_stats.dot11RTSFailureCount++; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic void rt2x00lib_clear_entry(struct rt2x00_dev *rt2x00dev, 3868c2ecf20Sopenharmony_ci struct queue_entry *entry) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci /* 3898c2ecf20Sopenharmony_ci * Make this entry available for reuse. 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_ci entry->skb = NULL; 3928c2ecf20Sopenharmony_ci entry->flags = 0; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci rt2x00dev->ops->lib->clear_entry(entry); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci rt2x00queue_index_inc(entry, Q_INDEX_DONE); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* 3998c2ecf20Sopenharmony_ci * If the data queue was below the threshold before the txdone 4008c2ecf20Sopenharmony_ci * handler we must make sure the packet queue in the mac80211 stack 4018c2ecf20Sopenharmony_ci * is reenabled when the txdone handler has finished. This has to be 4028c2ecf20Sopenharmony_ci * serialized with rt2x00mac_tx(), otherwise we can wake up queue 4038c2ecf20Sopenharmony_ci * before it was stopped. 4048c2ecf20Sopenharmony_ci */ 4058c2ecf20Sopenharmony_ci spin_lock_bh(&entry->queue->tx_lock); 4068c2ecf20Sopenharmony_ci if (!rt2x00queue_threshold(entry->queue)) 4078c2ecf20Sopenharmony_ci rt2x00queue_unpause_queue(entry->queue); 4088c2ecf20Sopenharmony_ci spin_unlock_bh(&entry->queue->tx_lock); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_civoid rt2x00lib_txdone_nomatch(struct queue_entry *entry, 4128c2ecf20Sopenharmony_ci struct txdone_entry_desc *txdesc) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; 4158c2ecf20Sopenharmony_ci struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); 4168c2ecf20Sopenharmony_ci struct ieee80211_tx_info txinfo = {}; 4178c2ecf20Sopenharmony_ci bool success; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* 4208c2ecf20Sopenharmony_ci * Unmap the skb. 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_ci rt2x00queue_unmap_skb(entry); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* 4258c2ecf20Sopenharmony_ci * Signal that the TX descriptor is no longer in the skb. 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_ci skbdesc->flags &= ~SKBDESC_DESC_IN_SKB; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* 4308c2ecf20Sopenharmony_ci * Send frame to debugfs immediately, after this call is completed 4318c2ecf20Sopenharmony_ci * we are going to overwrite the skb->cb array. 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_ci rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* 4368c2ecf20Sopenharmony_ci * Determine if the frame has been successfully transmitted and 4378c2ecf20Sopenharmony_ci * remove BARs from our check list while checking for their 4388c2ecf20Sopenharmony_ci * TX status. 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_ci success = 4418c2ecf20Sopenharmony_ci rt2x00lib_txdone_bar_status(entry) || 4428c2ecf20Sopenharmony_ci test_bit(TXDONE_SUCCESS, &txdesc->flags); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (!test_bit(TXDONE_UNKNOWN, &txdesc->flags)) { 4458c2ecf20Sopenharmony_ci /* 4468c2ecf20Sopenharmony_ci * Update TX statistics. 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_ci rt2x00dev->link.qual.tx_success += success; 4498c2ecf20Sopenharmony_ci rt2x00dev->link.qual.tx_failed += !success; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci rt2x00lib_fill_tx_status(rt2x00dev, &txinfo, skbdesc, txdesc, 4528c2ecf20Sopenharmony_ci success); 4538c2ecf20Sopenharmony_ci ieee80211_tx_status_noskb(rt2x00dev->hw, skbdesc->sta, &txinfo); 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci dev_kfree_skb_any(entry->skb); 4578c2ecf20Sopenharmony_ci rt2x00lib_clear_entry(rt2x00dev, entry); 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_txdone_nomatch); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_civoid rt2x00lib_txdone(struct queue_entry *entry, 4628c2ecf20Sopenharmony_ci struct txdone_entry_desc *txdesc) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; 4658c2ecf20Sopenharmony_ci struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb); 4668c2ecf20Sopenharmony_ci struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); 4678c2ecf20Sopenharmony_ci u8 skbdesc_flags = skbdesc->flags; 4688c2ecf20Sopenharmony_ci unsigned int header_length; 4698c2ecf20Sopenharmony_ci bool success; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* 4728c2ecf20Sopenharmony_ci * Unmap the skb. 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ci rt2x00queue_unmap_skb(entry); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* 4778c2ecf20Sopenharmony_ci * Remove the extra tx headroom from the skb. 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_ci skb_pull(entry->skb, rt2x00dev->extra_tx_headroom); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* 4828c2ecf20Sopenharmony_ci * Signal that the TX descriptor is no longer in the skb. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_ci skbdesc->flags &= ~SKBDESC_DESC_IN_SKB; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* 4878c2ecf20Sopenharmony_ci * Determine the length of 802.11 header. 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci header_length = ieee80211_get_hdrlen_from_skb(entry->skb); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* 4928c2ecf20Sopenharmony_ci * Remove L2 padding which was added during 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_ci if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD)) 4958c2ecf20Sopenharmony_ci rt2x00queue_remove_l2pad(entry->skb, header_length); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* 4988c2ecf20Sopenharmony_ci * If the IV/EIV data was stripped from the frame before it was 4998c2ecf20Sopenharmony_ci * passed to the hardware, we should now reinsert it again because 5008c2ecf20Sopenharmony_ci * mac80211 will expect the same data to be present it the 5018c2ecf20Sopenharmony_ci * frame as it was passed to us. 5028c2ecf20Sopenharmony_ci */ 5038c2ecf20Sopenharmony_ci if (rt2x00_has_cap_hw_crypto(rt2x00dev)) 5048c2ecf20Sopenharmony_ci rt2x00crypto_tx_insert_iv(entry->skb, header_length); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* 5078c2ecf20Sopenharmony_ci * Send frame to debugfs immediately, after this call is completed 5088c2ecf20Sopenharmony_ci * we are going to overwrite the skb->cb array. 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ci rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* 5138c2ecf20Sopenharmony_ci * Determine if the frame has been successfully transmitted and 5148c2ecf20Sopenharmony_ci * remove BARs from our check list while checking for their 5158c2ecf20Sopenharmony_ci * TX status. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ci success = 5188c2ecf20Sopenharmony_ci rt2x00lib_txdone_bar_status(entry) || 5198c2ecf20Sopenharmony_ci test_bit(TXDONE_SUCCESS, &txdesc->flags) || 5208c2ecf20Sopenharmony_ci test_bit(TXDONE_UNKNOWN, &txdesc->flags); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* 5238c2ecf20Sopenharmony_ci * Update TX statistics. 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_ci rt2x00dev->link.qual.tx_success += success; 5268c2ecf20Sopenharmony_ci rt2x00dev->link.qual.tx_failed += !success; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci rt2x00lib_fill_tx_status(rt2x00dev, tx_info, skbdesc, txdesc, success); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* 5318c2ecf20Sopenharmony_ci * Only send the status report to mac80211 when it's a frame 5328c2ecf20Sopenharmony_ci * that originated in mac80211. If this was a extra frame coming 5338c2ecf20Sopenharmony_ci * through a mac80211 library call (RTS/CTS) then we should not 5348c2ecf20Sopenharmony_ci * send the status report back. 5358c2ecf20Sopenharmony_ci */ 5368c2ecf20Sopenharmony_ci if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) { 5378c2ecf20Sopenharmony_ci if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TASKLET_CONTEXT)) 5388c2ecf20Sopenharmony_ci ieee80211_tx_status(rt2x00dev->hw, entry->skb); 5398c2ecf20Sopenharmony_ci else 5408c2ecf20Sopenharmony_ci ieee80211_tx_status_ni(rt2x00dev->hw, entry->skb); 5418c2ecf20Sopenharmony_ci } else { 5428c2ecf20Sopenharmony_ci dev_kfree_skb_any(entry->skb); 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci rt2x00lib_clear_entry(rt2x00dev, entry); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_txdone); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_civoid rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct txdone_entry_desc txdesc; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci txdesc.flags = 0; 5548c2ecf20Sopenharmony_ci __set_bit(status, &txdesc.flags); 5558c2ecf20Sopenharmony_ci txdesc.retry = 0; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci rt2x00lib_txdone(entry, &txdesc); 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_txdone_noinfo); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic u8 *rt2x00lib_find_ie(u8 *data, unsigned int len, u8 ie) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt = (void *)data; 5648c2ecf20Sopenharmony_ci u8 *pos, *end; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci pos = (u8 *)mgmt->u.beacon.variable; 5678c2ecf20Sopenharmony_ci end = data + len; 5688c2ecf20Sopenharmony_ci while (pos < end) { 5698c2ecf20Sopenharmony_ci if (pos + 2 + pos[1] > end) 5708c2ecf20Sopenharmony_ci return NULL; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (pos[0] == ie) 5738c2ecf20Sopenharmony_ci return pos; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci pos += 2 + pos[1]; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci return NULL; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic void rt2x00lib_sleep(struct work_struct *work) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = 5848c2ecf20Sopenharmony_ci container_of(work, struct rt2x00_dev, sleep_work); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) 5878c2ecf20Sopenharmony_ci return; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* 5908c2ecf20Sopenharmony_ci * Check again is powersaving is enabled, to prevent races from delayed 5918c2ecf20Sopenharmony_ci * work execution. 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_ci if (!test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags)) 5948c2ecf20Sopenharmony_ci rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf, 5958c2ecf20Sopenharmony_ci IEEE80211_CONF_CHANGE_PS); 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic void rt2x00lib_rxdone_check_ba(struct rt2x00_dev *rt2x00dev, 5998c2ecf20Sopenharmony_ci struct sk_buff *skb, 6008c2ecf20Sopenharmony_ci struct rxdone_entry_desc *rxdesc) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci struct rt2x00_bar_list_entry *entry; 6038c2ecf20Sopenharmony_ci struct ieee80211_bar *ba = (void *)skb->data; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (likely(!ieee80211_is_back(ba->frame_control))) 6068c2ecf20Sopenharmony_ci return; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (rxdesc->size < sizeof(*ba) + FCS_LEN) 6098c2ecf20Sopenharmony_ci return; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci rcu_read_lock(); 6128c2ecf20Sopenharmony_ci list_for_each_entry_rcu(entry, &rt2x00dev->bar_list, list) { 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (ba->start_seq_num != entry->start_seq_num) 6158c2ecf20Sopenharmony_ci continue; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci#define TID_CHECK(a, b) ( \ 6188c2ecf20Sopenharmony_ci ((a) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK)) == \ 6198c2ecf20Sopenharmony_ci ((b) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK))) \ 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (!TID_CHECK(ba->control, entry->control)) 6228c2ecf20Sopenharmony_ci continue; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci#undef TID_CHECK 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (!ether_addr_equal_64bits(ba->ra, entry->ta)) 6278c2ecf20Sopenharmony_ci continue; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (!ether_addr_equal_64bits(ba->ta, entry->ra)) 6308c2ecf20Sopenharmony_ci continue; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /* Mark BAR since we received the according BA */ 6338c2ecf20Sopenharmony_ci spin_lock_bh(&rt2x00dev->bar_list_lock); 6348c2ecf20Sopenharmony_ci entry->block_acked = 1; 6358c2ecf20Sopenharmony_ci spin_unlock_bh(&rt2x00dev->bar_list_lock); 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci rcu_read_unlock(); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev, 6438c2ecf20Sopenharmony_ci struct sk_buff *skb, 6448c2ecf20Sopenharmony_ci struct rxdone_entry_desc *rxdesc) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr = (void *) skb->data; 6478c2ecf20Sopenharmony_ci struct ieee80211_tim_ie *tim_ie; 6488c2ecf20Sopenharmony_ci u8 *tim; 6498c2ecf20Sopenharmony_ci u8 tim_len; 6508c2ecf20Sopenharmony_ci bool cam; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* If this is not a beacon, or if mac80211 has no powersaving 6538c2ecf20Sopenharmony_ci * configured, or if the device is already in powersaving mode 6548c2ecf20Sopenharmony_ci * we can exit now. */ 6558c2ecf20Sopenharmony_ci if (likely(!ieee80211_is_beacon(hdr->frame_control) || 6568c2ecf20Sopenharmony_ci !(rt2x00dev->hw->conf.flags & IEEE80211_CONF_PS))) 6578c2ecf20Sopenharmony_ci return; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* min. beacon length + FCS_LEN */ 6608c2ecf20Sopenharmony_ci if (skb->len <= 40 + FCS_LEN) 6618c2ecf20Sopenharmony_ci return; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* and only beacons from the associated BSSID, please */ 6648c2ecf20Sopenharmony_ci if (!(rxdesc->dev_flags & RXDONE_MY_BSS) || 6658c2ecf20Sopenharmony_ci !rt2x00dev->aid) 6668c2ecf20Sopenharmony_ci return; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci rt2x00dev->last_beacon = jiffies; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci tim = rt2x00lib_find_ie(skb->data, skb->len - FCS_LEN, WLAN_EID_TIM); 6718c2ecf20Sopenharmony_ci if (!tim) 6728c2ecf20Sopenharmony_ci return; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (tim[1] < sizeof(*tim_ie)) 6758c2ecf20Sopenharmony_ci return; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci tim_len = tim[1]; 6788c2ecf20Sopenharmony_ci tim_ie = (struct ieee80211_tim_ie *) &tim[2]; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* Check whenever the PHY can be turned off again. */ 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* 1. What about buffered unicast traffic for our AID? */ 6838c2ecf20Sopenharmony_ci cam = ieee80211_check_tim(tim_ie, tim_len, rt2x00dev->aid); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* 2. Maybe the AP wants to send multicast/broadcast data? */ 6868c2ecf20Sopenharmony_ci cam |= (tim_ie->bitmap_ctrl & 0x01); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci if (!cam && !test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags)) 6898c2ecf20Sopenharmony_ci queue_work(rt2x00dev->workqueue, &rt2x00dev->sleep_work); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev, 6938c2ecf20Sopenharmony_ci struct rxdone_entry_desc *rxdesc) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 6968c2ecf20Sopenharmony_ci const struct rt2x00_rate *rate; 6978c2ecf20Sopenharmony_ci unsigned int i; 6988c2ecf20Sopenharmony_ci int signal = rxdesc->signal; 6998c2ecf20Sopenharmony_ci int type = (rxdesc->dev_flags & RXDONE_SIGNAL_MASK); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci switch (rxdesc->rate_mode) { 7028c2ecf20Sopenharmony_ci case RATE_MODE_CCK: 7038c2ecf20Sopenharmony_ci case RATE_MODE_OFDM: 7048c2ecf20Sopenharmony_ci /* 7058c2ecf20Sopenharmony_ci * For non-HT rates the MCS value needs to contain the 7068c2ecf20Sopenharmony_ci * actually used rate modulation (CCK or OFDM). 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_ci if (rxdesc->dev_flags & RXDONE_SIGNAL_MCS) 7098c2ecf20Sopenharmony_ci signal = RATE_MCS(rxdesc->rate_mode, signal); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci sband = &rt2x00dev->bands[rt2x00dev->curr_band]; 7128c2ecf20Sopenharmony_ci for (i = 0; i < sband->n_bitrates; i++) { 7138c2ecf20Sopenharmony_ci rate = rt2x00_get_rate(sband->bitrates[i].hw_value); 7148c2ecf20Sopenharmony_ci if (((type == RXDONE_SIGNAL_PLCP) && 7158c2ecf20Sopenharmony_ci (rate->plcp == signal)) || 7168c2ecf20Sopenharmony_ci ((type == RXDONE_SIGNAL_BITRATE) && 7178c2ecf20Sopenharmony_ci (rate->bitrate == signal)) || 7188c2ecf20Sopenharmony_ci ((type == RXDONE_SIGNAL_MCS) && 7198c2ecf20Sopenharmony_ci (rate->mcs == signal))) { 7208c2ecf20Sopenharmony_ci return i; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci break; 7248c2ecf20Sopenharmony_ci case RATE_MODE_HT_MIX: 7258c2ecf20Sopenharmony_ci case RATE_MODE_HT_GREENFIELD: 7268c2ecf20Sopenharmony_ci if (signal >= 0 && signal <= 76) 7278c2ecf20Sopenharmony_ci return signal; 7288c2ecf20Sopenharmony_ci break; 7298c2ecf20Sopenharmony_ci default: 7308c2ecf20Sopenharmony_ci break; 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci rt2x00_warn(rt2x00dev, "Frame received with unrecognized signal, mode=0x%.4x, signal=0x%.4x, type=%d\n", 7348c2ecf20Sopenharmony_ci rxdesc->rate_mode, signal, type); 7358c2ecf20Sopenharmony_ci return 0; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_civoid rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; 7418c2ecf20Sopenharmony_ci struct rxdone_entry_desc rxdesc; 7428c2ecf20Sopenharmony_ci struct sk_buff *skb; 7438c2ecf20Sopenharmony_ci struct ieee80211_rx_status *rx_status; 7448c2ecf20Sopenharmony_ci unsigned int header_length; 7458c2ecf20Sopenharmony_ci int rate_idx; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || 7488c2ecf20Sopenharmony_ci !test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 7498c2ecf20Sopenharmony_ci goto submit_entry; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) 7528c2ecf20Sopenharmony_ci goto submit_entry; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* 7558c2ecf20Sopenharmony_ci * Allocate a new sk_buffer. If no new buffer available, drop the 7568c2ecf20Sopenharmony_ci * received frame and reuse the existing buffer. 7578c2ecf20Sopenharmony_ci */ 7588c2ecf20Sopenharmony_ci skb = rt2x00queue_alloc_rxskb(entry, gfp); 7598c2ecf20Sopenharmony_ci if (!skb) 7608c2ecf20Sopenharmony_ci goto submit_entry; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci /* 7638c2ecf20Sopenharmony_ci * Unmap the skb. 7648c2ecf20Sopenharmony_ci */ 7658c2ecf20Sopenharmony_ci rt2x00queue_unmap_skb(entry); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci /* 7688c2ecf20Sopenharmony_ci * Extract the RXD details. 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_ci memset(&rxdesc, 0, sizeof(rxdesc)); 7718c2ecf20Sopenharmony_ci rt2x00dev->ops->lib->fill_rxdone(entry, &rxdesc); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci /* 7748c2ecf20Sopenharmony_ci * Check for valid size in case we get corrupted descriptor from 7758c2ecf20Sopenharmony_ci * hardware. 7768c2ecf20Sopenharmony_ci */ 7778c2ecf20Sopenharmony_ci if (unlikely(rxdesc.size == 0 || 7788c2ecf20Sopenharmony_ci rxdesc.size > entry->queue->data_size)) { 7798c2ecf20Sopenharmony_ci rt2x00_err(rt2x00dev, "Wrong frame size %d max %d\n", 7808c2ecf20Sopenharmony_ci rxdesc.size, entry->queue->data_size); 7818c2ecf20Sopenharmony_ci dev_kfree_skb(entry->skb); 7828c2ecf20Sopenharmony_ci goto renew_skb; 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci /* 7868c2ecf20Sopenharmony_ci * The data behind the ieee80211 header must be 7878c2ecf20Sopenharmony_ci * aligned on a 4 byte boundary. 7888c2ecf20Sopenharmony_ci */ 7898c2ecf20Sopenharmony_ci header_length = ieee80211_get_hdrlen_from_skb(entry->skb); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* 7928c2ecf20Sopenharmony_ci * Hardware might have stripped the IV/EIV/ICV data, 7938c2ecf20Sopenharmony_ci * in that case it is possible that the data was 7948c2ecf20Sopenharmony_ci * provided separately (through hardware descriptor) 7958c2ecf20Sopenharmony_ci * in which case we should reinsert the data into the frame. 7968c2ecf20Sopenharmony_ci */ 7978c2ecf20Sopenharmony_ci if ((rxdesc.dev_flags & RXDONE_CRYPTO_IV) && 7988c2ecf20Sopenharmony_ci (rxdesc.flags & RX_FLAG_IV_STRIPPED)) 7998c2ecf20Sopenharmony_ci rt2x00crypto_rx_insert_iv(entry->skb, header_length, 8008c2ecf20Sopenharmony_ci &rxdesc); 8018c2ecf20Sopenharmony_ci else if (header_length && 8028c2ecf20Sopenharmony_ci (rxdesc.size > header_length) && 8038c2ecf20Sopenharmony_ci (rxdesc.dev_flags & RXDONE_L2PAD)) 8048c2ecf20Sopenharmony_ci rt2x00queue_remove_l2pad(entry->skb, header_length); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* Trim buffer to correct size */ 8078c2ecf20Sopenharmony_ci skb_trim(entry->skb, rxdesc.size); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci /* 8108c2ecf20Sopenharmony_ci * Translate the signal to the correct bitrate index. 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_ci rate_idx = rt2x00lib_rxdone_read_signal(rt2x00dev, &rxdesc); 8138c2ecf20Sopenharmony_ci if (rxdesc.rate_mode == RATE_MODE_HT_MIX || 8148c2ecf20Sopenharmony_ci rxdesc.rate_mode == RATE_MODE_HT_GREENFIELD) 8158c2ecf20Sopenharmony_ci rxdesc.encoding = RX_ENC_HT; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci /* 8188c2ecf20Sopenharmony_ci * Check if this is a beacon, and more frames have been 8198c2ecf20Sopenharmony_ci * buffered while we were in powersaving mode. 8208c2ecf20Sopenharmony_ci */ 8218c2ecf20Sopenharmony_ci rt2x00lib_rxdone_check_ps(rt2x00dev, entry->skb, &rxdesc); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci /* 8248c2ecf20Sopenharmony_ci * Check for incoming BlockAcks to match to the BlockAckReqs 8258c2ecf20Sopenharmony_ci * we've send out. 8268c2ecf20Sopenharmony_ci */ 8278c2ecf20Sopenharmony_ci rt2x00lib_rxdone_check_ba(rt2x00dev, entry->skb, &rxdesc); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci /* 8308c2ecf20Sopenharmony_ci * Update extra components 8318c2ecf20Sopenharmony_ci */ 8328c2ecf20Sopenharmony_ci rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc); 8338c2ecf20Sopenharmony_ci rt2x00debug_update_crypto(rt2x00dev, &rxdesc); 8348c2ecf20Sopenharmony_ci rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci /* 8378c2ecf20Sopenharmony_ci * Initialize RX status information, and send frame 8388c2ecf20Sopenharmony_ci * to mac80211. 8398c2ecf20Sopenharmony_ci */ 8408c2ecf20Sopenharmony_ci rx_status = IEEE80211_SKB_RXCB(entry->skb); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci /* Ensure that all fields of rx_status are initialized 8438c2ecf20Sopenharmony_ci * properly. The skb->cb array was used for driver 8448c2ecf20Sopenharmony_ci * specific informations, so rx_status might contain 8458c2ecf20Sopenharmony_ci * garbage. 8468c2ecf20Sopenharmony_ci */ 8478c2ecf20Sopenharmony_ci memset(rx_status, 0, sizeof(*rx_status)); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci rx_status->mactime = rxdesc.timestamp; 8508c2ecf20Sopenharmony_ci rx_status->band = rt2x00dev->curr_band; 8518c2ecf20Sopenharmony_ci rx_status->freq = rt2x00dev->curr_freq; 8528c2ecf20Sopenharmony_ci rx_status->rate_idx = rate_idx; 8538c2ecf20Sopenharmony_ci rx_status->signal = rxdesc.rssi; 8548c2ecf20Sopenharmony_ci rx_status->flag = rxdesc.flags; 8558c2ecf20Sopenharmony_ci rx_status->enc_flags = rxdesc.enc_flags; 8568c2ecf20Sopenharmony_ci rx_status->encoding = rxdesc.encoding; 8578c2ecf20Sopenharmony_ci rx_status->bw = rxdesc.bw; 8588c2ecf20Sopenharmony_ci rx_status->antenna = rt2x00dev->link.ant.active.rx; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci ieee80211_rx_ni(rt2x00dev->hw, entry->skb); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cirenew_skb: 8638c2ecf20Sopenharmony_ci /* 8648c2ecf20Sopenharmony_ci * Replace the skb with the freshly allocated one. 8658c2ecf20Sopenharmony_ci */ 8668c2ecf20Sopenharmony_ci entry->skb = skb; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cisubmit_entry: 8698c2ecf20Sopenharmony_ci entry->flags = 0; 8708c2ecf20Sopenharmony_ci rt2x00queue_index_inc(entry, Q_INDEX_DONE); 8718c2ecf20Sopenharmony_ci if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && 8728c2ecf20Sopenharmony_ci test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 8738c2ecf20Sopenharmony_ci rt2x00dev->ops->lib->clear_entry(entry); 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_rxdone); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci/* 8788c2ecf20Sopenharmony_ci * Driver initialization handlers. 8798c2ecf20Sopenharmony_ci */ 8808c2ecf20Sopenharmony_ciconst struct rt2x00_rate rt2x00_supported_rates[12] = { 8818c2ecf20Sopenharmony_ci { 8828c2ecf20Sopenharmony_ci .flags = DEV_RATE_CCK, 8838c2ecf20Sopenharmony_ci .bitrate = 10, 8848c2ecf20Sopenharmony_ci .ratemask = BIT(0), 8858c2ecf20Sopenharmony_ci .plcp = 0x00, 8868c2ecf20Sopenharmony_ci .mcs = RATE_MCS(RATE_MODE_CCK, 0), 8878c2ecf20Sopenharmony_ci }, 8888c2ecf20Sopenharmony_ci { 8898c2ecf20Sopenharmony_ci .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, 8908c2ecf20Sopenharmony_ci .bitrate = 20, 8918c2ecf20Sopenharmony_ci .ratemask = BIT(1), 8928c2ecf20Sopenharmony_ci .plcp = 0x01, 8938c2ecf20Sopenharmony_ci .mcs = RATE_MCS(RATE_MODE_CCK, 1), 8948c2ecf20Sopenharmony_ci }, 8958c2ecf20Sopenharmony_ci { 8968c2ecf20Sopenharmony_ci .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, 8978c2ecf20Sopenharmony_ci .bitrate = 55, 8988c2ecf20Sopenharmony_ci .ratemask = BIT(2), 8998c2ecf20Sopenharmony_ci .plcp = 0x02, 9008c2ecf20Sopenharmony_ci .mcs = RATE_MCS(RATE_MODE_CCK, 2), 9018c2ecf20Sopenharmony_ci }, 9028c2ecf20Sopenharmony_ci { 9038c2ecf20Sopenharmony_ci .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, 9048c2ecf20Sopenharmony_ci .bitrate = 110, 9058c2ecf20Sopenharmony_ci .ratemask = BIT(3), 9068c2ecf20Sopenharmony_ci .plcp = 0x03, 9078c2ecf20Sopenharmony_ci .mcs = RATE_MCS(RATE_MODE_CCK, 3), 9088c2ecf20Sopenharmony_ci }, 9098c2ecf20Sopenharmony_ci { 9108c2ecf20Sopenharmony_ci .flags = DEV_RATE_OFDM, 9118c2ecf20Sopenharmony_ci .bitrate = 60, 9128c2ecf20Sopenharmony_ci .ratemask = BIT(4), 9138c2ecf20Sopenharmony_ci .plcp = 0x0b, 9148c2ecf20Sopenharmony_ci .mcs = RATE_MCS(RATE_MODE_OFDM, 0), 9158c2ecf20Sopenharmony_ci }, 9168c2ecf20Sopenharmony_ci { 9178c2ecf20Sopenharmony_ci .flags = DEV_RATE_OFDM, 9188c2ecf20Sopenharmony_ci .bitrate = 90, 9198c2ecf20Sopenharmony_ci .ratemask = BIT(5), 9208c2ecf20Sopenharmony_ci .plcp = 0x0f, 9218c2ecf20Sopenharmony_ci .mcs = RATE_MCS(RATE_MODE_OFDM, 1), 9228c2ecf20Sopenharmony_ci }, 9238c2ecf20Sopenharmony_ci { 9248c2ecf20Sopenharmony_ci .flags = DEV_RATE_OFDM, 9258c2ecf20Sopenharmony_ci .bitrate = 120, 9268c2ecf20Sopenharmony_ci .ratemask = BIT(6), 9278c2ecf20Sopenharmony_ci .plcp = 0x0a, 9288c2ecf20Sopenharmony_ci .mcs = RATE_MCS(RATE_MODE_OFDM, 2), 9298c2ecf20Sopenharmony_ci }, 9308c2ecf20Sopenharmony_ci { 9318c2ecf20Sopenharmony_ci .flags = DEV_RATE_OFDM, 9328c2ecf20Sopenharmony_ci .bitrate = 180, 9338c2ecf20Sopenharmony_ci .ratemask = BIT(7), 9348c2ecf20Sopenharmony_ci .plcp = 0x0e, 9358c2ecf20Sopenharmony_ci .mcs = RATE_MCS(RATE_MODE_OFDM, 3), 9368c2ecf20Sopenharmony_ci }, 9378c2ecf20Sopenharmony_ci { 9388c2ecf20Sopenharmony_ci .flags = DEV_RATE_OFDM, 9398c2ecf20Sopenharmony_ci .bitrate = 240, 9408c2ecf20Sopenharmony_ci .ratemask = BIT(8), 9418c2ecf20Sopenharmony_ci .plcp = 0x09, 9428c2ecf20Sopenharmony_ci .mcs = RATE_MCS(RATE_MODE_OFDM, 4), 9438c2ecf20Sopenharmony_ci }, 9448c2ecf20Sopenharmony_ci { 9458c2ecf20Sopenharmony_ci .flags = DEV_RATE_OFDM, 9468c2ecf20Sopenharmony_ci .bitrate = 360, 9478c2ecf20Sopenharmony_ci .ratemask = BIT(9), 9488c2ecf20Sopenharmony_ci .plcp = 0x0d, 9498c2ecf20Sopenharmony_ci .mcs = RATE_MCS(RATE_MODE_OFDM, 5), 9508c2ecf20Sopenharmony_ci }, 9518c2ecf20Sopenharmony_ci { 9528c2ecf20Sopenharmony_ci .flags = DEV_RATE_OFDM, 9538c2ecf20Sopenharmony_ci .bitrate = 480, 9548c2ecf20Sopenharmony_ci .ratemask = BIT(10), 9558c2ecf20Sopenharmony_ci .plcp = 0x08, 9568c2ecf20Sopenharmony_ci .mcs = RATE_MCS(RATE_MODE_OFDM, 6), 9578c2ecf20Sopenharmony_ci }, 9588c2ecf20Sopenharmony_ci { 9598c2ecf20Sopenharmony_ci .flags = DEV_RATE_OFDM, 9608c2ecf20Sopenharmony_ci .bitrate = 540, 9618c2ecf20Sopenharmony_ci .ratemask = BIT(11), 9628c2ecf20Sopenharmony_ci .plcp = 0x0c, 9638c2ecf20Sopenharmony_ci .mcs = RATE_MCS(RATE_MODE_OFDM, 7), 9648c2ecf20Sopenharmony_ci }, 9658c2ecf20Sopenharmony_ci}; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistatic void rt2x00lib_channel(struct ieee80211_channel *entry, 9688c2ecf20Sopenharmony_ci const int channel, const int tx_power, 9698c2ecf20Sopenharmony_ci const int value) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci /* XXX: this assumption about the band is wrong for 802.11j */ 9728c2ecf20Sopenharmony_ci entry->band = channel <= 14 ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; 9738c2ecf20Sopenharmony_ci entry->center_freq = ieee80211_channel_to_frequency(channel, 9748c2ecf20Sopenharmony_ci entry->band); 9758c2ecf20Sopenharmony_ci entry->hw_value = value; 9768c2ecf20Sopenharmony_ci entry->max_power = tx_power; 9778c2ecf20Sopenharmony_ci entry->max_antenna_gain = 0xff; 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic void rt2x00lib_rate(struct ieee80211_rate *entry, 9818c2ecf20Sopenharmony_ci const u16 index, const struct rt2x00_rate *rate) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci entry->flags = 0; 9848c2ecf20Sopenharmony_ci entry->bitrate = rate->bitrate; 9858c2ecf20Sopenharmony_ci entry->hw_value = index; 9868c2ecf20Sopenharmony_ci entry->hw_value_short = index; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci if (rate->flags & DEV_RATE_SHORT_PREAMBLE) 9898c2ecf20Sopenharmony_ci entry->flags |= IEEE80211_RATE_SHORT_PREAMBLE; 9908c2ecf20Sopenharmony_ci} 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_civoid rt2x00lib_set_mac_address(struct rt2x00_dev *rt2x00dev, u8 *eeprom_mac_addr) 9938c2ecf20Sopenharmony_ci{ 9948c2ecf20Sopenharmony_ci const char *mac_addr; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci mac_addr = of_get_mac_address(rt2x00dev->dev->of_node); 9978c2ecf20Sopenharmony_ci if (!IS_ERR(mac_addr)) 9988c2ecf20Sopenharmony_ci ether_addr_copy(eeprom_mac_addr, mac_addr); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(eeprom_mac_addr)) { 10018c2ecf20Sopenharmony_ci eth_random_addr(eeprom_mac_addr); 10028c2ecf20Sopenharmony_ci rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", eeprom_mac_addr); 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_set_mac_address); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_cistatic int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev, 10088c2ecf20Sopenharmony_ci struct hw_mode_spec *spec) 10098c2ecf20Sopenharmony_ci{ 10108c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = rt2x00dev->hw; 10118c2ecf20Sopenharmony_ci struct ieee80211_channel *channels; 10128c2ecf20Sopenharmony_ci struct ieee80211_rate *rates; 10138c2ecf20Sopenharmony_ci unsigned int num_rates; 10148c2ecf20Sopenharmony_ci unsigned int i; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci num_rates = 0; 10178c2ecf20Sopenharmony_ci if (spec->supported_rates & SUPPORT_RATE_CCK) 10188c2ecf20Sopenharmony_ci num_rates += 4; 10198c2ecf20Sopenharmony_ci if (spec->supported_rates & SUPPORT_RATE_OFDM) 10208c2ecf20Sopenharmony_ci num_rates += 8; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci channels = kcalloc(spec->num_channels, sizeof(*channels), GFP_KERNEL); 10238c2ecf20Sopenharmony_ci if (!channels) 10248c2ecf20Sopenharmony_ci return -ENOMEM; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci rates = kcalloc(num_rates, sizeof(*rates), GFP_KERNEL); 10278c2ecf20Sopenharmony_ci if (!rates) 10288c2ecf20Sopenharmony_ci goto exit_free_channels; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* 10318c2ecf20Sopenharmony_ci * Initialize Rate list. 10328c2ecf20Sopenharmony_ci */ 10338c2ecf20Sopenharmony_ci for (i = 0; i < num_rates; i++) 10348c2ecf20Sopenharmony_ci rt2x00lib_rate(&rates[i], i, rt2x00_get_rate(i)); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci /* 10378c2ecf20Sopenharmony_ci * Initialize Channel list. 10388c2ecf20Sopenharmony_ci */ 10398c2ecf20Sopenharmony_ci for (i = 0; i < spec->num_channels; i++) { 10408c2ecf20Sopenharmony_ci rt2x00lib_channel(&channels[i], 10418c2ecf20Sopenharmony_ci spec->channels[i].channel, 10428c2ecf20Sopenharmony_ci spec->channels_info[i].max_power, i); 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci /* 10468c2ecf20Sopenharmony_ci * Intitialize 802.11b, 802.11g 10478c2ecf20Sopenharmony_ci * Rates: CCK, OFDM. 10488c2ecf20Sopenharmony_ci * Channels: 2.4 GHz 10498c2ecf20Sopenharmony_ci */ 10508c2ecf20Sopenharmony_ci if (spec->supported_bands & SUPPORT_BAND_2GHZ) { 10518c2ecf20Sopenharmony_ci rt2x00dev->bands[NL80211_BAND_2GHZ].n_channels = 14; 10528c2ecf20Sopenharmony_ci rt2x00dev->bands[NL80211_BAND_2GHZ].n_bitrates = num_rates; 10538c2ecf20Sopenharmony_ci rt2x00dev->bands[NL80211_BAND_2GHZ].channels = channels; 10548c2ecf20Sopenharmony_ci rt2x00dev->bands[NL80211_BAND_2GHZ].bitrates = rates; 10558c2ecf20Sopenharmony_ci hw->wiphy->bands[NL80211_BAND_2GHZ] = 10568c2ecf20Sopenharmony_ci &rt2x00dev->bands[NL80211_BAND_2GHZ]; 10578c2ecf20Sopenharmony_ci memcpy(&rt2x00dev->bands[NL80211_BAND_2GHZ].ht_cap, 10588c2ecf20Sopenharmony_ci &spec->ht, sizeof(spec->ht)); 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* 10628c2ecf20Sopenharmony_ci * Intitialize 802.11a 10638c2ecf20Sopenharmony_ci * Rates: OFDM. 10648c2ecf20Sopenharmony_ci * Channels: OFDM, UNII, HiperLAN2. 10658c2ecf20Sopenharmony_ci */ 10668c2ecf20Sopenharmony_ci if (spec->supported_bands & SUPPORT_BAND_5GHZ) { 10678c2ecf20Sopenharmony_ci rt2x00dev->bands[NL80211_BAND_5GHZ].n_channels = 10688c2ecf20Sopenharmony_ci spec->num_channels - 14; 10698c2ecf20Sopenharmony_ci rt2x00dev->bands[NL80211_BAND_5GHZ].n_bitrates = 10708c2ecf20Sopenharmony_ci num_rates - 4; 10718c2ecf20Sopenharmony_ci rt2x00dev->bands[NL80211_BAND_5GHZ].channels = &channels[14]; 10728c2ecf20Sopenharmony_ci rt2x00dev->bands[NL80211_BAND_5GHZ].bitrates = &rates[4]; 10738c2ecf20Sopenharmony_ci hw->wiphy->bands[NL80211_BAND_5GHZ] = 10748c2ecf20Sopenharmony_ci &rt2x00dev->bands[NL80211_BAND_5GHZ]; 10758c2ecf20Sopenharmony_ci memcpy(&rt2x00dev->bands[NL80211_BAND_5GHZ].ht_cap, 10768c2ecf20Sopenharmony_ci &spec->ht, sizeof(spec->ht)); 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci return 0; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci exit_free_channels: 10828c2ecf20Sopenharmony_ci kfree(channels); 10838c2ecf20Sopenharmony_ci rt2x00_err(rt2x00dev, "Allocation ieee80211 modes failed\n"); 10848c2ecf20Sopenharmony_ci return -ENOMEM; 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic void rt2x00lib_remove_hw(struct rt2x00_dev *rt2x00dev) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci if (test_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags)) 10908c2ecf20Sopenharmony_ci ieee80211_unregister_hw(rt2x00dev->hw); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (likely(rt2x00dev->hw->wiphy->bands[NL80211_BAND_2GHZ])) { 10938c2ecf20Sopenharmony_ci kfree(rt2x00dev->hw->wiphy->bands[NL80211_BAND_2GHZ]->channels); 10948c2ecf20Sopenharmony_ci kfree(rt2x00dev->hw->wiphy->bands[NL80211_BAND_2GHZ]->bitrates); 10958c2ecf20Sopenharmony_ci rt2x00dev->hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL; 10968c2ecf20Sopenharmony_ci rt2x00dev->hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci kfree(rt2x00dev->spec.channels_info); 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci struct hw_mode_spec *spec = &rt2x00dev->spec; 11058c2ecf20Sopenharmony_ci int status; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (test_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags)) 11088c2ecf20Sopenharmony_ci return 0; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci /* 11118c2ecf20Sopenharmony_ci * Initialize HW modes. 11128c2ecf20Sopenharmony_ci */ 11138c2ecf20Sopenharmony_ci status = rt2x00lib_probe_hw_modes(rt2x00dev, spec); 11148c2ecf20Sopenharmony_ci if (status) 11158c2ecf20Sopenharmony_ci return status; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci /* 11188c2ecf20Sopenharmony_ci * Initialize HW fields. 11198c2ecf20Sopenharmony_ci */ 11208c2ecf20Sopenharmony_ci rt2x00dev->hw->queues = rt2x00dev->ops->tx_queues; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci /* 11238c2ecf20Sopenharmony_ci * Initialize extra TX headroom required. 11248c2ecf20Sopenharmony_ci */ 11258c2ecf20Sopenharmony_ci rt2x00dev->hw->extra_tx_headroom = 11268c2ecf20Sopenharmony_ci max_t(unsigned int, IEEE80211_TX_STATUS_HEADROOM, 11278c2ecf20Sopenharmony_ci rt2x00dev->extra_tx_headroom); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci /* 11308c2ecf20Sopenharmony_ci * Take TX headroom required for alignment into account. 11318c2ecf20Sopenharmony_ci */ 11328c2ecf20Sopenharmony_ci if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD)) 11338c2ecf20Sopenharmony_ci rt2x00dev->hw->extra_tx_headroom += RT2X00_L2PAD_SIZE; 11348c2ecf20Sopenharmony_ci else if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA)) 11358c2ecf20Sopenharmony_ci rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci /* 11388c2ecf20Sopenharmony_ci * Tell mac80211 about the size of our private STA structure. 11398c2ecf20Sopenharmony_ci */ 11408c2ecf20Sopenharmony_ci rt2x00dev->hw->sta_data_size = sizeof(struct rt2x00_sta); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci /* 11438c2ecf20Sopenharmony_ci * Allocate tx status FIFO for driver use. 11448c2ecf20Sopenharmony_ci */ 11458c2ecf20Sopenharmony_ci if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TXSTATUS_FIFO)) { 11468c2ecf20Sopenharmony_ci /* 11478c2ecf20Sopenharmony_ci * Allocate the txstatus fifo. In the worst case the tx 11488c2ecf20Sopenharmony_ci * status fifo has to hold the tx status of all entries 11498c2ecf20Sopenharmony_ci * in all tx queues. Hence, calculate the kfifo size as 11508c2ecf20Sopenharmony_ci * tx_queues * entry_num and round up to the nearest 11518c2ecf20Sopenharmony_ci * power of 2. 11528c2ecf20Sopenharmony_ci */ 11538c2ecf20Sopenharmony_ci int kfifo_size = 11548c2ecf20Sopenharmony_ci roundup_pow_of_two(rt2x00dev->ops->tx_queues * 11558c2ecf20Sopenharmony_ci rt2x00dev->tx->limit * 11568c2ecf20Sopenharmony_ci sizeof(u32)); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size, 11598c2ecf20Sopenharmony_ci GFP_KERNEL); 11608c2ecf20Sopenharmony_ci if (status) 11618c2ecf20Sopenharmony_ci return status; 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* 11658c2ecf20Sopenharmony_ci * Initialize tasklets if used by the driver. Tasklets are 11668c2ecf20Sopenharmony_ci * disabled until the interrupts are turned on. The driver 11678c2ecf20Sopenharmony_ci * has to handle that. 11688c2ecf20Sopenharmony_ci */ 11698c2ecf20Sopenharmony_ci#define RT2X00_TASKLET_INIT(taskletname) \ 11708c2ecf20Sopenharmony_ci if (rt2x00dev->ops->lib->taskletname) { \ 11718c2ecf20Sopenharmony_ci tasklet_setup(&rt2x00dev->taskletname, \ 11728c2ecf20Sopenharmony_ci rt2x00dev->ops->lib->taskletname); \ 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci RT2X00_TASKLET_INIT(txstatus_tasklet); 11768c2ecf20Sopenharmony_ci RT2X00_TASKLET_INIT(pretbtt_tasklet); 11778c2ecf20Sopenharmony_ci RT2X00_TASKLET_INIT(tbtt_tasklet); 11788c2ecf20Sopenharmony_ci RT2X00_TASKLET_INIT(rxdone_tasklet); 11798c2ecf20Sopenharmony_ci RT2X00_TASKLET_INIT(autowake_tasklet); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci#undef RT2X00_TASKLET_INIT 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* 11848c2ecf20Sopenharmony_ci * Register HW. 11858c2ecf20Sopenharmony_ci */ 11868c2ecf20Sopenharmony_ci status = ieee80211_register_hw(rt2x00dev->hw); 11878c2ecf20Sopenharmony_ci if (status) 11888c2ecf20Sopenharmony_ci return status; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci set_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci return 0; 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci/* 11968c2ecf20Sopenharmony_ci * Initialization/uninitialization handlers. 11978c2ecf20Sopenharmony_ci */ 11988c2ecf20Sopenharmony_cistatic void rt2x00lib_uninitialize(struct rt2x00_dev *rt2x00dev) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci if (!test_and_clear_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags)) 12018c2ecf20Sopenharmony_ci return; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci /* 12048c2ecf20Sopenharmony_ci * Stop rfkill polling. 12058c2ecf20Sopenharmony_ci */ 12068c2ecf20Sopenharmony_ci if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) 12078c2ecf20Sopenharmony_ci rt2x00rfkill_unregister(rt2x00dev); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci /* 12108c2ecf20Sopenharmony_ci * Allow the HW to uninitialize. 12118c2ecf20Sopenharmony_ci */ 12128c2ecf20Sopenharmony_ci rt2x00dev->ops->lib->uninitialize(rt2x00dev); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci /* 12158c2ecf20Sopenharmony_ci * Free allocated queue entries. 12168c2ecf20Sopenharmony_ci */ 12178c2ecf20Sopenharmony_ci rt2x00queue_uninitialize(rt2x00dev); 12188c2ecf20Sopenharmony_ci} 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_cistatic int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev) 12218c2ecf20Sopenharmony_ci{ 12228c2ecf20Sopenharmony_ci int status; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci if (test_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags)) 12258c2ecf20Sopenharmony_ci return 0; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci /* 12288c2ecf20Sopenharmony_ci * Allocate all queue entries. 12298c2ecf20Sopenharmony_ci */ 12308c2ecf20Sopenharmony_ci status = rt2x00queue_initialize(rt2x00dev); 12318c2ecf20Sopenharmony_ci if (status) 12328c2ecf20Sopenharmony_ci return status; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci /* 12358c2ecf20Sopenharmony_ci * Initialize the device. 12368c2ecf20Sopenharmony_ci */ 12378c2ecf20Sopenharmony_ci status = rt2x00dev->ops->lib->initialize(rt2x00dev); 12388c2ecf20Sopenharmony_ci if (status) { 12398c2ecf20Sopenharmony_ci rt2x00queue_uninitialize(rt2x00dev); 12408c2ecf20Sopenharmony_ci return status; 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci set_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci /* 12468c2ecf20Sopenharmony_ci * Start rfkill polling. 12478c2ecf20Sopenharmony_ci */ 12488c2ecf20Sopenharmony_ci if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) 12498c2ecf20Sopenharmony_ci rt2x00rfkill_register(rt2x00dev); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci return 0; 12528c2ecf20Sopenharmony_ci} 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ciint rt2x00lib_start(struct rt2x00_dev *rt2x00dev) 12558c2ecf20Sopenharmony_ci{ 12568c2ecf20Sopenharmony_ci int retval = 0; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci /* 12598c2ecf20Sopenharmony_ci * If this is the first interface which is added, 12608c2ecf20Sopenharmony_ci * we should load the firmware now. 12618c2ecf20Sopenharmony_ci */ 12628c2ecf20Sopenharmony_ci retval = rt2x00lib_load_firmware(rt2x00dev); 12638c2ecf20Sopenharmony_ci if (retval) 12648c2ecf20Sopenharmony_ci goto out; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* 12678c2ecf20Sopenharmony_ci * Initialize the device. 12688c2ecf20Sopenharmony_ci */ 12698c2ecf20Sopenharmony_ci retval = rt2x00lib_initialize(rt2x00dev); 12708c2ecf20Sopenharmony_ci if (retval) 12718c2ecf20Sopenharmony_ci goto out; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci rt2x00dev->intf_ap_count = 0; 12748c2ecf20Sopenharmony_ci rt2x00dev->intf_sta_count = 0; 12758c2ecf20Sopenharmony_ci rt2x00dev->intf_associated = 0; 12768c2ecf20Sopenharmony_ci rt2x00dev->intf_beaconing = 0; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci /* Enable the radio */ 12798c2ecf20Sopenharmony_ci retval = rt2x00lib_enable_radio(rt2x00dev); 12808c2ecf20Sopenharmony_ci if (retval) 12818c2ecf20Sopenharmony_ci goto out; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci set_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ciout: 12868c2ecf20Sopenharmony_ci return retval; 12878c2ecf20Sopenharmony_ci} 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_civoid rt2x00lib_stop(struct rt2x00_dev *rt2x00dev) 12908c2ecf20Sopenharmony_ci{ 12918c2ecf20Sopenharmony_ci if (!test_and_clear_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) 12928c2ecf20Sopenharmony_ci return; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci /* 12958c2ecf20Sopenharmony_ci * Perhaps we can add something smarter here, 12968c2ecf20Sopenharmony_ci * but for now just disabling the radio should do. 12978c2ecf20Sopenharmony_ci */ 12988c2ecf20Sopenharmony_ci rt2x00lib_disable_radio(rt2x00dev); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci rt2x00dev->intf_ap_count = 0; 13018c2ecf20Sopenharmony_ci rt2x00dev->intf_sta_count = 0; 13028c2ecf20Sopenharmony_ci rt2x00dev->intf_associated = 0; 13038c2ecf20Sopenharmony_ci rt2x00dev->intf_beaconing = 0; 13048c2ecf20Sopenharmony_ci} 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_cistatic inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev) 13078c2ecf20Sopenharmony_ci{ 13088c2ecf20Sopenharmony_ci struct ieee80211_iface_limit *if_limit; 13098c2ecf20Sopenharmony_ci struct ieee80211_iface_combination *if_combination; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci if (rt2x00dev->ops->max_ap_intf < 2) 13128c2ecf20Sopenharmony_ci return; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci /* 13158c2ecf20Sopenharmony_ci * Build up AP interface limits structure. 13168c2ecf20Sopenharmony_ci */ 13178c2ecf20Sopenharmony_ci if_limit = &rt2x00dev->if_limits_ap; 13188c2ecf20Sopenharmony_ci if_limit->max = rt2x00dev->ops->max_ap_intf; 13198c2ecf20Sopenharmony_ci if_limit->types = BIT(NL80211_IFTYPE_AP); 13208c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH 13218c2ecf20Sopenharmony_ci if_limit->types |= BIT(NL80211_IFTYPE_MESH_POINT); 13228c2ecf20Sopenharmony_ci#endif 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci /* 13258c2ecf20Sopenharmony_ci * Build up AP interface combinations structure. 13268c2ecf20Sopenharmony_ci */ 13278c2ecf20Sopenharmony_ci if_combination = &rt2x00dev->if_combinations[IF_COMB_AP]; 13288c2ecf20Sopenharmony_ci if_combination->limits = if_limit; 13298c2ecf20Sopenharmony_ci if_combination->n_limits = 1; 13308c2ecf20Sopenharmony_ci if_combination->max_interfaces = if_limit->max; 13318c2ecf20Sopenharmony_ci if_combination->num_different_channels = 1; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci /* 13348c2ecf20Sopenharmony_ci * Finally, specify the possible combinations to mac80211. 13358c2ecf20Sopenharmony_ci */ 13368c2ecf20Sopenharmony_ci rt2x00dev->hw->wiphy->iface_combinations = rt2x00dev->if_combinations; 13378c2ecf20Sopenharmony_ci rt2x00dev->hw->wiphy->n_iface_combinations = 1; 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistatic unsigned int rt2x00dev_extra_tx_headroom(struct rt2x00_dev *rt2x00dev) 13418c2ecf20Sopenharmony_ci{ 13428c2ecf20Sopenharmony_ci if (WARN_ON(!rt2x00dev->tx)) 13438c2ecf20Sopenharmony_ci return 0; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (rt2x00_is_usb(rt2x00dev)) 13468c2ecf20Sopenharmony_ci return rt2x00dev->tx[0].winfo_size + rt2x00dev->tx[0].desc_size; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci return rt2x00dev->tx[0].winfo_size; 13498c2ecf20Sopenharmony_ci} 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci/* 13528c2ecf20Sopenharmony_ci * driver allocation handlers. 13538c2ecf20Sopenharmony_ci */ 13548c2ecf20Sopenharmony_ciint rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) 13558c2ecf20Sopenharmony_ci{ 13568c2ecf20Sopenharmony_ci int retval = -ENOMEM; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci /* 13598c2ecf20Sopenharmony_ci * Set possible interface combinations. 13608c2ecf20Sopenharmony_ci */ 13618c2ecf20Sopenharmony_ci rt2x00lib_set_if_combinations(rt2x00dev); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci /* 13648c2ecf20Sopenharmony_ci * Allocate the driver data memory, if necessary. 13658c2ecf20Sopenharmony_ci */ 13668c2ecf20Sopenharmony_ci if (rt2x00dev->ops->drv_data_size > 0) { 13678c2ecf20Sopenharmony_ci rt2x00dev->drv_data = kzalloc(rt2x00dev->ops->drv_data_size, 13688c2ecf20Sopenharmony_ci GFP_KERNEL); 13698c2ecf20Sopenharmony_ci if (!rt2x00dev->drv_data) { 13708c2ecf20Sopenharmony_ci retval = -ENOMEM; 13718c2ecf20Sopenharmony_ci goto exit; 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci spin_lock_init(&rt2x00dev->irqmask_lock); 13768c2ecf20Sopenharmony_ci mutex_init(&rt2x00dev->csr_mutex); 13778c2ecf20Sopenharmony_ci mutex_init(&rt2x00dev->conf_mutex); 13788c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rt2x00dev->bar_list); 13798c2ecf20Sopenharmony_ci spin_lock_init(&rt2x00dev->bar_list_lock); 13808c2ecf20Sopenharmony_ci hrtimer_init(&rt2x00dev->txstatus_timer, CLOCK_MONOTONIC, 13818c2ecf20Sopenharmony_ci HRTIMER_MODE_REL); 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci /* 13868c2ecf20Sopenharmony_ci * Make room for rt2x00_intf inside the per-interface 13878c2ecf20Sopenharmony_ci * structure ieee80211_vif. 13888c2ecf20Sopenharmony_ci */ 13898c2ecf20Sopenharmony_ci rt2x00dev->hw->vif_data_size = sizeof(struct rt2x00_intf); 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci /* 13928c2ecf20Sopenharmony_ci * rt2x00 devices can only use the last n bits of the MAC address 13938c2ecf20Sopenharmony_ci * for virtual interfaces. 13948c2ecf20Sopenharmony_ci */ 13958c2ecf20Sopenharmony_ci rt2x00dev->hw->wiphy->addr_mask[ETH_ALEN - 1] = 13968c2ecf20Sopenharmony_ci (rt2x00dev->ops->max_ap_intf - 1); 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci /* 13998c2ecf20Sopenharmony_ci * Initialize work. 14008c2ecf20Sopenharmony_ci */ 14018c2ecf20Sopenharmony_ci rt2x00dev->workqueue = 14028c2ecf20Sopenharmony_ci alloc_ordered_workqueue("%s", 0, wiphy_name(rt2x00dev->hw->wiphy)); 14038c2ecf20Sopenharmony_ci if (!rt2x00dev->workqueue) { 14048c2ecf20Sopenharmony_ci retval = -ENOMEM; 14058c2ecf20Sopenharmony_ci goto exit; 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); 14098c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup); 14108c2ecf20Sopenharmony_ci INIT_WORK(&rt2x00dev->sleep_work, rt2x00lib_sleep); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci /* 14138c2ecf20Sopenharmony_ci * Let the driver probe the device to detect the capabilities. 14148c2ecf20Sopenharmony_ci */ 14158c2ecf20Sopenharmony_ci retval = rt2x00dev->ops->lib->probe_hw(rt2x00dev); 14168c2ecf20Sopenharmony_ci if (retval) { 14178c2ecf20Sopenharmony_ci rt2x00_err(rt2x00dev, "Failed to allocate device\n"); 14188c2ecf20Sopenharmony_ci goto exit; 14198c2ecf20Sopenharmony_ci } 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci /* 14228c2ecf20Sopenharmony_ci * Allocate queue array. 14238c2ecf20Sopenharmony_ci */ 14248c2ecf20Sopenharmony_ci retval = rt2x00queue_allocate(rt2x00dev); 14258c2ecf20Sopenharmony_ci if (retval) 14268c2ecf20Sopenharmony_ci goto exit; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci /* Cache TX headroom value */ 14298c2ecf20Sopenharmony_ci rt2x00dev->extra_tx_headroom = rt2x00dev_extra_tx_headroom(rt2x00dev); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci /* 14328c2ecf20Sopenharmony_ci * Determine which operating modes are supported, all modes 14338c2ecf20Sopenharmony_ci * which require beaconing, depend on the availability of 14348c2ecf20Sopenharmony_ci * beacon entries. 14358c2ecf20Sopenharmony_ci */ 14368c2ecf20Sopenharmony_ci rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); 14378c2ecf20Sopenharmony_ci if (rt2x00dev->bcn->limit > 0) 14388c2ecf20Sopenharmony_ci rt2x00dev->hw->wiphy->interface_modes |= 14398c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_ADHOC) | 14408c2ecf20Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH 14418c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_MESH_POINT) | 14428c2ecf20Sopenharmony_ci#endif 14438c2ecf20Sopenharmony_ci#ifdef CONFIG_WIRELESS_WDS 14448c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_WDS) | 14458c2ecf20Sopenharmony_ci#endif 14468c2ecf20Sopenharmony_ci BIT(NL80211_IFTYPE_AP); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci wiphy_ext_feature_set(rt2x00dev->hw->wiphy, 14518c2ecf20Sopenharmony_ci NL80211_EXT_FEATURE_CQM_RSSI_LIST); 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci /* 14548c2ecf20Sopenharmony_ci * Initialize ieee80211 structure. 14558c2ecf20Sopenharmony_ci */ 14568c2ecf20Sopenharmony_ci retval = rt2x00lib_probe_hw(rt2x00dev); 14578c2ecf20Sopenharmony_ci if (retval) { 14588c2ecf20Sopenharmony_ci rt2x00_err(rt2x00dev, "Failed to initialize hw\n"); 14598c2ecf20Sopenharmony_ci goto exit; 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci /* 14638c2ecf20Sopenharmony_ci * Register extra components. 14648c2ecf20Sopenharmony_ci */ 14658c2ecf20Sopenharmony_ci rt2x00link_register(rt2x00dev); 14668c2ecf20Sopenharmony_ci rt2x00leds_register(rt2x00dev); 14678c2ecf20Sopenharmony_ci rt2x00debug_register(rt2x00dev); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci /* 14708c2ecf20Sopenharmony_ci * Start rfkill polling. 14718c2ecf20Sopenharmony_ci */ 14728c2ecf20Sopenharmony_ci if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) 14738c2ecf20Sopenharmony_ci rt2x00rfkill_register(rt2x00dev); 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci return 0; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ciexit: 14788c2ecf20Sopenharmony_ci rt2x00lib_remove_dev(rt2x00dev); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci return retval; 14818c2ecf20Sopenharmony_ci} 14828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_probe_dev); 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_civoid rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) 14858c2ecf20Sopenharmony_ci{ 14868c2ecf20Sopenharmony_ci clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci /* 14898c2ecf20Sopenharmony_ci * Stop rfkill polling. 14908c2ecf20Sopenharmony_ci */ 14918c2ecf20Sopenharmony_ci if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) 14928c2ecf20Sopenharmony_ci rt2x00rfkill_unregister(rt2x00dev); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci /* 14958c2ecf20Sopenharmony_ci * Disable radio. 14968c2ecf20Sopenharmony_ci */ 14978c2ecf20Sopenharmony_ci rt2x00lib_disable_radio(rt2x00dev); 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci /* 15008c2ecf20Sopenharmony_ci * Stop all work. 15018c2ecf20Sopenharmony_ci */ 15028c2ecf20Sopenharmony_ci cancel_work_sync(&rt2x00dev->intf_work); 15038c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); 15048c2ecf20Sopenharmony_ci cancel_work_sync(&rt2x00dev->sleep_work); 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci hrtimer_cancel(&rt2x00dev->txstatus_timer); 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci /* 15098c2ecf20Sopenharmony_ci * Kill the tx status tasklet. 15108c2ecf20Sopenharmony_ci */ 15118c2ecf20Sopenharmony_ci tasklet_kill(&rt2x00dev->txstatus_tasklet); 15128c2ecf20Sopenharmony_ci tasklet_kill(&rt2x00dev->pretbtt_tasklet); 15138c2ecf20Sopenharmony_ci tasklet_kill(&rt2x00dev->tbtt_tasklet); 15148c2ecf20Sopenharmony_ci tasklet_kill(&rt2x00dev->rxdone_tasklet); 15158c2ecf20Sopenharmony_ci tasklet_kill(&rt2x00dev->autowake_tasklet); 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci /* 15188c2ecf20Sopenharmony_ci * Uninitialize device. 15198c2ecf20Sopenharmony_ci */ 15208c2ecf20Sopenharmony_ci rt2x00lib_uninitialize(rt2x00dev); 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci if (rt2x00dev->workqueue) 15238c2ecf20Sopenharmony_ci destroy_workqueue(rt2x00dev->workqueue); 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci /* 15268c2ecf20Sopenharmony_ci * Free the tx status fifo. 15278c2ecf20Sopenharmony_ci */ 15288c2ecf20Sopenharmony_ci kfifo_free(&rt2x00dev->txstatus_fifo); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci /* 15318c2ecf20Sopenharmony_ci * Free extra components 15328c2ecf20Sopenharmony_ci */ 15338c2ecf20Sopenharmony_ci rt2x00debug_deregister(rt2x00dev); 15348c2ecf20Sopenharmony_ci rt2x00leds_unregister(rt2x00dev); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* 15378c2ecf20Sopenharmony_ci * Free ieee80211_hw memory. 15388c2ecf20Sopenharmony_ci */ 15398c2ecf20Sopenharmony_ci rt2x00lib_remove_hw(rt2x00dev); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci /* 15428c2ecf20Sopenharmony_ci * Free firmware image. 15438c2ecf20Sopenharmony_ci */ 15448c2ecf20Sopenharmony_ci rt2x00lib_free_firmware(rt2x00dev); 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci /* 15478c2ecf20Sopenharmony_ci * Free queue structures. 15488c2ecf20Sopenharmony_ci */ 15498c2ecf20Sopenharmony_ci rt2x00queue_free(rt2x00dev); 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci /* 15528c2ecf20Sopenharmony_ci * Free the driver data. 15538c2ecf20Sopenharmony_ci */ 15548c2ecf20Sopenharmony_ci kfree(rt2x00dev->drv_data); 15558c2ecf20Sopenharmony_ci} 15568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_remove_dev); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci/* 15598c2ecf20Sopenharmony_ci * Device state handlers 15608c2ecf20Sopenharmony_ci */ 15618c2ecf20Sopenharmony_ciint rt2x00lib_suspend(struct rt2x00_dev *rt2x00dev) 15628c2ecf20Sopenharmony_ci{ 15638c2ecf20Sopenharmony_ci rt2x00_dbg(rt2x00dev, "Going to sleep\n"); 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci /* 15668c2ecf20Sopenharmony_ci * Prevent mac80211 from accessing driver while suspended. 15678c2ecf20Sopenharmony_ci */ 15688c2ecf20Sopenharmony_ci if (!test_and_clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) 15698c2ecf20Sopenharmony_ci return 0; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci /* 15728c2ecf20Sopenharmony_ci * Cleanup as much as possible. 15738c2ecf20Sopenharmony_ci */ 15748c2ecf20Sopenharmony_ci rt2x00lib_uninitialize(rt2x00dev); 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci /* 15778c2ecf20Sopenharmony_ci * Suspend/disable extra components. 15788c2ecf20Sopenharmony_ci */ 15798c2ecf20Sopenharmony_ci rt2x00leds_suspend(rt2x00dev); 15808c2ecf20Sopenharmony_ci rt2x00debug_deregister(rt2x00dev); 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci /* 15838c2ecf20Sopenharmony_ci * Set device mode to sleep for power management, 15848c2ecf20Sopenharmony_ci * on some hardware this call seems to consistently fail. 15858c2ecf20Sopenharmony_ci * From the specifications it is hard to tell why it fails, 15868c2ecf20Sopenharmony_ci * and if this is a "bad thing". 15878c2ecf20Sopenharmony_ci * Overall it is safe to just ignore the failure and 15888c2ecf20Sopenharmony_ci * continue suspending. The only downside is that the 15898c2ecf20Sopenharmony_ci * device will not be in optimal power save mode, but with 15908c2ecf20Sopenharmony_ci * the radio and the other components already disabled the 15918c2ecf20Sopenharmony_ci * device is as good as disabled. 15928c2ecf20Sopenharmony_ci */ 15938c2ecf20Sopenharmony_ci if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_SLEEP)) 15948c2ecf20Sopenharmony_ci rt2x00_warn(rt2x00dev, "Device failed to enter sleep state, continue suspending\n"); 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci return 0; 15978c2ecf20Sopenharmony_ci} 15988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_suspend); 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ciint rt2x00lib_resume(struct rt2x00_dev *rt2x00dev) 16018c2ecf20Sopenharmony_ci{ 16028c2ecf20Sopenharmony_ci rt2x00_dbg(rt2x00dev, "Waking up\n"); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci /* 16058c2ecf20Sopenharmony_ci * Restore/enable extra components. 16068c2ecf20Sopenharmony_ci */ 16078c2ecf20Sopenharmony_ci rt2x00debug_register(rt2x00dev); 16088c2ecf20Sopenharmony_ci rt2x00leds_resume(rt2x00dev); 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci /* 16118c2ecf20Sopenharmony_ci * We are ready again to receive requests from mac80211. 16128c2ecf20Sopenharmony_ci */ 16138c2ecf20Sopenharmony_ci set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci return 0; 16168c2ecf20Sopenharmony_ci} 16178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00lib_resume); 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci/* 16208c2ecf20Sopenharmony_ci * rt2x00lib module information. 16218c2ecf20Sopenharmony_ci */ 16228c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRV_PROJECT); 16238c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 16248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("rt2x00 library"); 16258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1626