diff options
author | Larry Finger <Larry.Finger@lwfinger.net> | 2011-12-21 19:47:59 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-01-04 14:30:43 -0500 |
commit | 5d07a3d62f63f3a9ce769c37108f8411c014903e (patch) | |
tree | 8545015a8ee333adad1a1f98dc1fbd0c05025539 | |
parent | 9bd2857188d920f358cfb740fc6f88e1a17a837e (diff) |
b43legacy: Avoid packet losses in the dma worker code
This patch addresses a bug in the dma worker code that keeps draining
packets even when the hardware queues are full. In such cases packets
can not be passed down to the device and are erroneusly dropped by the
code. It is based on commit bad6919469662b7c92bc6353642aaaa777b36bac,
which fixes the same problem in b43.
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/b43legacy/b43legacy.h | 20 | ||||
-rw-r--r-- | drivers/net/wireless/b43legacy/dma.c | 65 | ||||
-rw-r--r-- | drivers/net/wireless/b43legacy/dma.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/b43legacy/main.c | 86 |
4 files changed, 126 insertions, 50 deletions
diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/b43legacy/b43legacy.h index 1d4fc9db7f5e..98e3d44400c6 100644 --- a/drivers/net/wireless/b43legacy/b43legacy.h +++ b/drivers/net/wireless/b43legacy/b43legacy.h | |||
@@ -560,8 +560,16 @@ struct b43legacy_key { | |||
560 | u8 algorithm; | 560 | u8 algorithm; |
561 | }; | 561 | }; |
562 | 562 | ||
563 | #define B43legacy_QOS_QUEUE_NUM 4 | ||
564 | |||
563 | struct b43legacy_wldev; | 565 | struct b43legacy_wldev; |
564 | 566 | ||
567 | /* QOS parameters for a queue. */ | ||
568 | struct b43legacy_qos_params { | ||
569 | /* The QOS parameters */ | ||
570 | struct ieee80211_tx_queue_params p; | ||
571 | }; | ||
572 | |||
565 | /* Data structure for the WLAN parts (802.11 cores) of the b43legacy chip. */ | 573 | /* Data structure for the WLAN parts (802.11 cores) of the b43legacy chip. */ |
566 | struct b43legacy_wl { | 574 | struct b43legacy_wl { |
567 | /* Pointer to the active wireless device on this chip */ | 575 | /* Pointer to the active wireless device on this chip */ |
@@ -611,6 +619,18 @@ struct b43legacy_wl { | |||
611 | bool beacon1_uploaded; | 619 | bool beacon1_uploaded; |
612 | bool beacon_templates_virgin; /* Never wrote the templates? */ | 620 | bool beacon_templates_virgin; /* Never wrote the templates? */ |
613 | struct work_struct beacon_update_trigger; | 621 | struct work_struct beacon_update_trigger; |
622 | /* The current QOS parameters for the 4 queues. */ | ||
623 | struct b43legacy_qos_params qos_params[B43legacy_QOS_QUEUE_NUM]; | ||
624 | |||
625 | /* Packet transmit work */ | ||
626 | struct work_struct tx_work; | ||
627 | |||
628 | /* Queue of packets to be transmitted. */ | ||
629 | struct sk_buff_head tx_queue[B43legacy_QOS_QUEUE_NUM]; | ||
630 | |||
631 | /* Flag that implement the queues stopping. */ | ||
632 | bool tx_queue_stopped[B43legacy_QOS_QUEUE_NUM]; | ||
633 | |||
614 | }; | 634 | }; |
615 | 635 | ||
616 | /* Pointers to the firmware data and meta information about it. */ | 636 | /* Pointers to the firmware data and meta information about it. */ |
diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c index c5535adf6991..aebef75a2c62 100644 --- a/drivers/net/wireless/b43legacy/dma.c +++ b/drivers/net/wireless/b43legacy/dma.c | |||
@@ -727,7 +727,6 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, | |||
727 | } else | 727 | } else |
728 | B43legacy_WARN_ON(1); | 728 | B43legacy_WARN_ON(1); |
729 | } | 729 | } |
730 | spin_lock_init(&ring->lock); | ||
731 | #ifdef CONFIG_B43LEGACY_DEBUG | 730 | #ifdef CONFIG_B43LEGACY_DEBUG |
732 | ring->last_injected_overflow = jiffies; | 731 | ring->last_injected_overflow = jiffies; |
733 | #endif | 732 | #endif |
@@ -1144,10 +1143,8 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev, | |||
1144 | { | 1143 | { |
1145 | struct b43legacy_dmaring *ring; | 1144 | struct b43legacy_dmaring *ring; |
1146 | int err = 0; | 1145 | int err = 0; |
1147 | unsigned long flags; | ||
1148 | 1146 | ||
1149 | ring = priority_to_txring(dev, skb_get_queue_mapping(skb)); | 1147 | ring = priority_to_txring(dev, skb_get_queue_mapping(skb)); |
1150 | spin_lock_irqsave(&ring->lock, flags); | ||
1151 | B43legacy_WARN_ON(!ring->tx); | 1148 | B43legacy_WARN_ON(!ring->tx); |
1152 | 1149 | ||
1153 | if (unlikely(ring->stopped)) { | 1150 | if (unlikely(ring->stopped)) { |
@@ -1157,16 +1154,14 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev, | |||
1157 | * For now, just refuse the transmit. */ | 1154 | * For now, just refuse the transmit. */ |
1158 | if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) | 1155 | if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) |
1159 | b43legacyerr(dev->wl, "Packet after queue stopped\n"); | 1156 | b43legacyerr(dev->wl, "Packet after queue stopped\n"); |
1160 | err = -ENOSPC; | 1157 | return -ENOSPC; |
1161 | goto out_unlock; | ||
1162 | } | 1158 | } |
1163 | 1159 | ||
1164 | if (unlikely(WARN_ON(free_slots(ring) < SLOTS_PER_PACKET))) { | 1160 | if (unlikely(WARN_ON(free_slots(ring) < SLOTS_PER_PACKET))) { |
1165 | /* If we get here, we have a real error with the queue | 1161 | /* If we get here, we have a real error with the queue |
1166 | * full, but queues not stopped. */ | 1162 | * full, but queues not stopped. */ |
1167 | b43legacyerr(dev->wl, "DMA queue overflow\n"); | 1163 | b43legacyerr(dev->wl, "DMA queue overflow\n"); |
1168 | err = -ENOSPC; | 1164 | return -ENOSPC; |
1169 | goto out_unlock; | ||
1170 | } | 1165 | } |
1171 | 1166 | ||
1172 | /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing | 1167 | /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing |
@@ -1176,25 +1171,23 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev, | |||
1176 | /* Drop this packet, as we don't have the encryption key | 1171 | /* Drop this packet, as we don't have the encryption key |
1177 | * anymore and must not transmit it unencrypted. */ | 1172 | * anymore and must not transmit it unencrypted. */ |
1178 | dev_kfree_skb_any(skb); | 1173 | dev_kfree_skb_any(skb); |
1179 | err = 0; | 1174 | return 0; |
1180 | goto out_unlock; | ||
1181 | } | 1175 | } |
1182 | if (unlikely(err)) { | 1176 | if (unlikely(err)) { |
1183 | b43legacyerr(dev->wl, "DMA tx mapping failure\n"); | 1177 | b43legacyerr(dev->wl, "DMA tx mapping failure\n"); |
1184 | goto out_unlock; | 1178 | return err; |
1185 | } | 1179 | } |
1186 | if ((free_slots(ring) < SLOTS_PER_PACKET) || | 1180 | if ((free_slots(ring) < SLOTS_PER_PACKET) || |
1187 | should_inject_overflow(ring)) { | 1181 | should_inject_overflow(ring)) { |
1188 | /* This TX ring is full. */ | 1182 | /* This TX ring is full. */ |
1189 | ieee80211_stop_queue(dev->wl->hw, txring_to_priority(ring)); | 1183 | unsigned int skb_mapping = skb_get_queue_mapping(skb); |
1184 | ieee80211_stop_queue(dev->wl->hw, skb_mapping); | ||
1185 | dev->wl->tx_queue_stopped[skb_mapping] = 1; | ||
1190 | ring->stopped = 1; | 1186 | ring->stopped = 1; |
1191 | if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) | 1187 | if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) |
1192 | b43legacydbg(dev->wl, "Stopped TX ring %d\n", | 1188 | b43legacydbg(dev->wl, "Stopped TX ring %d\n", |
1193 | ring->index); | 1189 | ring->index); |
1194 | } | 1190 | } |
1195 | out_unlock: | ||
1196 | spin_unlock_irqrestore(&ring->lock, flags); | ||
1197 | |||
1198 | return err; | 1191 | return err; |
1199 | } | 1192 | } |
1200 | 1193 | ||
@@ -1205,14 +1198,29 @@ void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, | |||
1205 | struct b43legacy_dmadesc_meta *meta; | 1198 | struct b43legacy_dmadesc_meta *meta; |
1206 | int retry_limit; | 1199 | int retry_limit; |
1207 | int slot; | 1200 | int slot; |
1201 | int firstused; | ||
1208 | 1202 | ||
1209 | ring = parse_cookie(dev, status->cookie, &slot); | 1203 | ring = parse_cookie(dev, status->cookie, &slot); |
1210 | if (unlikely(!ring)) | 1204 | if (unlikely(!ring)) |
1211 | return; | 1205 | return; |
1212 | B43legacy_WARN_ON(!irqs_disabled()); | ||
1213 | spin_lock(&ring->lock); | ||
1214 | |||
1215 | B43legacy_WARN_ON(!ring->tx); | 1206 | B43legacy_WARN_ON(!ring->tx); |
1207 | |||
1208 | /* Sanity check: TX packets are processed in-order on one ring. | ||
1209 | * Check if the slot deduced from the cookie really is the first | ||
1210 | * used slot. */ | ||
1211 | firstused = ring->current_slot - ring->used_slots + 1; | ||
1212 | if (firstused < 0) | ||
1213 | firstused = ring->nr_slots + firstused; | ||
1214 | if (unlikely(slot != firstused)) { | ||
1215 | /* This possibly is a firmware bug and will result in | ||
1216 | * malfunction, memory leaks and/or stall of DMA functionality. | ||
1217 | */ | ||
1218 | b43legacydbg(dev->wl, "Out of order TX status report on DMA " | ||
1219 | "ring %d. Expected %d, but got %d\n", | ||
1220 | ring->index, firstused, slot); | ||
1221 | return; | ||
1222 | } | ||
1223 | |||
1216 | while (1) { | 1224 | while (1) { |
1217 | B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); | 1225 | B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); |
1218 | op32_idx2desc(ring, slot, &meta); | 1226 | op32_idx2desc(ring, slot, &meta); |
@@ -1285,14 +1293,21 @@ void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, | |||
1285 | dev->stats.last_tx = jiffies; | 1293 | dev->stats.last_tx = jiffies; |
1286 | if (ring->stopped) { | 1294 | if (ring->stopped) { |
1287 | B43legacy_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET); | 1295 | B43legacy_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET); |
1288 | ieee80211_wake_queue(dev->wl->hw, txring_to_priority(ring)); | ||
1289 | ring->stopped = 0; | 1296 | ring->stopped = 0; |
1297 | } | ||
1298 | |||
1299 | if (dev->wl->tx_queue_stopped[ring->queue_prio]) { | ||
1300 | dev->wl->tx_queue_stopped[ring->queue_prio] = 0; | ||
1301 | } else { | ||
1302 | /* If the driver queue is running wake the corresponding | ||
1303 | * mac80211 queue. */ | ||
1304 | ieee80211_wake_queue(dev->wl->hw, ring->queue_prio); | ||
1290 | if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) | 1305 | if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) |
1291 | b43legacydbg(dev->wl, "Woke up TX ring %d\n", | 1306 | b43legacydbg(dev->wl, "Woke up TX ring %d\n", |
1292 | ring->index); | 1307 | ring->index); |
1293 | } | 1308 | } |
1294 | 1309 | /* Add work to the queue. */ | |
1295 | spin_unlock(&ring->lock); | 1310 | ieee80211_queue_work(dev->wl->hw, &dev->wl->tx_work); |
1296 | } | 1311 | } |
1297 | 1312 | ||
1298 | static void dma_rx(struct b43legacy_dmaring *ring, | 1313 | static void dma_rx(struct b43legacy_dmaring *ring, |
@@ -1415,22 +1430,14 @@ void b43legacy_dma_rx(struct b43legacy_dmaring *ring) | |||
1415 | 1430 | ||
1416 | static void b43legacy_dma_tx_suspend_ring(struct b43legacy_dmaring *ring) | 1431 | static void b43legacy_dma_tx_suspend_ring(struct b43legacy_dmaring *ring) |
1417 | { | 1432 | { |
1418 | unsigned long flags; | ||
1419 | |||
1420 | spin_lock_irqsave(&ring->lock, flags); | ||
1421 | B43legacy_WARN_ON(!ring->tx); | 1433 | B43legacy_WARN_ON(!ring->tx); |
1422 | op32_tx_suspend(ring); | 1434 | op32_tx_suspend(ring); |
1423 | spin_unlock_irqrestore(&ring->lock, flags); | ||
1424 | } | 1435 | } |
1425 | 1436 | ||
1426 | static void b43legacy_dma_tx_resume_ring(struct b43legacy_dmaring *ring) | 1437 | static void b43legacy_dma_tx_resume_ring(struct b43legacy_dmaring *ring) |
1427 | { | 1438 | { |
1428 | unsigned long flags; | ||
1429 | |||
1430 | spin_lock_irqsave(&ring->lock, flags); | ||
1431 | B43legacy_WARN_ON(!ring->tx); | 1439 | B43legacy_WARN_ON(!ring->tx); |
1432 | op32_tx_resume(ring); | 1440 | op32_tx_resume(ring); |
1433 | spin_unlock_irqrestore(&ring->lock, flags); | ||
1434 | } | 1441 | } |
1435 | 1442 | ||
1436 | void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev) | 1443 | void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev) |
diff --git a/drivers/net/wireless/b43legacy/dma.h b/drivers/net/wireless/b43legacy/dma.h index 504a58767e95..c3282f906bc7 100644 --- a/drivers/net/wireless/b43legacy/dma.h +++ b/drivers/net/wireless/b43legacy/dma.h | |||
@@ -150,8 +150,9 @@ struct b43legacy_dmaring { | |||
150 | enum b43legacy_dmatype type; | 150 | enum b43legacy_dmatype type; |
151 | /* Boolean. Is this ring stopped at ieee80211 level? */ | 151 | /* Boolean. Is this ring stopped at ieee80211 level? */ |
152 | bool stopped; | 152 | bool stopped; |
153 | /* Lock, only used for TX. */ | 153 | /* The QOS priority assigned to this ring. Only used for TX rings. |
154 | spinlock_t lock; | 154 | * This is the mac80211 "queue" value. */ |
155 | u8 queue_prio; | ||
155 | struct b43legacy_wldev *dev; | 156 | struct b43legacy_wldev *dev; |
156 | #ifdef CONFIG_B43LEGACY_DEBUG | 157 | #ifdef CONFIG_B43LEGACY_DEBUG |
157 | /* Maximum number of used slots. */ | 158 | /* Maximum number of used slots. */ |
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 20f02437af8c..d9185633e82f 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c | |||
@@ -2440,30 +2440,64 @@ static int b43legacy_rng_init(struct b43legacy_wl *wl) | |||
2440 | return err; | 2440 | return err; |
2441 | } | 2441 | } |
2442 | 2442 | ||
2443 | static void b43legacy_tx_work(struct work_struct *work) | ||
2444 | { | ||
2445 | struct b43legacy_wl *wl = container_of(work, struct b43legacy_wl, | ||
2446 | tx_work); | ||
2447 | struct b43legacy_wldev *dev; | ||
2448 | struct sk_buff *skb; | ||
2449 | int queue_num; | ||
2450 | int err = 0; | ||
2451 | |||
2452 | mutex_lock(&wl->mutex); | ||
2453 | dev = wl->current_dev; | ||
2454 | if (unlikely(!dev || b43legacy_status(dev) < B43legacy_STAT_STARTED)) { | ||
2455 | mutex_unlock(&wl->mutex); | ||
2456 | return; | ||
2457 | } | ||
2458 | |||
2459 | for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) { | ||
2460 | while (skb_queue_len(&wl->tx_queue[queue_num])) { | ||
2461 | skb = skb_dequeue(&wl->tx_queue[queue_num]); | ||
2462 | if (b43legacy_using_pio(dev)) | ||
2463 | err = b43legacy_pio_tx(dev, skb); | ||
2464 | else | ||
2465 | err = b43legacy_dma_tx(dev, skb); | ||
2466 | if (err == -ENOSPC) { | ||
2467 | wl->tx_queue_stopped[queue_num] = 1; | ||
2468 | ieee80211_stop_queue(wl->hw, queue_num); | ||
2469 | skb_queue_head(&wl->tx_queue[queue_num], skb); | ||
2470 | break; | ||
2471 | } | ||
2472 | if (unlikely(err)) | ||
2473 | dev_kfree_skb(skb); /* Drop it */ | ||
2474 | err = 0; | ||
2475 | } | ||
2476 | |||
2477 | if (!err) | ||
2478 | wl->tx_queue_stopped[queue_num] = 0; | ||
2479 | } | ||
2480 | |||
2481 | mutex_unlock(&wl->mutex); | ||
2482 | } | ||
2483 | |||
2443 | static void b43legacy_op_tx(struct ieee80211_hw *hw, | 2484 | static void b43legacy_op_tx(struct ieee80211_hw *hw, |
2444 | struct sk_buff *skb) | 2485 | struct sk_buff *skb) |
2445 | { | 2486 | { |
2446 | struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); | 2487 | struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); |
2447 | struct b43legacy_wldev *dev = wl->current_dev; | ||
2448 | int err = -ENODEV; | ||
2449 | unsigned long flags; | ||
2450 | 2488 | ||
2451 | if (unlikely(!dev)) | 2489 | if (unlikely(skb->len < 2 + 2 + 6)) { |
2452 | goto out; | 2490 | /* Too short, this can't be a valid frame. */ |
2453 | if (unlikely(b43legacy_status(dev) < B43legacy_STAT_STARTED)) | ||
2454 | goto out; | ||
2455 | /* DMA-TX is done without a global lock. */ | ||
2456 | if (b43legacy_using_pio(dev)) { | ||
2457 | spin_lock_irqsave(&wl->irq_lock, flags); | ||
2458 | err = b43legacy_pio_tx(dev, skb); | ||
2459 | spin_unlock_irqrestore(&wl->irq_lock, flags); | ||
2460 | } else | ||
2461 | err = b43legacy_dma_tx(dev, skb); | ||
2462 | out: | ||
2463 | if (unlikely(err)) { | ||
2464 | /* Drop the packet. */ | ||
2465 | dev_kfree_skb_any(skb); | 2491 | dev_kfree_skb_any(skb); |
2492 | return; | ||
2466 | } | 2493 | } |
2494 | B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags); | ||
2495 | |||
2496 | skb_queue_tail(&wl->tx_queue[skb->queue_mapping], skb); | ||
2497 | if (!wl->tx_queue_stopped[skb->queue_mapping]) | ||
2498 | ieee80211_queue_work(wl->hw, &wl->tx_work); | ||
2499 | else | ||
2500 | ieee80211_stop_queue(wl->hw, skb->queue_mapping); | ||
2467 | } | 2501 | } |
2468 | 2502 | ||
2469 | static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, | 2503 | static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, |
@@ -2879,6 +2913,7 @@ static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev) | |||
2879 | { | 2913 | { |
2880 | struct b43legacy_wl *wl = dev->wl; | 2914 | struct b43legacy_wl *wl = dev->wl; |
2881 | unsigned long flags; | 2915 | unsigned long flags; |
2916 | int queue_num; | ||
2882 | 2917 | ||
2883 | if (b43legacy_status(dev) < B43legacy_STAT_STARTED) | 2918 | if (b43legacy_status(dev) < B43legacy_STAT_STARTED) |
2884 | return; | 2919 | return; |
@@ -2898,11 +2933,16 @@ static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev) | |||
2898 | /* Must unlock as it would otherwise deadlock. No races here. | 2933 | /* Must unlock as it would otherwise deadlock. No races here. |
2899 | * Cancel the possibly running self-rearming periodic work. */ | 2934 | * Cancel the possibly running self-rearming periodic work. */ |
2900 | cancel_delayed_work_sync(&dev->periodic_work); | 2935 | cancel_delayed_work_sync(&dev->periodic_work); |
2936 | cancel_work_sync(&wl->tx_work); | ||
2901 | mutex_lock(&wl->mutex); | 2937 | mutex_lock(&wl->mutex); |
2902 | 2938 | ||
2903 | ieee80211_stop_queues(wl->hw); /* FIXME this could cause a deadlock */ | 2939 | /* Drain all TX queues. */ |
2940 | for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) { | ||
2941 | while (skb_queue_len(&wl->tx_queue[queue_num])) | ||
2942 | dev_kfree_skb(skb_dequeue(&wl->tx_queue[queue_num])); | ||
2943 | } | ||
2904 | 2944 | ||
2905 | b43legacy_mac_suspend(dev); | 2945 | b43legacy_mac_suspend(dev); |
2906 | free_irq(dev->dev->irq, dev); | 2946 | free_irq(dev->dev->irq, dev); |
2907 | b43legacydbg(wl, "Wireless interface stopped\n"); | 2947 | b43legacydbg(wl, "Wireless interface stopped\n"); |
2908 | } | 2948 | } |
@@ -3748,6 +3788,7 @@ static int b43legacy_wireless_init(struct ssb_device *dev) | |||
3748 | struct ieee80211_hw *hw; | 3788 | struct ieee80211_hw *hw; |
3749 | struct b43legacy_wl *wl; | 3789 | struct b43legacy_wl *wl; |
3750 | int err = -ENOMEM; | 3790 | int err = -ENOMEM; |
3791 | int queue_num; | ||
3751 | 3792 | ||
3752 | b43legacy_sprom_fixup(dev->bus); | 3793 | b43legacy_sprom_fixup(dev->bus); |
3753 | 3794 | ||
@@ -3782,6 +3823,13 @@ static int b43legacy_wireless_init(struct ssb_device *dev) | |||
3782 | mutex_init(&wl->mutex); | 3823 | mutex_init(&wl->mutex); |
3783 | INIT_LIST_HEAD(&wl->devlist); | 3824 | INIT_LIST_HEAD(&wl->devlist); |
3784 | INIT_WORK(&wl->beacon_update_trigger, b43legacy_beacon_update_trigger_work); | 3825 | INIT_WORK(&wl->beacon_update_trigger, b43legacy_beacon_update_trigger_work); |
3826 | INIT_WORK(&wl->tx_work, b43legacy_tx_work); | ||
3827 | |||
3828 | /* Initialize queues and flags. */ | ||
3829 | for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) { | ||
3830 | skb_queue_head_init(&wl->tx_queue[queue_num]); | ||
3831 | wl->tx_queue_stopped[queue_num] = 0; | ||
3832 | } | ||
3785 | 3833 | ||
3786 | ssb_set_devtypedata(dev, wl); | 3834 | ssb_set_devtypedata(dev, wl); |
3787 | b43legacyinfo(wl, "Broadcom %04X WLAN found (core revision %u)\n", | 3835 | b43legacyinfo(wl, "Broadcom %04X WLAN found (core revision %u)\n", |