File Index
Files and directories
Sign in
FILE

txrx.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/marvell/mwifiex/txrx.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * NXP Wireless LAN device driver: generic TX/RX data handling
  4 *
  5 * Copyright 2011-2020 NXP
  6 */
  7
  8#include "decl.h"
  9#include "ioctl.h"
 10#include "util.h"
 11#include "fw.h"
 12#include "main.h"
 13#include "wmm.h"
 14
 15/*
 16 * This function processes the received buffer.
 17 *
 18 * Main responsibility of this function is to parse the RxPD to
 19 * identify the correct interface this packet is headed for and
 20 * forwarding it to the associated handling function, where the
 21 * packet will be further processed and sent to kernel/upper layer
 22 * if required.
 23 */
 24int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter,
 25			     struct sk_buff *skb)
 26{
 27	struct mwifiex_private *priv;
 28	struct rxpd *local_rx_pd;
 29	struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
 30	int ret;
 31
 32	local_rx_pd = (struct rxpd *) (skb->data);
 33	/* Get the BSS number from rxpd, get corresponding priv */
 34	priv = mwifiex_get_priv_by_id(adapter, local_rx_pd->bss_num &
 35				      BSS_NUM_MASK, local_rx_pd->bss_type);
 36	if (!priv)
 37		priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
 38
 39	if (!priv) {
 40		mwifiex_dbg(adapter, ERROR,
 41			    "data: priv not found. Drop RX packet\n");
 42		dev_kfree_skb_any(skb);
 43		return -1;
 44	}
 45
 46	mwifiex_dbg_dump(adapter, DAT_D, "rx pkt:", skb->data,
 47			 min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN));
 48
 49	memset(rx_info, 0, sizeof(*rx_info));
 50	rx_info->bss_num = priv->bss_num;
 51	rx_info->bss_type = priv->bss_type;
 52
 53	if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
 54		ret = mwifiex_process_uap_rx_packet(priv, skb);
 55	else
 56		ret = mwifiex_process_sta_rx_packet(priv, skb);
 57
 58	return ret;
 59}
 60EXPORT_SYMBOL_GPL(mwifiex_handle_rx_packet);
 61
 62/*
 63 * This function sends a packet to device.
 64 *
 65 * It processes the packet to add the TxPD, checks condition and
 66 * sends the processed packet to firmware for transmission.
 67 *
 68 * On successful completion, the function calls the completion callback
 69 * and logs the time.
 70 */
 71int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb,
 72		       struct mwifiex_tx_param *tx_param)
 73{
 74	int hroom, ret;
 75	struct mwifiex_adapter *adapter = priv->adapter;
 76	struct txpd *local_tx_pd = NULL;
 77	struct mwifiex_sta_node *dest_node;
 78	struct ethhdr *hdr = (void *)skb->data;
 79
 80	if (unlikely(!skb->len ||
 81		     skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN)) {
 82		ret = -EINVAL;
 83		goto out;
 84	}
 85
 86	hroom = adapter->intf_hdr_len;
 87
 88	if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) {
 89		dest_node = mwifiex_get_sta_entry(priv, hdr->h_dest);
 90		if (dest_node) {
 91			dest_node->stats.tx_bytes += skb->len;
 92			dest_node->stats.tx_packets++;
 93		}
 94
 95		mwifiex_process_uap_txpd(priv, skb);
 96	} else {
 97		mwifiex_process_sta_txpd(priv, skb);
 98	}
 99
