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