diff options
author | Lennert Buytenhek <buytenh@wantstofly.org> | 2009-07-16 23:21:04 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-08-20 11:38:07 -0400 |
commit | 23b339062f247e0be84eaabb15e17b403c4388b6 (patch) | |
tree | a8ceb91545c7ee21ac30350ef6f1b0c84c49d4bd /drivers/net/wireless | |
parent | 7595d67a06466cc00e3aae1b86544278b57481ee (diff) |
mwl8k: mwl8k_txq_xmit() rework
Various mwl8k_txq_xmit changes:
- Extract the QoS field before adding the DMA header.
- Only write to tx->status once, and only after all the other
descriptor fields have been set.
- Do all tx state manipulation under the tx spinlock.
- Remove the priv->inconfig check, as all transmit queues will
be frozen during config cycles, so we won't ever be asked to
transmit if a config cycle is running.
- Remove some more dead code.
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/mwl8k.c | 145 |
1 files changed, 52 insertions, 93 deletions
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 65eefe835024..88ba52e13af2 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c | |||
@@ -1257,46 +1257,60 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) | |||
1257 | { | 1257 | { |
1258 | struct mwl8k_priv *priv = hw->priv; | 1258 | struct mwl8k_priv *priv = hw->priv; |
1259 | struct ieee80211_tx_info *tx_info; | 1259 | struct ieee80211_tx_info *tx_info; |
1260 | struct mwl8k_vif *mwl8k_vif; | ||
1260 | struct ieee80211_hdr *wh; | 1261 | struct ieee80211_hdr *wh; |
1261 | struct mwl8k_tx_queue *txq; | 1262 | struct mwl8k_tx_queue *txq; |
1262 | struct mwl8k_tx_desc *tx; | 1263 | struct mwl8k_tx_desc *tx; |
1263 | struct mwl8k_dma_data *tr; | ||
1264 | struct mwl8k_vif *mwl8k_vif; | ||
1265 | dma_addr_t dma; | 1264 | dma_addr_t dma; |
1266 | u16 qos = 0; | 1265 | u32 txstatus; |
1267 | bool qosframe = false, ampduframe = false; | 1266 | u8 txdatarate; |
1268 | bool mcframe = false, eapolframe = false; | 1267 | u16 qos; |
1269 | bool amsduframe = false; | ||
1270 | __le16 fc; | ||
1271 | 1268 | ||
1272 | txq = priv->txq + index; | 1269 | wh = (struct ieee80211_hdr *)skb->data; |
1273 | tx = txq->tx_desc_area + txq->tx_tail; | 1270 | if (ieee80211_is_data_qos(wh->frame_control)) |
1274 | 1271 | qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh))); | |
1275 | BUG_ON(txq->tx_skb[txq->tx_tail] != NULL); | 1272 | else |
1273 | qos = 0; | ||
1276 | 1274 | ||
1277 | /* | ||
1278 | * Append HW DMA header to start of packet. | ||
1279 | */ | ||
1280 | mwl8k_add_dma_header(skb); | 1275 | mwl8k_add_dma_header(skb); |
1276 | wh = &((struct mwl8k_dma_data *)skb->data)->wh; | ||
1281 | 1277 | ||
1282 | tx_info = IEEE80211_SKB_CB(skb); | 1278 | tx_info = IEEE80211_SKB_CB(skb); |
1283 | mwl8k_vif = MWL8K_VIF(tx_info->control.vif); | 1279 | mwl8k_vif = MWL8K_VIF(tx_info->control.vif); |
1284 | tr = (struct mwl8k_dma_data *)skb->data; | ||
1285 | wh = &tr->wh; | ||
1286 | fc = wh->frame_control; | ||
1287 | qosframe = ieee80211_is_data_qos(fc); | ||
1288 | mcframe = is_multicast_ether_addr(wh->addr1); | ||
1289 | ampduframe = !!(tx_info->flags & IEEE80211_TX_CTL_AMPDU); | ||
1290 | 1280 | ||
1291 | if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { | 1281 | if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { |
1292 | u16 seqno = mwl8k_vif->seqno; | 1282 | u16 seqno = mwl8k_vif->seqno; |
1283 | |||
1293 | wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); | 1284 | wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); |
1294 | wh->seq_ctrl |= cpu_to_le16(seqno << 4); | 1285 | wh->seq_ctrl |= cpu_to_le16(seqno << 4); |
1295 | mwl8k_vif->seqno = seqno++ % 4096; | 1286 | mwl8k_vif->seqno = seqno++ % 4096; |
1296 | } | 1287 | } |
1297 | 1288 | ||
1298 | if (qosframe) | 1289 | /* Setup firmware control bit fields for each frame type. */ |
1299 | qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh))); | 1290 | txstatus = 0; |
1291 | txdatarate = 0; | ||
1292 | if (ieee80211_is_mgmt(wh->frame_control) || | ||
1293 | ieee80211_is_ctl(wh->frame_control)) { | ||
1294 | txdatarate = 0; | ||
1295 | qos = mwl8k_qos_setbit_eosp(qos); | ||
1296 | /* Set Queue size to unspecified */ | ||
1297 | qos = mwl8k_qos_setbit_qlen(qos, 0xff); | ||
1298 | } else if (ieee80211_is_data(wh->frame_control)) { | ||
1299 | txdatarate = 1; | ||
1300 | if (is_multicast_ether_addr(wh->addr1)) | ||
1301 | txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX; | ||
1302 | |||
1303 | /* Send pkt in an aggregate if AMPDU frame. */ | ||
1304 | if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) | ||
1305 | qos = mwl8k_qos_setbit_ack(qos, | ||
1306 | MWL8K_TXD_ACK_POLICY_BLOCKACK); | ||
1307 | else | ||
1308 | qos = mwl8k_qos_setbit_ack(qos, | ||
1309 | MWL8K_TXD_ACK_POLICY_NORMAL); | ||
1310 | |||
1311 | if (qos & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) | ||
1312 | qos = mwl8k_qos_setbit_amsdu(qos); | ||
1313 | } | ||
1300 | 1314 | ||
1301 | dma = pci_map_single(priv->pdev, skb->data, | 1315 | dma = pci_map_single(priv->pdev, skb->data, |
1302 | skb->len, PCI_DMA_TODEVICE); | 1316 | skb->len, PCI_DMA_TODEVICE); |
@@ -1304,95 +1318,40 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) | |||
1304 | if (pci_dma_mapping_error(priv->pdev, dma)) { | 1318 | if (pci_dma_mapping_error(priv->pdev, dma)) { |
1305 | printk(KERN_DEBUG "%s: failed to dma map skb, " | 1319 | printk(KERN_DEBUG "%s: failed to dma map skb, " |
1306 | "dropping TX frame.\n", priv->name); | 1320 | "dropping TX frame.\n", priv->name); |
1307 | 1321 | dev_kfree_skb(skb); | |
1308 | if (skb != NULL) | ||
1309 | dev_kfree_skb(skb); | ||
1310 | return NETDEV_TX_OK; | 1322 | return NETDEV_TX_OK; |
1311 | } | 1323 | } |
1312 | 1324 | ||
1313 | /* Set desc header, cpu bit order. */ | 1325 | spin_lock_bh(&priv->tx_lock); |
1314 | tx->status = 0; | ||
1315 | tx->data_rate = 0; | ||
1316 | tx->tx_priority = index; | ||
1317 | tx->qos_control = 0; | ||
1318 | tx->rate_info = 0; | ||
1319 | tx->peer_id = mwl8k_vif->peer_id; | ||
1320 | |||
1321 | amsduframe = !!(qos & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT); | ||
1322 | |||
1323 | /* Setup firmware control bit fields for each frame type. */ | ||
1324 | if (ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc)) { | ||
1325 | tx->data_rate = 0; | ||
1326 | qos = mwl8k_qos_setbit_eosp(qos); | ||
1327 | /* Set Queue size to unspecified */ | ||
1328 | qos = mwl8k_qos_setbit_qlen(qos, 0xff); | ||
1329 | } else if (ieee80211_is_data(fc)) { | ||
1330 | tx->data_rate = 1; | ||
1331 | if (mcframe) | ||
1332 | tx->status |= MWL8K_TXD_STATUS_MULTICAST_TX; | ||
1333 | 1326 | ||
1334 | /* | 1327 | txq = priv->txq + index; |
1335 | * Tell firmware to not send EAPOL pkts in an | ||
1336 | * aggregate. Verify against mac80211 tx path. If | ||
1337 | * stack turns off AMPDU for an EAPOL frame this | ||
1338 | * check will be removed. | ||
1339 | */ | ||
1340 | if (eapolframe) { | ||
1341 | qos = mwl8k_qos_setbit_ack(qos, | ||
1342 | MWL8K_TXD_ACK_POLICY_NORMAL); | ||
1343 | } else { | ||
1344 | /* Send pkt in an aggregate if AMPDU frame. */ | ||
1345 | if (ampduframe) | ||
1346 | qos = mwl8k_qos_setbit_ack(qos, | ||
1347 | MWL8K_TXD_ACK_POLICY_BLOCKACK); | ||
1348 | else | ||
1349 | qos = mwl8k_qos_setbit_ack(qos, | ||
1350 | MWL8K_TXD_ACK_POLICY_NORMAL); | ||
1351 | 1328 | ||
1352 | if (amsduframe) | 1329 | BUG_ON(txq->tx_skb[txq->tx_tail] != NULL); |
1353 | qos = mwl8k_qos_setbit_amsdu(qos); | 1330 | txq->tx_skb[txq->tx_tail] = skb; |
1354 | } | ||
1355 | } | ||
1356 | 1331 | ||
1357 | /* Convert to little endian */ | 1332 | tx = txq->tx_desc_area + txq->tx_tail; |
1333 | tx->data_rate = txdatarate; | ||
1334 | tx->tx_priority = index; | ||
1358 | tx->qos_control = cpu_to_le16(qos); | 1335 | tx->qos_control = cpu_to_le16(qos); |
1359 | tx->status = cpu_to_le32(tx->status); | ||
1360 | tx->pkt_phys_addr = cpu_to_le32(dma); | 1336 | tx->pkt_phys_addr = cpu_to_le32(dma); |
1361 | tx->pkt_len = cpu_to_le16(skb->len); | 1337 | tx->pkt_len = cpu_to_le16(skb->len); |
1362 | 1338 | tx->rate_info = 0; | |
1363 | txq->tx_skb[txq->tx_tail] = skb; | 1339 | tx->peer_id = mwl8k_vif->peer_id; |
1364 | |||
1365 | spin_lock_bh(&priv->tx_lock); | ||
1366 | |||
1367 | tx->status = cpu_to_le32(MWL8K_TXD_STATUS_OK | | ||
1368 | MWL8K_TXD_STATUS_FW_OWNED); | ||
1369 | wmb(); | 1340 | wmb(); |
1341 | tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus); | ||
1342 | |||
1343 | txq->tx_stats.count++; | ||
1370 | txq->tx_stats.len++; | 1344 | txq->tx_stats.len++; |
1371 | priv->pending_tx_pkts++; | 1345 | priv->pending_tx_pkts++; |
1372 | txq->tx_stats.count++; | ||
1373 | txq->tx_tail++; | ||
1374 | 1346 | ||
1347 | txq->tx_tail++; | ||
1375 | if (txq->tx_tail == MWL8K_TX_DESCS) | 1348 | if (txq->tx_tail == MWL8K_TX_DESCS) |
1376 | txq->tx_tail = 0; | 1349 | txq->tx_tail = 0; |
1350 | |||
1377 | if (txq->tx_head == txq->tx_tail) | 1351 | if (txq->tx_head == txq->tx_tail) |
1378 | ieee80211_stop_queue(hw, index); | 1352 | ieee80211_stop_queue(hw, index); |
1379 | 1353 | ||
1380 | if (priv->inconfig) { | 1354 | mwl8k_tx_start(priv); |
1381 | /* | ||
1382 | * Silently queue packet when we are in the middle of | ||
1383 | * a config cycle. Notify firmware only if we are | ||
1384 | * waiting for TXQs to empty. If a packet is sent | ||
1385 | * before .config() is complete, perhaps it is better | ||
1386 | * to drop the packet, as the channel is being changed | ||
1387 | * and the packet will end up on the wrong channel. | ||
1388 | */ | ||
1389 | printk(KERN_ERR "%s(): WARNING TX activity while " | ||
1390 | "in config\n", __func__); | ||
1391 | |||
1392 | if (priv->tx_wait != NULL) | ||
1393 | mwl8k_tx_start(priv); | ||
1394 | } else | ||
1395 | mwl8k_tx_start(priv); | ||
1396 | 1355 | ||
1397 | spin_unlock_bh(&priv->tx_lock); | 1356 | spin_unlock_bh(&priv->tx_lock); |
1398 | 1357 | ||