100	if (adapter->data_sent || adapter->tx_lock_flag) {
101		skb_queue_tail(&adapter->tx_data_q, skb);
102		atomic_inc(&adapter->tx_queued);
103		return 0;
104	}
105
106	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)
107		local_tx_pd = (struct txpd *)(skb->data + hroom);
108	if (adapter->iface_type == MWIFIEX_USB) {
109		ret = adapter->if_ops.host_to_card(adapter,
110						   priv->usb_port,
111						   skb, tx_param);
112	} else {
113		ret = adapter->if_ops.host_to_card(adapter,
114						   MWIFIEX_TYPE_DATA,
115						   skb, tx_param);
116	}
117	mwifiex_dbg_dump(adapter, DAT_D, "tx pkt:", skb->data,
118			 min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN));
119out:
120	switch (ret) {
121	case -ENOSR:
122		mwifiex_dbg(adapter, DATA, "data: -ENOSR is returned\n");
123		break;
124	case -EBUSY:
125		if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
126		    (adapter->pps_uapsd_mode) && (adapter->tx_lock_flag)) {
127				priv->adapter->tx_lock_flag = false;
128				if (local_tx_pd)
129					local_tx_pd->flags = 0;
130		}
131		mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
132		break;
133	case -1:
134		mwifiex_dbg(adapter, ERROR,
135			    "mwifiex_write_data_async failed: 0x%X\n",
136			    ret);
137		adapter->dbg.num_tx_host_to_card_failure++;
138		mwifiex_write_data_complete(adapter, skb, 0, ret);
139		break;
140	case -EINPROGRESS:
141		break;
142	case -EINVAL:
143		mwifiex_dbg(adapter, ERROR,
144			    "malformed skb (length: %u, headroom: %u)\n",
145			    skb->len, skb_headroom(skb));
146		fallthrough;
147	case 0:
148		mwifiex_write_data_complete(adapter, skb, 0, ret);
149		break;
150	default:
151		break;
152	}
153
154	return ret;
155}
156
157static int mwifiex_host_to_card(struct mwifiex_adapter *adapter,
158				struct sk_buff *skb,
159				struct mwifiex_tx_param *tx_param)
160{
161	struct txpd *local_tx_pd = NULL;
162	u8 *head_ptr = skb->data;
163	int ret = 0;
164	struct mwifiex_private *priv;
165	struct mwifiex_txinfo *tx_info;
166
167	tx_info = MWIFIEX_SKB_TXCB(skb);
168	priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num,
169				      tx_info->bss_type);
170	if (!priv) {
171		mwifiex_dbg(adapter, ERROR,
172			    "data: priv not found. Drop TX packet\n");
173		adapter->dbg.num_tx_host_to_card_failure++;
174		mwifiex_write_data_complete(adapter, skb, 0, 0);
175		return ret;
176	}
177	if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)
178		local_tx_pd = (struct txpd *)(head_ptr + adapter->intf_hdr_len);
179
180	if (adapter->iface_type == MWIFIEX_USB) {
181		ret = adapter->if_ops.host_to_card(adapter,
182						   priv->usb_port,
183						   skb, tx_param);
184	} else {
185		ret = adapter->if_ops.host_to_card(adapter,
186						   MWIFIEX_TYPE_DATA,
187						   skb, tx_param);
188	}
189	switch (ret) {
190	case -ENOSR:
191		mwifiex_dbg(adapter, ERROR, "data: -ENOSR is returned\n");
192		break;
193	case -EBUSY:
194		if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
195		    (adapter->pps_uapsd_mode) &&
196		    (adapter->tx_lock_flag)) {
197			priv->adapter->tx_lock_flag = false;
198			if (local_tx_pd)
199				local_tx_pd->flags = 0;
200		}
201		skb_queue_head(&adapter->tx_data_q, skb);
202		if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT)
203			atomic_add(tx_info->aggr_num, &adapter->tx_queued);
204		else
205			atomic_inc(&adapter->tx_queued);
206		mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
207		break;
208	case -1:
209		mwifiex_dbg(adapter, ERROR,
210			    "mwifiex_write_data_async failed: 0x%X\n", ret);
211		adapter->dbg.num_tx_host_to_card_failure++;
212		mwifiex_write_data_complete(adapter, skb, 0, ret);
213		break;
214	case -EINPROGRESS:
215		break;
216	case 0:
217		mwifiex_write_data_complete(adapter, skb, 0, ret);
218		break;
219	default:
220		break;
221	}
222	return ret;
223}
224
225static int
226mwifiex_dequeue_tx_queue(struct mwifiex_adapter *adapter)
227{
228	struct sk_buff *skb, *skb_next;
229	struct mwifiex_txinfo *tx_info;
230	struct mwifiex_tx_param tx_param;
231
232	skb = skb_dequeue(&adapter->tx_data_q);
233	if (!skb)
234		return -1;
235
236	tx_info = MWIFIEX_SKB_TXCB(skb);
237	if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT)
238		atomic_sub(tx_info->aggr_num, &adapter->tx_queued);
239	else
240		atomic_dec(&adapter->tx_queued);
241
242	if (!skb_queue_empty(&adapter->tx_data_q))
243		skb_next = skb_peek(&adapter->tx_data_q);
244	else
245		skb_next = NULL;
246	tx_param.next_pkt_len = ((skb_next) ? skb_next->len : 0);
247	if (!tx_param.next_pkt_len) {
248		if (!mwifiex_wmm_lists_empty(adapter))
249			tx_param.next_pkt_len = 1;
250	}
251	return mwifiex_host_to_card(adapter, skb, &tx_param);
252}
253
254void
255mwifiex_process_tx_queue(struct mwifiex_adapter *adapter)
256{
257	do {
258		if (adapter->data_sent || adapter->tx_lock_flag)
259			break;
260		if (mwifiex_dequeue_tx_queue(adapter))
261			break;
262	} while (!skb_queue_empty(&adapter->tx_data_q));
263}
264
265/*
266 * Packet send completion callback handler.
267 *
268 * It either frees the buffer directly or forwards it to another
269 * completion callback which checks conditions, updates statistics,
270 * wakes up stalled traffic queue if required, and then frees the buffer.
271 */
272int mwifiex_write_data_complete(struct mwifiex_adapter *adapter,
273				struct sk_buff *skb, int aggr, int status)
274{
275	struct mwifiex_private *priv;
276	struct mwifiex_txinfo *tx_info;
277	struct netdev_queue *txq;
278	int index;
279
280	if (!skb)
281		return 0;
282
283	tx_info = MWIFIEX_SKB_TXCB(skb);
284	priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num,
285				      tx_info->bss_type);
286	if (!priv)
287		goto done;
288
289	mwifiex_set_trans_start(priv->netdev);
290
291	if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT)
292		atomic_dec_return(&adapter->pending_bridged_pkts);
293
294	if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT)
295		goto done;
296
297	if (!status) {
298		priv->stats.tx_packets++;
299		priv->stats.tx_bytes += tx_info->pkt_len;
300		if (priv->tx_timeout_cnt)
301			priv->tx_timeout_cnt = 0;
302	} else {
303		priv->stats.tx_errors++;
304	}
305
306	if (aggr)
307		/* For skb_aggr, do not wake up tx queue */
308		goto done;
309
310	atomic_dec(&adapter->tx_pending);
311
312	index = mwifiex_1d_to_wmm_queue[skb->priority];
313	if (atomic_dec_return(&priv->wmm_tx_pending[index]) < LOW_TX_PENDING) {
314		txq = netdev_get_tx_queue(priv->netdev, index);
315		if (netif_tx_queue_stopped(txq)) {
316			netif_tx_wake_queue(txq);
317			mwifiex_dbg(adapter, DATA, "wake queue: %d\n", index);
318		}
319	}
320done:
321	dev_kfree_skb_any(skb);
322
323	return 0;
324}
325EXPORT_SYMBOL_GPL(mwifiex_write_data_complete);
326
327void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
328				   void *event_body)
329{
330	struct tx_status_event *tx_status = (void *)priv->adapter->event_body;
331	struct sk_buff *ack_skb;
332	struct mwifiex_txinfo *tx_info;
333
334	if (!tx_status->tx_token_id)
335		return;
336
337	spin_lock_bh(&priv->ack_status_lock);
338	ack_skb = idr_remove(&priv->ack_status_frames, tx_status->tx_token_id);
339	spin_unlock_bh(&priv->ack_status_lock);
340
341	if (ack_skb) {
342		tx_info = MWIFIEX_SKB_TXCB(ack_skb);
343
344		if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS) {
345			/* consumes ack_skb */
346			skb_complete_wifi_ack(ack_skb, !tx_status->status);
347		} else {
348			/* Remove broadcast address which was added by driver */
349			memmove(ack_skb->data +
350				sizeof(struct ieee80211_hdr_3addr) +
351				MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16),
352				ack_skb->data +
353				sizeof(struct ieee80211_hdr_3addr) +
354				MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16) +
355				ETH_ALEN, ack_skb->len -
356				(sizeof(struct ieee80211_hdr_3addr) +
357				MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16) +
358				ETH_ALEN));
359			ack_skb->len = ack_skb->len - ETH_ALEN;
360			/* Remove driver's proprietary header including 2 bytes
361			 * of packet length and pass actual management frame buffer
362			 * to cfg80211.
363			 */
364			cfg80211_mgmt_tx_status(&priv->wdev, tx_info->cookie,
365						ack_skb->data +
366						MWIFIEX_MGMT_FRAME_HEADER_SIZE +
367						sizeof(u16), ack_skb->len -
368						(MWIFIEX_MGMT_FRAME_HEADER_SIZE
369						 + sizeof(u16)),
370						!tx_status->status, GFP_ATOMIC);
371			dev_kfree_skb_any(ack_skb);
372		}
373	}
374}
~/ Read-only file preview.