blob: 7bc2124382592ef7a28164d02ddccadd61c14280 [file] [log] [blame]
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001/**
2 * This file contains the handling of TX in wlan driver.
3 */
4#include <linux/netdevice.h>
5
6#include "hostcmd.h"
7#include "radiotap.h"
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02008#include "decl.h"
9#include "defs.h"
10#include "dev.h"
11#include "wext.h"
12
13/**
14 * @brief This function converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE
15 * units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1)
16 *
17 * @param rate Input rate
18 * @return Output Rate (0 if invalid)
19 */
20static u32 convert_radiotap_rate_to_mv(u8 rate)
21{
22 switch (rate) {
23 case 2: /* 1 Mbps */
24 return 0 | (1 << 4);
25 case 4: /* 2 Mbps */
26 return 1 | (1 << 4);
27 case 11: /* 5.5 Mbps */
28 return 2 | (1 << 4);
29 case 22: /* 11 Mbps */
30 return 3 | (1 << 4);
31 case 12: /* 6 Mbps */
32 return 4 | (1 << 4);
33 case 18: /* 9 Mbps */
34 return 5 | (1 << 4);
35 case 24: /* 12 Mbps */
36 return 6 | (1 << 4);
37 case 36: /* 18 Mbps */
38 return 7 | (1 << 4);
39 case 48: /* 24 Mbps */
40 return 8 | (1 << 4);
41 case 72: /* 36 Mbps */
42 return 9 | (1 << 4);
43 case 96: /* 48 Mbps */
44 return 10 | (1 << 4);
45 case 108: /* 54 Mbps */
46 return 11 | (1 << 4);
47 }
48 return 0;
49}
50
51/**
David Woodhouse6b4a7e02007-12-09 12:48:10 -050052 * @brief This function checks the conditions and sends packet to IF
53 * layer if everything is ok.
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020054 *
Holger Schurig69f90322007-11-23 15:43:44 +010055 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020056 * @param skb A pointer to skb which includes TX packet
57 * @return 0 or -1
58 */
David Woodhouse8af23b22007-12-09 12:57:14 -050059int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020060{
David Woodhouse8af23b22007-12-09 12:57:14 -050061 struct lbs_private *priv = dev->priv;
David Woodhouse6b4a7e02007-12-09 12:48:10 -050062 int ret = -1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020063 struct txpd localtxpd;
64 struct txpd *plocaltxpd = &localtxpd;
65 u8 *p802x_hdr;
66 struct tx_radiotap_hdr *pradiotap_hdr;
67 u32 new_rate;
David Woodhouseaa21c002007-12-08 20:04:36 +000068 u8 *ptr = priv->tmptxbuf;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020069
Holger Schurig9012b282007-05-25 11:27:16 -040070 lbs_deb_enter(LBS_DEB_TX);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020071
David Woodhouse6b4a7e02007-12-09 12:48:10 -050072 lbs_deb_hex(LBS_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100));
73
David Woodhouse8af23b22007-12-09 12:57:14 -050074 netif_stop_queue(priv->dev);
75 if (priv->mesh_dev)
76 netif_stop_queue(priv->mesh_dev);
77
David Woodhouse6b4a7e02007-12-09 12:48:10 -050078 if (priv->dnld_sent) {
79 lbs_pr_alert( "TX error: dnld_sent = %d, not sending\n",
80 priv->dnld_sent);
81 goto done;
82 }
83
David Woodhouse8af23b22007-12-09 12:57:14 -050084 if (priv->currenttxskb) {
85 lbs_pr_err("%s while TX skb pending\n", __func__);
86 goto done;
87 }
88
David Woodhouse6b4a7e02007-12-09 12:48:10 -050089 if ((priv->psstate == PS_STATE_SLEEP) ||
90 (priv->psstate == PS_STATE_PRE_SLEEP)) {
91 lbs_pr_alert("TX error: packet xmit in %ssleep mode\n",
92 priv->psstate == PS_STATE_SLEEP?"":"pre-");
93 goto done;
94 }
95
David Woodhouseaa21c002007-12-08 20:04:36 +000096 if (priv->surpriseremoved)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020097 return -1;
98
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020099 if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) {
Holger Schurig9012b282007-05-25 11:27:16 -0400100 lbs_deb_tx("tx err: skb length %d 0 or > %zd\n",
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200101 skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE);
David Woodhouse6b4a7e02007-12-09 12:48:10 -0500102 goto done_tx;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200103 }
104
David Woodhouse6b4a7e02007-12-09 12:48:10 -0500105 ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200106 memset(plocaltxpd, 0, sizeof(struct txpd));
107
David Woodhouse981f1872007-05-25 23:36:54 -0400108 plocaltxpd->tx_packet_length = cpu_to_le16(skb->len);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200109
110 /* offset of actual data */
David Woodhouse981f1872007-05-25 23:36:54 -0400111 plocaltxpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200112
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200113 p802x_hdr = skb->data;
David Woodhouseaa21c002007-12-08 20:04:36 +0000114 if (priv->monitormode != LBS_MONITOR_OFF) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200115
116 /* locate radiotap header */
117 pradiotap_hdr = (struct tx_radiotap_hdr *)skb->data;
118
119 /* set txpd fields from the radiotap header */
120 new_rate = convert_radiotap_rate_to_mv(pradiotap_hdr->rate);
121 if (new_rate != 0) {
David Woodhouse981f1872007-05-25 23:36:54 -0400122 /* use new tx_control[4:0] */
David Woodhouse981f1872007-05-25 23:36:54 -0400123 plocaltxpd->tx_control = cpu_to_le32(new_rate);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200124 }
125
126 /* skip the radiotap header */
127 p802x_hdr += sizeof(struct tx_radiotap_hdr);
David Woodhouse981f1872007-05-25 23:36:54 -0400128 plocaltxpd->tx_packet_length =
David Woodhouse86760082007-05-25 23:39:34 -0400129 cpu_to_le16(le16_to_cpu(plocaltxpd->tx_packet_length)
David Woodhouse981f1872007-05-25 23:36:54 -0400130 - sizeof(struct tx_radiotap_hdr));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200131
132 }
133 /* copy destination address from 802.3 or 802.11 header */
David Woodhouseaa21c002007-12-08 20:04:36 +0000134 if (priv->monitormode != LBS_MONITOR_OFF)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200135 memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN);
136 else
137 memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN);
138
Holger Schurigece56192007-08-02 11:53:06 -0400139 lbs_deb_hex(LBS_DEB_TX, "txpd", (u8 *) plocaltxpd, sizeof(struct txpd));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200140
141 if (IS_MESH_FRAME(skb)) {
David Woodhouse981f1872007-05-25 23:36:54 -0400142 plocaltxpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200143 }
144
145 memcpy(ptr, plocaltxpd, sizeof(struct txpd));
146
147 ptr += sizeof(struct txpd);
148
Holger Schurigece56192007-08-02 11:53:06 -0400149 lbs_deb_hex(LBS_DEB_TX, "Tx Data", (u8 *) p802x_hdr, le16_to_cpu(plocaltxpd->tx_packet_length));
David Woodhouse86760082007-05-25 23:39:34 -0400150 memcpy(ptr, p802x_hdr, le16_to_cpu(plocaltxpd->tx_packet_length));
Holger Schurig208fdd22007-05-25 12:17:06 -0400151 ret = priv->hw_host_to_card(priv, MVMS_DAT,
David Woodhouseaa21c002007-12-08 20:04:36 +0000152 priv->tmptxbuf,
David Woodhouse86760082007-05-25 23:39:34 -0400153 le16_to_cpu(plocaltxpd->tx_packet_length) +
David Woodhouse981f1872007-05-25 23:36:54 -0400154 sizeof(struct txpd));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200155
156 if (ret) {
Holger Schurig208fdd22007-05-25 12:17:06 -0400157 lbs_deb_tx("tx err: hw_host_to_card returned 0x%X\n", ret);
David Woodhouse6b4a7e02007-12-09 12:48:10 -0500158 goto done_tx;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200159 }
160
David Woodhouse6b4a7e02007-12-09 12:48:10 -0500161 lbs_deb_tx("%s succeeds\n", __func__);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200162
David Woodhouse6b4a7e02007-12-09 12:48:10 -0500163done_tx:
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200164 if (!ret) {
165 priv->stats.tx_packets++;
166 priv->stats.tx_bytes += skb->len;
David Woodhouse8af23b22007-12-09 12:57:14 -0500167
168 dev->trans_start = jiffies;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200169 } else {
170 priv->stats.tx_dropped++;
171 priv->stats.tx_errors++;
172 }
173
David Woodhouseaa21c002007-12-08 20:04:36 +0000174 if (!ret && priv->monitormode != LBS_MONITOR_OFF) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200175 /* Keep the skb to echo it back once Tx feedback is
176 received from FW */
177 skb_orphan(skb);
178 /* stop processing outgoing pkts */
Holger Schurig634b8f42007-05-25 13:05:16 -0400179 netif_stop_queue(priv->dev);
Holger Schurig3cf84092007-08-02 11:50:12 -0400180 if (priv->mesh_dev)
181 netif_stop_queue(priv->mesh_dev);
David Woodhousef86a93e2007-12-08 19:46:19 +0000182
183 /* Keep the skb around for when we get feedback */
David Woodhouseaa21c002007-12-08 20:04:36 +0000184 priv->currenttxskb = skb;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200185 } else {
186 dev_kfree_skb_any(skb);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200187 }
188
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200189done:
Holger Schurig9012b282007-05-25 11:27:16 -0400190 lbs_deb_leave_args(LBS_DEB_TX, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200191 return ret;
192}
193
194/**
195 * @brief This function sends to the host the last transmitted packet,
196 * filling the radiotap headers with transmission information.
197 *
Holger Schurig69f90322007-11-23 15:43:44 +0100198 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200199 * @param status A 32 bit value containing transmission status.
200 *
201 * @returns void
202 */
Holger Schurig69f90322007-11-23 15:43:44 +0100203void lbs_send_tx_feedback(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200204{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200205 struct tx_radiotap_hdr *radiotap_hdr;
David Woodhouseaa21c002007-12-08 20:04:36 +0000206 u32 status = priv->eventcause;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200207 int txfail;
208 int try_count;
209
David Woodhouseaa21c002007-12-08 20:04:36 +0000210 if (priv->monitormode == LBS_MONITOR_OFF ||
211 priv->currenttxskb == NULL)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200212 return;
213
David Woodhouseaa21c002007-12-08 20:04:36 +0000214 radiotap_hdr = (struct tx_radiotap_hdr *)priv->currenttxskb->data;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200215
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200216 txfail = (status >> 24);
217
218#if 0
219 /* The version of roofnet that we've tested does not use this yet
220 * But it may be used in the future.
221 */
222 if (txfail)
223 radiotap_hdr->flags &= IEEE80211_RADIOTAP_F_TX_FAIL;
224#endif
225 try_count = (status >> 16) & 0xff;
226 radiotap_hdr->data_retries = (try_count) ?
David Woodhouseaa21c002007-12-08 20:04:36 +0000227 (1 + priv->txretrycount - try_count) : 0;
228 lbs_upload_rx_packet(priv, priv->currenttxskb);
229 priv->currenttxskb = NULL;
Brajesh Dave01d77d82007-11-20 17:44:14 -0500230
David Woodhouseaa21c002007-12-08 20:04:36 +0000231 if (priv->connect_status == LBS_CONNECTED)
Holger Schurig634b8f42007-05-25 13:05:16 -0400232 netif_wake_queue(priv->dev);
Brajesh Dave01d77d82007-11-20 17:44:14 -0500233
David Woodhouseaa21c002007-12-08 20:04:36 +0000234 if (priv->mesh_dev && (priv->mesh_connect_status == LBS_CONNECTED))
Brajesh Dave01d77d82007-11-20 17:44:14 -0500235 netif_wake_queue(priv->mesh_dev);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200236}
Holger Schurig10078322007-11-15 18:05:47 -0500237EXPORT_SYMBOL_GPL(lbs_send_tx_feedback);