This incomplete patch tries to implement QoS support in
the bcm43xx driver.
It's incomplete, because we don't upload the QoS parameters
to the hardware, yet.
http://bcm-v4.sipsolutions.net/802.11/QoS
We might need some stack support to implement this (I'm not
entirely sure, as I didn't read the whole ieee 802.11e, yet).
Does some stack support for QoS exist? We need to upload
some TX-opportunity, Interframe-space and some other values
to the firmware. Not sure where to get them from, yet (didn't
completely read 802.11e, yet :) ).
The bcm43xx device has 6 DMA rings, but I think only 4
are usable for 802.11e QoS. The other two seem to be useful
for APs, though.
This patch also implements per-DMAring-locking. So this
(theoretically) enables the stack to simultaneously transmit
on two (or more) rings. Though, I'm not sure the stack exploits this
behaviour, yet.
Index: bu3sch-wireless-dev/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.c
===================================================================
--- bu3sch-wireless-dev.orig/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.c 2007-06-02 23:14:46.000000000 +0200
+++ bu3sch-wireless-dev/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.c 2007-06-16 00:38:59.000000000 +0200
@@ -290,6 +290,50 @@ void return_slot(struct bcm43xx_dmaring
ring->used_slots--;
}
+/* Mac80211-queue to bcm43xx-ring mapping */
+static struct bcm43xx_dmaring * priority_to_txring(struct bcm43xx_wldev *dev,
+ int queue_priority)
+{
+ struct bcm43xx_dmaring *ring;
+
+ /* 0 = highest priority */
+ switch (queue_priority) {
+ default:
+ assert(0);
+ /* fallthrough */
+ case 0:
+ ring = dev->dma.tx_ring3;
+ break;
+ case 1:
+ ring = dev->dma.tx_ring2;
+ break;
+ case 2:
+ ring = dev->dma.tx_ring1;
+ break;
+ case 3:
+ ring = dev->dma.tx_ring0;
+ break;
+ case 4:
+ ring = dev->dma.tx_ring4;
+ break;
+ case 5:
+ ring = dev->dma.tx_ring5;
+ break;
+ }
+
+ return ring;
+}
+
+/* Bcm43xx-ring to mac80211-queue mapping */
+static inline int txring_to_priority(struct bcm43xx_dmaring *ring)
+{
+ static const u8 idx_to_prio[] =
+ { 3, 2, 1, 0, 4, 5, };
+
+ return idx_to_prio[ring->index];
+}
+
+
u16 bcm43xx_dmacontroller_base(int dma64bit, int controller_idx)
{
static const u16 map64[] = {
@@ -816,6 +860,7 @@ struct bcm43xx_dmaring * bcm43xx_setup_d
} else
assert(0);
}
+ spin_lock_init(&ring->lock);
#ifdef CONFIG_BCM43XX_MAC80211_DEBUG
ring->last_injected_overflow = jiffies;
#endif
@@ -1171,37 +1216,39 @@ int bcm43xx_dma_tx(struct bcm43xx_wldev
struct sk_buff *skb,
struct ieee80211_tx_control *ctl)
{
- struct bcm43xx_dmaring *ring = dev->dma.tx_ring1;
+ struct bcm43xx_dmaring *ring;
int err = 0;
+ unsigned long flags;
+ ring = priority_to_txring(dev, ctl->queue);
+ spin_lock_irqsave(&ring->lock, flags);
assert(ring->tx);
if (unlikely(free_slots(ring) < SLOTS_PER_PACKET)) {
- /* This should never trigger, as we call
- * ieee80211_stop_queue() when it's full.
- */
printkl(KERN_ERR PFX "DMA queue overflow\n");
- return NETDEV_TX_BUSY;
+ err = -ENOSPC;
+ goto out_unlock;
}
/* Check if the queue was stopped in mac80211,
- * but we got called nevertheless. */
+ * but we got called nevertheless.
+ * That would be a mac80211 bug. */
assert(!ring->stopped);
err = dma_tx_fragment(ring, skb, ctl);
if (unlikely(err)) {
printkl(KERN_ERR PFX "DMA tx mapping failure\n");
- return NETDEV_TX_BUSY;
+ goto out_unlock;
}
-
ring->nr_tx_packets++;
if ((free_slots(ring) < SLOTS_PER_PACKET) ||
should_inject_overflow(ring)) {
/* This TX ring is full. */
- /* FIXME: we currently only have one queue, so hardcode queue 0 here. */
- ieee80211_stop_queue(dev->wl->hw, 0);
+ ieee80211_stop_queue(dev->wl->hw, txring_to_priority(ring));
ring->stopped = 1;
}
+out_unlock:
+ spin_unlock_irqrestore(&ring->lock, flags);
- return 0;
+ return err;
}
void bcm43xx_dma_handle_txstatus(struct bcm43xx_wldev *dev,
@@ -1216,6 +1263,9 @@ void bcm43xx_dma_handle_txstatus(struct
ring = parse_cookie(dev, status->cookie, &slot);
if (unlikely(!ring))
return;
+ assert(irqs_disabled());
+ spin_lock(&ring->lock);
+
assert(ring->tx);
ops = ring->ops;
while (1) {
@@ -1257,24 +1307,32 @@ void bcm43xx_dma_handle_txstatus(struct
dev->stats.last_tx = jiffies;
if (ring->stopped) {
assert(free_slots(ring) >= SLOTS_PER_PACKET);
- /* FIXME: we currently only have one queue, co hardcode queue 0 here. */
- ieee80211_wake_queue(dev->wl->hw, 0);
+ ieee80211_wake_queue(dev->wl->hw, txring_to_priority(ring));
ring->stopped = 0;
}
+
+ spin_unlock(&ring->lock);
}
void bcm43xx_dma_get_tx_stats(struct bcm43xx_wldev *dev,
struct ieee80211_tx_queue_stats *stats)
{
- struct bcm43xx_dma *dma = &dev->dma;
+ const int nr_queues = dev->wl->hw->queues;
struct bcm43xx_dmaring *ring;
struct ieee80211_tx_queue_stats_data *data;
+ unsigned long flags;
+ int i;
- ring = dma->tx_ring1;
- data = &(stats->data[0]);
- data->len = ring->used_slots / SLOTS_PER_PACKET;
- data->limit = ring->nr_slots / SLOTS_PER_PACKET;
- data->count = ring->nr_tx_packets;
+ for (i = 0; i < nr_queues; i++) {
+ data = &(stats->data[i]);
+ ring = priority_to_txring(dev, i);
+
+ spin_lock_irqsave(&ring->lock, flags);
+ data->len = ring->used_slots / SLOTS_PER_PACKET;
+ data->limit = ring->nr_slots / SLOTS_PER_PACKET;
+ data->count = ring->nr_tx_packets;
+ spin_unlock_irqrestore(&ring->lock, flags);
+ }
}
static void dma_rx(struct bcm43xx_dmaring *ring,
@@ -1397,16 +1455,24 @@ void bcm43xx_dma_rx(struct bcm43xx_dmari
ring->current_slot = slot;
}
-static inline void bcm43xx_dma_tx_suspend_ring(struct bcm43xx_dmaring *ring)
+static void bcm43xx_dma_tx_suspend_ring(struct bcm43xx_dmaring *ring)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ring->lock, flags);
assert(ring->tx);
ring->ops->tx_suspend(ring);
+ spin_unlock_irqrestore(&ring->lock, flags);
}
-static inline void bcm43xx_dma_tx_resume_ring(struct bcm43xx_dmaring *ring)
+static void bcm43xx_dma_tx_resume_ring(struct bcm43xx_dmaring *ring)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ring->lock, flags);
assert(ring->tx);
ring->ops->tx_resume(ring);
+ spin_unlock_irqrestore(&ring->lock, flags);
}
void bcm43xx_dma_tx_suspend(struct bcm43xx_wldev *dev)
Index: bu3sch-wireless-dev/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.h
===================================================================
--- bu3sch-wireless-dev.orig/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.h 2007-06-02 22:54:41.000000000 +0200
+++ bu3sch-wireless-dev/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.h 2007-06-16 00:43:40.000000000 +0200
@@ -252,6 +252,8 @@ struct bcm43xx_dmaring {
u8 dma64;
/* Boolean. Is this ring stopped at ieee80211 level? */
u8 stopped;
+ /* Lock, only used for TX. */
+ spinlock_t lock;
struct bcm43xx_wldev *dev;
#ifdef CONFIG_BCM43XX_MAC80211_DEBUG
/* Maximum number of used slots. */
Index: bu3sch-wireless-dev/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.c
===================================================================
--- bu3sch-wireless-dev.orig/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.c 2007-06-14 16:05:09.000000000 +0200
+++ bu3sch-wireless-dev/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.c 2007-06-16 00:50:26.000000000 +0200
@@ -2448,16 +2448,17 @@ static int bcm43xx_tx(struct ieee80211_h
int err = -ENODEV;
unsigned long flags;
+ /* DMA-TX is done without a global lock. */
if (unlikely(!dev))
goto out;
- spin_lock_irqsave(&wl->irq_lock, flags);
- if (likely(bcm43xx_status(dev) == BCM43xx_STAT_INITIALIZED)) {
- if (bcm43xx_using_pio(dev))
- err = bcm43xx_pio_tx(dev, skb, ctl);
- else
- err = bcm43xx_dma_tx(dev, skb, ctl);
- }
- spin_unlock_irqrestore(&wl->irq_lock, flags);
+ assert(bcm43xx_status(dev) == BCM43xx_STAT_INITIALIZED);
+ assert(dev->started);
+ if (bcm43xx_using_pio(dev)) {
+ spin_lock_irqsave(&wl->irq_lock, flags);
+ err = bcm43xx_pio_tx(dev, skb, ctl);
+ spin_unlock_irqrestore(&wl->irq_lock, flags);
+ } else
+ err = bcm43xx_dma_tx(dev, skb, ctl);
out:
if (unlikely(err))
return NETDEV_TX_BUSY;
@@ -3313,10 +3314,13 @@ static int bcm43xx_wireless_core_init(st
bcm43xx_write_mac_bssid_templates(dev);
do {
- if (bcm43xx_using_pio(dev))
+ if (bcm43xx_using_pio(dev)) {
err = bcm43xx_pio_init(dev);
- else
+ } else {
err = bcm43xx_dma_init(dev);
+ if (!err)
+ bcm43xx_qos_init(dev);
+ }
} while (err == -EAGAIN);
if (err)
goto err_chip_exit;
@@ -3798,7 +3802,7 @@ static int bcm43xx_wireless_init(struct
hw->max_signal = 100;
hw->max_rssi = -110;
hw->max_noise = -110;
- hw->queues = 1;
+ hw->queues = 4;
SET_IEEE80211_DEV(hw, dev->dev);
if (is_valid_ether_addr(sprom->r1.et1mac))
SET_IEEE80211_PERM_ADDR(hw, sprom->r1.et1mac);
Index: bu3sch-wireless-dev/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.c
===================================================================
--- bu3sch-wireless-dev.orig/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.c 2007-06-10 14:37:06.000000000 +0200
+++ bu3sch-wireless-dev/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.c 2007-06-16 13:14:30.000000000 +0200
@@ -619,3 +619,28 @@ void bcm43xx_tx_resume(struct bcm43xx_wl
else
bcm43xx_dma_tx_resume(dev);
}
+
+static void upload_qos_parms(struct bcm43xx_wldev *dev,
+ const u16 *parms,
+ u16 offset)
+{
+ int i;
+
+ for (i = 0; i < BCM43xx_NR_QOSPARMS; i++) {
+ bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED,
+ offset + i, parms[i]);
+ }
+}
+
+/* Initialize the QoS parameters */
+void bcm43xx_qos_init(struct bcm43xx_wldev *dev)
+{
+ //TODO
+ bcm43xx_hf_write(dev, bcm43xx_hf_read(dev) | BCM43xx_HF_EDCF);
+ //FIXME kill magic
+ bcm43xx_write16(dev, 0x688,
+ bcm43xx_read16(dev, 0x688) | 0x4);
+
+
+ /*TODO: We might need some stack support here to get the values. */
+}
Index: bu3sch-wireless-dev/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.h
===================================================================
--- bu3sch-wireless-dev.orig/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.h 2007-06-01 01:14:02.000000000 +0200
+++ bu3sch-wireless-dev/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.h 2007-06-16 01:09:03.000000000 +0200
@@ -215,6 +215,21 @@ void bcm43xx_handle_hwtxstatus(struct bc
void bcm43xx_tx_suspend(struct bcm43xx_wldev *dev);
void bcm43xx_tx_resume(struct bcm43xx_wldev *dev);
+
+#define BCM43xx_NR_QOSPARMS 21
+enum {
+ BCM43xx_QOSPARM_TXOP = 0,
+ BCM43xx_QOSPARM_CWMIN,
+ BCM43xx_QOSPARM_CWMAX,
+ BCM43xx_QOSPARM_CWCUR,
+ BCM43xx_QOSPARM_AIFS,
+ BCM43xx_QOSPARM_BSLOTS,
+ BCM43xx_QOSPARM_REGGAP,
+ BCM43xx_QOSPARM_STATUS,
+};
+void bcm43xx_qos_init(struct bcm43xx_wldev *dev);
+
+
/* Helper functions for converting the key-table index from "firmware-format"
* to "raw-format" and back. The firmware API changed for this at some revision.
* We need to account for that here. */
--
Greetings Michael.
-
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html