tx.c 5.4 KB
Newer Older
1 2 3 4
/**
  * This file contains the handling of TX in wlan driver.
  */
#include <linux/netdevice.h>
5
#include <linux/etherdevice.h>
6
#include <linux/sched.h>
K
Kiran Divekar 已提交
7
#include <net/cfg80211.h>
8

9
#include "host.h"
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
#include "radiotap.h"
#include "decl.h"
#include "defs.h"
#include "dev.h"

/**
 *  @brief This function converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE
 *  units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1)
 *
 *  @param rate    Input rate
 *  @return      Output Rate (0 if invalid)
 */
static u32 convert_radiotap_rate_to_mv(u8 rate)
{
	switch (rate) {
	case 2:		/*   1 Mbps */
		return 0 | (1 << 4);
	case 4:		/*   2 Mbps */
		return 1 | (1 << 4);
	case 11:		/* 5.5 Mbps */
		return 2 | (1 << 4);
	case 22:		/*  11 Mbps */
		return 3 | (1 << 4);
	case 12:		/*   6 Mbps */
		return 4 | (1 << 4);
	case 18:		/*   9 Mbps */
		return 5 | (1 << 4);
	case 24:		/*  12 Mbps */
		return 6 | (1 << 4);
	case 36:		/*  18 Mbps */
		return 7 | (1 << 4);
	case 48:		/*  24 Mbps */
		return 8 | (1 << 4);
	case 72:		/*  36 Mbps */
		return 9 | (1 << 4);
	case 96:		/*  48 Mbps */
		return 10 | (1 << 4);
	case 108:		/*  54 Mbps */
		return 11 | (1 << 4);
	}
	return 0;
}

/**
54 55
 *  @brief This function checks the conditions and sends packet to IF
 *  layer if everything is ok.
56
 *
57
 *  @param priv    A pointer to struct lbs_private structure
58 59 60
 *  @param skb     A pointer to skb which includes TX packet
 *  @return 	   0 or -1
 */
61
netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
62
{
63
	unsigned long flags;
64
	struct lbs_private *priv = dev->ml_priv;
65 66 67
	struct txpd *txpd;
	char *p802x_hdr;
	uint16_t pkt_len;
68
	netdev_tx_t ret = NETDEV_TX_OK;
69

70
	lbs_deb_enter(LBS_DEB_TX);
71

72 73 74
	/* We need to protect against the queues being restarted before
	   we get round to stopping them */
	spin_lock_irqsave(&priv->driver_lock, flags);
75

76
	if (priv->surpriseremoved)
77
		goto free;
78 79

	if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) {
80
		lbs_deb_tx("tx err: skb length %d 0 or > %zd\n",
81
		       skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE);
82
		/* We'll never manage to send this one; drop it and return 'OK' */
83

84 85
		dev->stats.tx_dropped++;
		dev->stats.tx_errors++;
86 87 88 89 90 91 92 93 94
		goto free;
	}


	netif_stop_queue(priv->dev);
	if (priv->mesh_dev)
		netif_stop_queue(priv->mesh_dev);

	if (priv->tx_pending_len) {
95 96
		/* This can happen if packets come in on the mesh and eth
		   device simultaneously -- there's no mutual exclusion on
97 98 99 100
		   hard_start_xmit() calls between devices. */
		lbs_deb_tx("Packet on %s while busy\n", dev->name);
		ret = NETDEV_TX_BUSY;
		goto unlock;
101 102
	}

103 104 105
	priv->tx_pending_len = -1;
	spin_unlock_irqrestore(&priv->driver_lock, flags);

106
	lbs_deb_hex(LBS_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100));
107

108
	txpd = (void *)priv->tx_pending_buf;
109
	memset(txpd, 0, sizeof(struct txpd));
110 111

	p802x_hdr = skb->data;
112
	pkt_len = skb->len;
113

K
Kiran Divekar 已提交
114
	if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
115
		struct tx_radiotap_hdr *rtap_hdr = (void *)skb->data;
116 117

		/* set txpd fields from the radiotap header */
118
		txpd->tx_control = cpu_to_le32(convert_radiotap_rate_to_mv(rtap_hdr->rate));
119 120

		/* skip the radiotap header */
121 122
		p802x_hdr += sizeof(*rtap_hdr);
		pkt_len -= sizeof(*rtap_hdr);
123

124 125 126 127 128
		/* copy destination address from 802.11 header */
		memcpy(txpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN);
	} else {
		/* copy destination address from 802.3 header */
		memcpy(txpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN);
129 130
	}

131 132
	txpd->tx_packet_length = cpu_to_le16(pkt_len);
	txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd));
133

134
	lbs_mesh_set_txpd(priv, dev, txpd);
135

136
	lbs_deb_hex(LBS_DEB_TX, "txpd", (u8 *) &txpd, sizeof(struct txpd));
137

138
	lbs_deb_hex(LBS_DEB_TX, "Tx Data", (u8 *) p802x_hdr, le16_to_cpu(txpd->tx_packet_length));
139

140
	memcpy(&txpd[1], p802x_hdr, le16_to_cpu(txpd->tx_packet_length));
141

142
	spin_lock_irqsave(&priv->driver_lock, flags);
143
	priv->tx_pending_len = pkt_len + sizeof(struct txpd);
144

145
	lbs_deb_tx("%s lined up packet\n", __func__);
146

147 148
	dev->stats.tx_packets++;
	dev->stats.tx_bytes += skb->len;
149

K
Kiran Divekar 已提交
150
	if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
151 152 153
		/* Keep the skb to echo it back once Tx feedback is
		   received from FW */
		skb_orphan(skb);
D
David Woodhouse 已提交
154

155 156 157 158
		/* Keep the skb around for when we get feedback */
		priv->currenttxskb = skb;
	} else {
 free:
159 160
		dev_kfree_skb_any(skb);
	}
K
Kiran Divekar 已提交
161

162 163 164
 unlock:
	spin_unlock_irqrestore(&priv->driver_lock, flags);
	wake_up(&priv->waitq);
165

166
	lbs_deb_leave_args(LBS_DEB_TX, "ret %d", ret);
167 168 169 170 171 172 173
	return ret;
}

/**
 *  @brief This function sends to the host the last transmitted packet,
 *  filling the radiotap headers with transmission information.
 *
174
 *  @param priv     A pointer to struct lbs_private structure
175 176 177 178
 *  @param status   A 32 bit value containing transmission status.
 *
 *  @returns void
 */
179
void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count)
180 181 182
{
	struct tx_radiotap_hdr *radiotap_hdr;

K
Kiran Divekar 已提交
183 184
	if (!priv->wdev->iftype == NL80211_IFTYPE_MONITOR ||
	    priv->currenttxskb == NULL)
185 186
		return;

187
	radiotap_hdr = (struct tx_radiotap_hdr *)priv->currenttxskb->data;
188

189 190
	radiotap_hdr->data_retries = try_count ?
		(1 + priv->txretrycount - try_count) : 0;
191 192

	priv->currenttxskb->protocol = eth_type_trans(priv->currenttxskb,
K
Kiran Divekar 已提交
193
						      priv->dev);
194 195
	netif_rx(priv->currenttxskb);

196
	priv->currenttxskb = NULL;
197

198
	if (priv->connect_status == LBS_CONNECTED)
199
		netif_wake_queue(priv->dev);
200

201
	if (priv->mesh_dev && lbs_mesh_connected(priv))
202
		netif_wake_queue(priv->mesh_dev);
203
}
204
EXPORT_SYMBOL_GPL(lbs_send_tx_feedback);