summaryrefslogtreecommitdiff
path: root/net/mac802154/util.c
blob: f08605f59b609a548512cabaf6b845f86235bd8d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// SPDX-License-Identifier: GPL-2.0-only
/*
 *
 * Authors:
 * Alexander Aring <aar@pengutronix.de>
 *
 * Based on: net/mac80211/util.c
 */

#include "ieee802154_i.h"
#include "driver-ops.h"

/* privid for wpan_phys to determine whether they belong to us or not */
const void *const mac802154_wpan_phy_privid = &mac802154_wpan_phy_privid;

/**
 * ieee802154_wake_queue - wake ieee802154 queue
 * @local: main mac object
 *
 * Tranceivers usually have either one transmit framebuffer or one framebuffer
 * for both transmitting and receiving. Hence, the core currently only handles
 * one frame at a time for each phy, which means we had to stop the queue to
 * avoid new skb to come during the transmission. The queue then needs to be
 * woken up after the operation.
 */
static void ieee802154_wake_queue(struct ieee802154_hw *hw)
{
	struct ieee802154_local *local = hw_to_local(hw);
	struct ieee802154_sub_if_data *sdata;

	rcu_read_lock();
	clear_bit(WPAN_PHY_FLAG_STATE_QUEUE_STOPPED, &local->phy->flags);
	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
		if (!sdata->dev)
			continue;

		netif_wake_queue(sdata->dev);
	}
	rcu_read_unlock();
}

/**
 * ieee802154_stop_queue - stop ieee802154 queue
 * @local: main mac object
 *
 * Tranceivers usually have either one transmit framebuffer or one framebuffer
 * for both transmitting and receiving. Hence, the core currently only handles
 * one frame at a time for each phy, which means we need to tell upper layers to
 * stop giving us new skbs while we are busy with the transmitted one. The queue
 * must then be stopped before transmitting.
 */
static void ieee802154_stop_queue(struct ieee802154_hw *hw)
{
	struct ieee802154_local *local = hw_to_local(hw);
	struct ieee802154_sub_if_data *sdata;

	rcu_read_lock();
	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
		if (!sdata->dev)
			continue;

		netif_stop_queue(sdata->dev);
	}
	rcu_read_unlock();
}

void ieee802154_hold_queue(struct ieee802154_local *local)
{
	unsigned long flags;

	spin_lock_irqsave(&local->phy->queue_lock, flags);
	if (!atomic_fetch_inc(&local->phy->hold_txs))
		ieee802154_stop_queue(&local->hw);
	spin_unlock_irqrestore(&local->phy->queue_lock, flags);
}

void ieee802154_release_queue(struct ieee802154_local *local)
{
	unsigned long flags;

	spin_lock_irqsave(&local->phy->queue_lock, flags);
	if (atomic_dec_and_test(&local->phy->hold_txs))
		ieee802154_wake_queue(&local->hw);
	spin_unlock_irqrestore(&local->phy->queue_lock, flags);
}

void ieee802154_disable_queue(struct ieee802154_local *local)
{
	struct ieee802154_sub_if_data *sdata;

	rcu_read_lock();
	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
		if (!sdata->dev)
			continue;

		netif_tx_disable(sdata->dev);
	}
	rcu_read_unlock();
}

enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer)
{
	struct ieee802154_local *local =
		container_of(timer, struct ieee802154_local, ifs_timer);

	ieee802154_release_queue(local);

	return HRTIMER_NORESTART;
}

void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb,
			      bool ifs_handling)
{
	struct ieee802154_local *local = hw_to_local(hw);

	local->tx_result = IEEE802154_SUCCESS;

	if (ifs_handling) {
		u8 max_sifs_size;

		/* If transceiver sets CRC on his own we need to use lifs
		 * threshold len above 16 otherwise 18, because it's not
		 * part of skb->len.
		 */
		if (hw->flags & IEEE802154_HW_TX_OMIT_CKSUM)
			max_sifs_size = IEEE802154_MAX_SIFS_FRAME_SIZE -
					IEEE802154_FCS_LEN;
		else
			max_sifs_size = IEEE802154_MAX_SIFS_FRAME_SIZE;

		if (skb->len > max_sifs_size)
			hrtimer_start(&local->ifs_timer,
				      hw->phy->lifs_period * NSEC_PER_USEC,
				      HRTIMER_MODE_REL);
		else
			hrtimer_start(&local->ifs_timer,
				      hw->phy->sifs_period * NSEC_PER_USEC,
				      HRTIMER_MODE_REL);
	} else {
		ieee802154_release_queue(local);
	}

	dev_consume_skb_any(skb);
	if (atomic_dec_and_test(&hw->phy->ongoing_txs))
		wake_up(&hw->phy->sync_txq);
}
EXPORT_SYMBOL(ieee802154_xmit_complete);

void ieee802154_xmit_error(struct ieee802154_hw *hw, struct sk_buff *skb,
			   int reason)
{
	struct ieee802154_local *local = hw_to_local(hw);

	local->tx_result = reason;
	ieee802154_release_queue(local);
	dev_kfree_skb_any(skb);
	if (atomic_dec_and_test(&hw->phy->ongoing_txs))
		wake_up(&hw->phy->sync_txq);
}
EXPORT_SYMBOL(ieee802154_xmit_error);

void ieee802154_xmit_hw_error(struct ieee802154_hw *hw, struct sk_buff *skb)
{
	ieee802154_xmit_error(hw, skb, IEEE802154_SYSTEM_ERROR);
}
EXPORT_SYMBOL(ieee802154_xmit_hw_error);

void ieee802154_stop_device(struct ieee802154_local *local)
{
	flush_workqueue(local->workqueue);
	hrtimer_cancel(&local->ifs_timer);
	drv_stop(local);
}