* [RFC PATCH 0/17] zd1211rw: add support for AP mode
@ 2011-01-04 23:47 Jussi Kivilinna
2011-01-04 23:47 ` [RFC PATCH 01/17] zd1211rw: fix tx-queue disabling Jussi Kivilinna
` (16 more replies)
0 siblings, 17 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:47 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
Hello!
This ended up being much bigger task I expected. Anyway AP mode is now
usable and zd1211rw should be less crashy now. I'm still sending this
as RFC as it touches and adds a lot of code.
-Jussi
^ permalink raw reply [flat|nested] 30+ messages in thread
* [RFC PATCH 01/17] zd1211rw: fix tx-queue disabling
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
@ 2011-01-04 23:47 ` Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 02/17] zd1211rw: cancel process_intr work on zd_chip_disable_int() Jussi Kivilinna
` (15 subsequent siblings)
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:47 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
When stress testing AP-mode I hit OOPS when unpluging or rmmodding
driver.
It appears that when tx-queue is disabled, tx-urbs might be left pending.
These can cause ehci to call non-existing tx_urb_complete() (after rmmod)
or uninitialized/reseted private structure (after disconnect()). Add skb
queue for submitted packets and unlink pending urbs on zd_usb_disable_tx().
Part of the problem seems to be usb->free_urb_list that isn't always
working as it should, causing machine freeze when trying to free the list
in zd_usb_disable_tx(). Caching free urbs isn't what other drivers seem
to be doing (usbnet for example) so strip free_usb_list.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_usb.c | 113 ++++++++++++++------------------
drivers/net/wireless/zd1211rw/zd_usb.h | 3 -
2 files changed, 51 insertions(+), 65 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index 06041cb..ed5379b 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -778,15 +778,34 @@ void zd_usb_disable_rx(struct zd_usb *usb)
void zd_usb_disable_tx(struct zd_usb *usb)
{
struct zd_usb_tx *tx = &usb->tx;
+ struct sk_buff_head *q = &tx->submitted_queue;
+ struct sk_buff *skb, *skbnext;
unsigned long flags;
- struct list_head *pos, *n;
+ int qlen;
spin_lock_irqsave(&tx->lock, flags);
- list_for_each_safe(pos, n, &tx->free_urb_list) {
- list_del(pos);
- usb_free_urb(list_entry(pos, struct urb, urb_list));
- }
tx->enabled = 0;
+ spin_unlock(&tx->lock);
+
+ /* unlink all submitted tx-urbs */
+ spin_lock(&q->lock);
+ skb_queue_walk_safe(q, skb, skbnext) {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ usb_unlink_urb(info->rate_driver_data[1]);
+ }
+ qlen = skb_queue_len(q);
+ spin_unlock(&q->lock);
+
+ /* wait until unlink has completed */
+ while (qlen > 0) {
+ msleep(20);
+
+ spin_lock(&q->lock);
+ qlen = skb_queue_len(q);
+ spin_unlock(&q->lock);
+ }
+
+ spin_lock(&tx->lock);
tx->submitted_urbs = 0;
/* The stopped state is ignored, relying on ieee80211_wake_queues()
* in a potentionally following zd_usb_enable_tx().
@@ -814,56 +833,6 @@ void zd_usb_enable_tx(struct zd_usb *usb)
spin_unlock_irqrestore(&tx->lock, flags);
}
-/**
- * alloc_tx_urb - provides an tx URB
- * @usb: a &struct zd_usb pointer
- *
- * Allocates a new URB. If possible takes the urb from the free list in
- * usb->tx.
- */
-static struct urb *alloc_tx_urb(struct zd_usb *usb)
-{
- struct zd_usb_tx *tx = &usb->tx;
- unsigned long flags;
- struct list_head *entry;
- struct urb *urb;
-
- spin_lock_irqsave(&tx->lock, flags);
- if (list_empty(&tx->free_urb_list)) {
- urb = usb_alloc_urb(0, GFP_ATOMIC);
- goto out;
- }
- entry = tx->free_urb_list.next;
- list_del(entry);
- urb = list_entry(entry, struct urb, urb_list);
-out:
- spin_unlock_irqrestore(&tx->lock, flags);
- return urb;
-}
-
-/**
- * free_tx_urb - frees a used tx URB
- * @usb: a &struct zd_usb pointer
- * @urb: URB to be freed
- *
- * Frees the transmission URB, which means to put it on the free URB
- * list.
- */
-static void free_tx_urb(struct zd_usb *usb, struct urb *urb)
-{
- struct zd_usb_tx *tx = &usb->tx;
- unsigned long flags;
-
- spin_lock_irqsave(&tx->lock, flags);
- if (!tx->enabled) {
- usb_free_urb(urb);
- goto out;
- }
- list_add(&urb->urb_list, &tx->free_urb_list);
-out:
- spin_unlock_irqrestore(&tx->lock, flags);
-}
-
static void tx_dec_submitted_urbs(struct zd_usb *usb)
{
struct zd_usb_tx *tx = &usb->tx;
@@ -878,7 +847,7 @@ static void tx_dec_submitted_urbs(struct zd_usb *usb)
spin_unlock_irqrestore(&tx->lock, flags);
}
-static void tx_inc_submitted_urbs(struct zd_usb *usb)
+static void tx_inc_submitted_urbs(struct zd_usb *usb, struct urb *urb)
{
struct zd_usb_tx *tx = &usb->tx;
unsigned long flags;
@@ -929,8 +898,9 @@ free_urb:
*/
info = IEEE80211_SKB_CB(skb);
usb = &zd_hw_mac(info->rate_driver_data[0])->chip.usb;
+ skb_unlink(skb, &usb->tx.submitted_queue);
zd_mac_tx_to_dev(skb, urb->status);
- free_tx_urb(usb, urb);
+ usb_free_urb(urb);
tx_dec_submitted_urbs(usb);
return;
resubmit:
@@ -955,11 +925,22 @@ resubmit:
*/
int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb)
{
- int r;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ int r, enabled;
struct usb_device *udev = zd_usb_to_usbdev(usb);
struct urb *urb;
+ struct zd_usb_tx *tx = &usb->tx;
+ unsigned long flags;
- urb = alloc_tx_urb(usb);
+ spin_lock_irqsave(&tx->lock, flags);
+ enabled = tx->enabled;
+ spin_unlock_irqrestore(&tx->lock, flags);
+ if (!enabled) {
+ r = -ENOENT;
+ goto out;
+ }
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
r = -ENOMEM;
goto out;
@@ -968,13 +949,18 @@ int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb)
usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
skb->data, skb->len, tx_urb_complete, skb);
+ info->rate_driver_data[1] = urb;
+ skb_queue_tail(&tx->submitted_queue, skb);
+
r = usb_submit_urb(urb, GFP_ATOMIC);
- if (r)
+ if (r) {
+ skb_unlink(skb, &tx->submitted_queue);
goto error;
- tx_inc_submitted_urbs(usb);
+ }
+ tx_inc_submitted_urbs(usb, urb);
return 0;
error:
- free_tx_urb(usb, urb);
+ usb_free_urb(urb);
out:
return r;
}
@@ -1007,7 +993,7 @@ static inline void init_usb_tx(struct zd_usb *usb)
spin_lock_init(&tx->lock);
tx->enabled = 0;
tx->stopped = 0;
- INIT_LIST_HEAD(&tx->free_urb_list);
+ skb_queue_head_init(&tx->submitted_queue);
tx->submitted_urbs = 0;
}
@@ -1240,6 +1226,7 @@ static void disconnect(struct usb_interface *intf)
ieee80211_unregister_hw(hw);
/* Just in case something has gone wrong! */
+ zd_usb_disable_tx(usb);
zd_usb_disable_rx(usb);
zd_usb_disable_int(usb);
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h
index 1b1655c..b71baa0 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.h
+++ b/drivers/net/wireless/zd1211rw/zd_usb.h
@@ -185,7 +185,6 @@ struct zd_usb_rx {
/**
* struct zd_usb_tx - structure used for transmitting frames
* @lock: lock for transmission
- * @free_urb_list: list of free URBs, contains all the URBs, which can be used
* @submitted_urbs: atomic integer that counts the URBs having sent to the
* device, which haven't been completed
* @enabled: enabled flag, indicates whether tx is enabled
@@ -193,7 +192,7 @@ struct zd_usb_rx {
*/
struct zd_usb_tx {
spinlock_t lock;
- struct list_head free_urb_list;
+ struct sk_buff_head submitted_queue;
int submitted_urbs;
int enabled;
int stopped;
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 02/17] zd1211rw: cancel process_intr work on zd_chip_disable_int()
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
2011-01-04 23:47 ` [RFC PATCH 01/17] zd1211rw: fix tx-queue disabling Jussi Kivilinna
@ 2011-01-04 23:48 ` Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 03/17] zd1211rw: fix beacon interval setup Jussi Kivilinna
` (14 subsequent siblings)
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:48 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
OOPS if worker is running and disconnect() is called (triggered
by unpluging device). Much harder to trigger at this stage but
later when we have AP beacon work in process_intr it happens very
easy.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_chip.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 30f8d40..6effe18 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -1406,6 +1406,9 @@ void zd_chip_disable_int(struct zd_chip *chip)
mutex_lock(&chip->mutex);
zd_usb_disable_int(&chip->usb);
mutex_unlock(&chip->mutex);
+
+ /* cancel pending interrupt work */
+ cancel_work_sync(&zd_chip_to_mac(chip)->process_intr);
}
int zd_chip_enable_rxtx(struct zd_chip *chip)
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 03/17] zd1211rw: fix beacon interval setup
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
2011-01-04 23:47 ` [RFC PATCH 01/17] zd1211rw: fix tx-queue disabling Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 02/17] zd1211rw: cancel process_intr work on zd_chip_disable_int() Jussi Kivilinna
@ 2011-01-04 23:48 ` Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 04/17] zd1211rw: move set_multicast_hash and set_rx_filter from workers to configure_filter Jussi Kivilinna
` (13 subsequent siblings)
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:48 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
Vendor driver uses CR_BNC_INTERVAL at various places, one is HW_EnableBeacon()
that combinies beacon interval with BSS-type flag and DTIM value in upper
16bit of u32. The other one is HW_UpdateBcnInterval() that set_aw_pt_bi()
appears to be based on. HW_UpdateBcnInterval() takes interval argument as u16
and uses that for calculations, set_aw_pt_bi() uses u32 value that has flags
and dtim in upper part. This clearly seems wrong. Also HW_UpdateBcnInterval()
updates only lower 16bit part of CR_BNC_INTERVAL. So make set_aw_pt_bi() do
calculations on only lower u16 part of s->beacon_interval.
Also set 32bit beacon interval register before reading values from device,
as HW_EnableBeacon() on vendor driver does. This is required to make beacon
work on AP-mode, simply reading and then writing updated values is not enough
at least with zd1211b.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_chip.c | 16 ++++++++++------
1 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 6effe18..8c05737 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -848,11 +848,12 @@ static int get_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
{
struct zd_ioreq32 reqs[3];
+ u16 b_interval = s->beacon_interval & 0xffff;
- if (s->beacon_interval <= 5)
- s->beacon_interval = 5;
- if (s->pre_tbtt < 4 || s->pre_tbtt >= s->beacon_interval)
- s->pre_tbtt = s->beacon_interval - 1;
+ if (b_interval <= 5)
+ b_interval = 5;
+ if (s->pre_tbtt < 4 || s->pre_tbtt >= b_interval)
+ s->pre_tbtt = b_interval - 1;
if (s->atim_wnd_period >= s->pre_tbtt)
s->atim_wnd_period = s->pre_tbtt - 1;
@@ -861,7 +862,7 @@ static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
reqs[1].addr = CR_PRE_TBTT;
reqs[1].value = s->pre_tbtt;
reqs[2].addr = CR_BCN_INTERVAL;
- reqs[2].value = s->beacon_interval;
+ reqs[2].value = (s->beacon_interval & ~0xffff) | b_interval;
return zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs));
}
@@ -873,10 +874,13 @@ static int set_beacon_interval(struct zd_chip *chip, u32 interval)
struct aw_pt_bi s;
ZD_ASSERT(mutex_is_locked(&chip->mutex));
+
+ r = zd_iowrite32_locked(chip, interval, CR_BCN_INTERVAL);
+ if (r)
+ return r;
r = get_aw_pt_bi(chip, &s);
if (r)
return r;
- s.beacon_interval = interval;
return set_aw_pt_bi(chip, &s);
}
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 04/17] zd1211rw: move set_multicast_hash and set_rx_filter from workers to configure_filter
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (2 preceding siblings ...)
2011-01-04 23:48 ` [RFC PATCH 03/17] zd1211rw: fix beacon interval setup Jussi Kivilinna
@ 2011-01-04 23:48 ` Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 05/17] zd1211rw: move set_rts_cts_work to bss_info_changed Jussi Kivilinna
` (12 subsequent siblings)
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:48 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
Workers not needed anymore since configure_filter may sleep. Keep
mac->multicast_hash for later use (hw reset).
Signed-off-by: Jussi Kivilinna <jussi.kivilina@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_mac.c | 38 ++++++--------------------------
drivers/net/wireless/zd1211rw/zd_mac.h | 2 --
2 files changed, 7 insertions(+), 33 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 43307bd..ca3b4e1 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -923,31 +923,6 @@ static void zd_process_intr(struct work_struct *work)
}
-static void set_multicast_hash_handler(struct work_struct *work)
-{
- struct zd_mac *mac =
- container_of(work, struct zd_mac, set_multicast_hash_work);
- struct zd_mc_hash hash;
-
- spin_lock_irq(&mac->lock);
- hash = mac->multicast_hash;
- spin_unlock_irq(&mac->lock);
-
- zd_chip_set_multicast_hash(&mac->chip, &hash);
-}
-
-static void set_rx_filter_handler(struct work_struct *work)
-{
- struct zd_mac *mac =
- container_of(work, struct zd_mac, set_rx_filter_work);
- int r;
-
- dev_dbg_f(zd_mac_dev(mac), "\n");
- r = set_rx_filter(mac);
- if (r)
- dev_err(zd_mac_dev(mac), "set_rx_filter_handler error %d\n", r);
-}
-
static u64 zd_op_prepare_multicast(struct ieee80211_hw *hw,
struct netdev_hw_addr_list *mc_list)
{
@@ -979,6 +954,7 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw,
};
struct zd_mac *mac = zd_hw_mac(hw);
unsigned long flags;
+ int r;
/* Only deal with supported flags */
changed_flags &= SUPPORTED_FIF_FLAGS;
@@ -1000,11 +976,13 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw,
mac->multicast_hash = hash;
spin_unlock_irqrestore(&mac->lock, flags);
- /* XXX: these can be called here now, can sleep now! */
- queue_work(zd_workqueue, &mac->set_multicast_hash_work);
+ zd_chip_set_multicast_hash(&mac->chip, &hash);
- if (changed_flags & FIF_CONTROL)
- queue_work(zd_workqueue, &mac->set_rx_filter_work);
+ if (changed_flags & FIF_CONTROL) {
+ r = set_rx_filter(mac);
+ if (r)
+ dev_err(zd_mac_dev(mac), "set_rx_filter error %d\n", r);
+ }
/* no handling required for FIF_OTHER_BSS as we don't currently
* do BSSID filtering */
@@ -1160,9 +1138,7 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
zd_chip_init(&mac->chip, hw, intf);
housekeeping_init(mac);
- INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler);
INIT_WORK(&mac->set_rts_cts_work, set_rts_cts_work);
- INIT_WORK(&mac->set_rx_filter_work, set_rx_filter_handler);
INIT_WORK(&mac->process_intr, zd_process_intr);
SET_IEEE80211_DEV(hw, &intf->dev);
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index a6d86b9..f28ecb9 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -173,9 +173,7 @@ struct zd_mac {
spinlock_t intr_lock;
struct ieee80211_hw *hw;
struct housekeeping housekeeping;
- struct work_struct set_multicast_hash_work;
struct work_struct set_rts_cts_work;
- struct work_struct set_rx_filter_work;
struct work_struct process_intr;
struct zd_mc_hash multicast_hash;
u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 05/17] zd1211rw: move set_rts_cts_work to bss_info_changed
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (3 preceding siblings ...)
2011-01-04 23:48 ` [RFC PATCH 04/17] zd1211rw: move set_multicast_hash and set_rx_filter from workers to configure_filter Jussi Kivilinna
@ 2011-01-04 23:48 ` Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 06/17] zd1211rw: support setting BSSID for AP mode Jussi Kivilinna
` (11 subsequent siblings)
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:48 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
As bss_info_changed may sleep, we can as well set RTS_CTS register right away.
Keep mac->short_preamble for later use (hw reset).
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_mac.c | 27 +++++----------------------
drivers/net/wireless/zd1211rw/zd_mac.h | 3 ---
2 files changed, 5 insertions(+), 25 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index ca3b4e1..9284643 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -994,20 +994,9 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw,
* time. */
}
-static void set_rts_cts_work(struct work_struct *work)
+static void set_rts_cts(struct zd_mac *mac, unsigned int short_preamble)
{
- struct zd_mac *mac =
- container_of(work, struct zd_mac, set_rts_cts_work);
- unsigned long flags;
- unsigned int short_preamble;
-
mutex_lock(&mac->chip.mutex);
-
- spin_lock_irqsave(&mac->lock, flags);
- mac->updating_rts_rate = 0;
- short_preamble = mac->short_preamble;
- spin_unlock_irqrestore(&mac->lock, flags);
-
zd_chip_set_rts_cts_rate_locked(&mac->chip, short_preamble);
mutex_unlock(&mac->chip.mutex);
}
@@ -1018,7 +1007,6 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw,
u32 changes)
{
struct zd_mac *mac = zd_hw_mac(hw);
- unsigned long flags;
int associated;
dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes);
@@ -1056,15 +1044,11 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw,
/* TODO: do hardware bssid filtering */
if (changes & BSS_CHANGED_ERP_PREAMBLE) {
- spin_lock_irqsave(&mac->lock, flags);
+ spin_lock_irq(&mac->lock);
mac->short_preamble = bss_conf->use_short_preamble;
- if (!mac->updating_rts_rate) {
- mac->updating_rts_rate = 1;
- /* FIXME: should disable TX here, until work has
- * completed and RTS_CTS reg is updated */
- queue_work(zd_workqueue, &mac->set_rts_cts_work);
- }
- spin_unlock_irqrestore(&mac->lock, flags);
+ spin_unlock_irq(&mac->lock);
+
+ set_rts_cts(mac, bss_conf->use_short_preamble);
}
}
@@ -1138,7 +1122,6 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
zd_chip_init(&mac->chip, hw, intf);
housekeeping_init(mac);
- INIT_WORK(&mac->set_rts_cts_work, set_rts_cts_work);
INIT_WORK(&mac->process_intr, zd_process_intr);
SET_IEEE80211_DEV(hw, &intf->dev);
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index f28ecb9..ff7ef30 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -189,9 +189,6 @@ struct zd_mac {
/* Short preamble (used for RTS/CTS) */
unsigned int short_preamble:1;
- /* flags to indicate update in progress */
- unsigned int updating_rts_rate:1;
-
/* whether to pass frames with CRC errors to stack */
unsigned int pass_failed_fcs:1;
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 06/17] zd1211rw: support setting BSSID for AP mode
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (4 preceding siblings ...)
2011-01-04 23:48 ` [RFC PATCH 05/17] zd1211rw: move set_rts_cts_work to bss_info_changed Jussi Kivilinna
@ 2011-01-04 23:48 ` Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 07/17] zd1211rw: fix ack_pending in filter_ack causing tx-packet ordering problem on monitor Jussi Kivilinna
` (10 subsequent siblings)
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:48 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_chip.c | 39 +++++++++++++++++++++++--------
drivers/net/wireless/zd1211rw/zd_chip.h | 1 +
drivers/net/wireless/zd1211rw/zd_mac.c | 25 +++++++++++++++++++-
drivers/net/wireless/zd1211rw/zd_mac.h | 1 +
4 files changed, 55 insertions(+), 11 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 8c05737..f1813a2 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -369,16 +369,12 @@ error:
return r;
}
-/* MAC address: if custom mac addresses are to be used CR_MAC_ADDR_P1 and
- * CR_MAC_ADDR_P2 must be overwritten
- */
-int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
+static int zd_write_mac_addr_common(struct zd_chip *chip, const u8 *mac_addr,
+ const struct zd_ioreq32 *in_reqs,
+ const char *type)
{
int r;
- struct zd_ioreq32 reqs[2] = {
- [0] = { .addr = CR_MAC_ADDR_P1 },
- [1] = { .addr = CR_MAC_ADDR_P2 },
- };
+ struct zd_ioreq32 reqs[2] = {in_reqs[0], in_reqs[1]};
if (mac_addr) {
reqs[0].value = (mac_addr[3] << 24)
@@ -387,9 +383,9 @@ int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
| mac_addr[0];
reqs[1].value = (mac_addr[5] << 8)
| mac_addr[4];
- dev_dbg_f(zd_chip_dev(chip), "mac addr %pM\n", mac_addr);
+ dev_dbg_f(zd_chip_dev(chip), "%s addr %pM\n", type, mac_addr);
} else {
- dev_dbg_f(zd_chip_dev(chip), "set NULL mac\n");
+ dev_dbg_f(zd_chip_dev(chip), "set NULL %s\n", type);
}
mutex_lock(&chip->mutex);
@@ -398,6 +394,29 @@ int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
return r;
}
+/* MAC address: if custom mac addresses are to be used CR_MAC_ADDR_P1 and
+ * CR_MAC_ADDR_P2 must be overwritten
+ */
+int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
+{
+ static const struct zd_ioreq32 reqs[2] = {
+ [0] = { .addr = CR_MAC_ADDR_P1 },
+ [1] = { .addr = CR_MAC_ADDR_P2 },
+ };
+
+ return zd_write_mac_addr_common(chip, mac_addr, reqs, "mac");
+}
+
+int zd_write_bssid(struct zd_chip *chip, const u8 *bssid)
+{
+ static const struct zd_ioreq32 reqs[2] = {
+ [0] = { .addr = CR_BSSID_P1 },
+ [1] = { .addr = CR_BSSID_P2 },
+ };
+
+ return zd_write_mac_addr_common(chip, bssid, reqs, "bssid");
+}
+
int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain)
{
int r;
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h
index f8bbf7d..7b0c58c 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.h
+++ b/drivers/net/wireless/zd1211rw/zd_chip.h
@@ -881,6 +881,7 @@ static inline u8 _zd_chip_get_channel(struct zd_chip *chip)
u8 zd_chip_get_channel(struct zd_chip *chip);
int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain);
int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr);
+int zd_write_bssid(struct zd_chip *chip, const u8 *bssid);
int zd_chip_switch_radio_on(struct zd_chip *chip);
int zd_chip_switch_radio_off(struct zd_chip *chip);
int zd_chip_enable_int(struct zd_chip *chip);
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 9284643..0ef27db 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -231,6 +231,26 @@ static int set_rx_filter(struct zd_mac *mac)
return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter);
}
+static int set_mac_and_bssid(struct zd_mac *mac)
+{
+ int r;
+
+ if (!mac->vif)
+ return -1;
+
+ r = zd_write_mac_addr(&mac->chip, mac->vif->addr);
+ if (r)
+ return r;
+
+ /* Vendor driver after setting MAC either sets BSSID for AP or
+ * filter for other modes.
+ */
+ if (mac->type != NL80211_IFTYPE_AP)
+ return set_rx_filter(mac);
+ else
+ return zd_write_bssid(&mac->chip, mac->vif->addr);
+}
+
static int set_mc_hash(struct zd_mac *mac)
{
struct zd_mc_hash hash;
@@ -888,7 +908,9 @@ static int zd_op_add_interface(struct ieee80211_hw *hw,
return -EOPNOTSUPP;
}
- return zd_write_mac_addr(&mac->chip, vif->addr);
+ mac->vif = vif;
+
+ return set_mac_and_bssid(mac);
}
static void zd_op_remove_interface(struct ieee80211_hw *hw,
@@ -896,6 +918,7 @@ static void zd_op_remove_interface(struct ieee80211_hw *hw,
{
struct zd_mac *mac = zd_hw_mac(hw);
mac->type = NL80211_IFTYPE_UNSPECIFIED;
+ mac->vif = NULL;
zd_set_beacon_interval(&mac->chip, 0);
zd_write_mac_addr(&mac->chip, NULL);
}
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index ff7ef30..0ec6bde 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -172,6 +172,7 @@ struct zd_mac {
spinlock_t lock;
spinlock_t intr_lock;
struct ieee80211_hw *hw;
+ struct ieee80211_vif *vif;
struct housekeeping housekeeping;
struct work_struct set_rts_cts_work;
struct work_struct process_intr;
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 07/17] zd1211rw: fix ack_pending in filter_ack causing tx-packet ordering problem on monitor
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (5 preceding siblings ...)
2011-01-04 23:48 ` [RFC PATCH 06/17] zd1211rw: support setting BSSID for AP mode Jussi Kivilinna
@ 2011-01-04 23:48 ` Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 08/17] zd1211rw: let zd_set_beacon_interval() set dtim_period and add AP-beacon flag Jussi Kivilinna
` (9 subsequent siblings)
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:48 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
For reasons not very clear yet to me, filter_ack leaves matching tx-packet
pending with 'ack_pending'. This causes tx-packet to be passed back to upper
layer after next packet has been transfered and tx-packets might end up
coming come out of monitor interface in wrong order vs. rx.
Because of this when enable AP-mode, hostapd monitor interface would get
packets in wrong order causing problems in WPA association.
So don't use mac->ack_pending when in AP-mode.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_mac.c | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 0ef27db..9dde27a 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -799,6 +799,13 @@ static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
mac->ack_pending = 1;
mac->ack_signal = stats->signal;
+
+ /* Prevent pending tx-packet on AP-mode */
+ if (mac->type == NL80211_IFTYPE_AP) {
+ skb = __skb_dequeue(q);
+ zd_mac_tx_status(hw, skb, mac->ack_signal, NULL);
+ mac->ack_pending = 0;
+ }
}
spin_unlock_irqrestore(&q->lock, flags);
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 08/17] zd1211rw: let zd_set_beacon_interval() set dtim_period and add AP-beacon flag
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (6 preceding siblings ...)
2011-01-04 23:48 ` [RFC PATCH 07/17] zd1211rw: fix ack_pending in filter_ack causing tx-packet ordering problem on monitor Jussi Kivilinna
@ 2011-01-04 23:48 ` Jussi Kivilinna
2011-01-04 23:49 ` [RFC PATCH 09/17] zd1211rw: implement seq_num for IEEE80211_TX_CTL_ASSIGN_SEQ Jussi Kivilinna
` (8 subsequent siblings)
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:48 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
Add support for AP-mode beacon. Also disable beacon when interface is set
down as otherwise hw will keep flooding NEXT_BCN interrupts.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_chip.c | 33 ++++++++++++++++++++++++++-----
drivers/net/wireless/zd1211rw/zd_chip.h | 4 +++-
drivers/net/wireless/zd1211rw/zd_mac.c | 17 ++++++++--------
3 files changed, 40 insertions(+), 14 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index f1813a2..fa90d06 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -887,14 +887,36 @@ static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
}
-static int set_beacon_interval(struct zd_chip *chip, u32 interval)
+static int set_beacon_interval(struct zd_chip *chip, u16 interval,
+ u8 dtim_period, int type)
{
int r;
struct aw_pt_bi s;
+ u32 b_interval, mode_flag;
ZD_ASSERT(mutex_is_locked(&chip->mutex));
- r = zd_iowrite32_locked(chip, interval, CR_BCN_INTERVAL);
+ if (interval > 0) {
+ switch (type) {
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ mode_flag = BCN_MODE_IBSS;
+ break;
+ case NL80211_IFTYPE_AP:
+ mode_flag = BCN_MODE_AP;
+ break;
+ default:
+ mode_flag = 0;
+ break;
+ }
+ } else {
+ dtim_period = 0;
+ mode_flag = 0;
+ }
+
+ b_interval = mode_flag | (dtim_period << 16) | interval;
+
+ r = zd_iowrite32_locked(chip, b_interval, CR_BCN_INTERVAL);
if (r)
return r;
r = get_aw_pt_bi(chip, &s);
@@ -903,12 +925,13 @@ static int set_beacon_interval(struct zd_chip *chip, u32 interval)
return set_aw_pt_bi(chip, &s);
}
-int zd_set_beacon_interval(struct zd_chip *chip, u32 interval)
+int zd_set_beacon_interval(struct zd_chip *chip, u16 interval, u8 dtim_period,
+ int type)
{
int r;
mutex_lock(&chip->mutex);
- r = set_beacon_interval(chip, interval);
+ r = set_beacon_interval(chip, interval, dtim_period, type);
mutex_unlock(&chip->mutex);
return r;
}
@@ -927,7 +950,7 @@ static int hw_init(struct zd_chip *chip)
if (r)
return r;
- return set_beacon_interval(chip, 100);
+ return set_beacon_interval(chip, 100, 0, NL80211_IFTYPE_UNSPECIFIED);
}
static zd_addr_t fw_reg_addr(struct zd_chip *chip, u16 offset)
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h
index 7b0c58c..14e4402 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.h
+++ b/drivers/net/wireless/zd1211rw/zd_chip.h
@@ -546,6 +546,7 @@ enum {
#define RX_FILTER_CTRL (RX_FILTER_RTS | RX_FILTER_CTS | \
RX_FILTER_CFEND | RX_FILTER_CFACK)
+#define BCN_MODE_AP 0x1000000
#define BCN_MODE_IBSS 0x2000000
/* Monitor mode sets filter to 0xfffff */
@@ -921,7 +922,8 @@ enum led_status {
int zd_chip_control_leds(struct zd_chip *chip, enum led_status status);
-int zd_set_beacon_interval(struct zd_chip *chip, u32 interval);
+int zd_set_beacon_interval(struct zd_chip *chip, u16 interval, u8 dtim_period,
+ int type);
static inline int zd_get_beacon_interval(struct zd_chip *chip, u32 *interval)
{
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 9dde27a..6be406e 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -926,7 +926,7 @@ static void zd_op_remove_interface(struct ieee80211_hw *hw,
struct zd_mac *mac = zd_hw_mac(hw);
mac->type = NL80211_IFTYPE_UNSPECIFIED;
mac->vif = NULL;
- zd_set_beacon_interval(&mac->chip, 0);
+ zd_set_beacon_interval(&mac->chip, 0, 0, NL80211_IFTYPE_UNSPECIFIED);
zd_write_mac_addr(&mac->chip, NULL);
}
@@ -1054,15 +1054,16 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw,
}
if (changes & BSS_CHANGED_BEACON_ENABLED) {
- u32 interval;
+ u16 interval = 0;
+ u8 period = 0;
- if (bss_conf->enable_beacon)
- interval = BCN_MODE_IBSS |
- bss_conf->beacon_int;
- else
- interval = 0;
+ if (bss_conf->enable_beacon) {
+ period = bss_conf->dtim_period;
+ interval = bss_conf->beacon_int;
+ }
- zd_set_beacon_interval(&mac->chip, interval);
+ zd_set_beacon_interval(&mac->chip, interval, period,
+ mac->type);
}
} else
associated = is_valid_ether_addr(bss_conf->bssid);
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 09/17] zd1211rw: implement seq_num for IEEE80211_TX_CTL_ASSIGN_SEQ
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (7 preceding siblings ...)
2011-01-04 23:48 ` [RFC PATCH 08/17] zd1211rw: let zd_set_beacon_interval() set dtim_period and add AP-beacon flag Jussi Kivilinna
@ 2011-01-04 23:49 ` Jussi Kivilinna
2011-01-04 23:49 ` [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc() Jussi Kivilinna
` (7 subsequent siblings)
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:49 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_mac.c | 21 +++++++++++++++++++++
drivers/net/wireless/zd1211rw/zd_mac.h | 2 ++
2 files changed, 23 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 6be406e..aace010 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -654,6 +654,24 @@ static int zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon)
(full_len << 19));
}
+static void create_tx_desc_seq(struct zd_mac *mac, struct ieee80211_hdr *hdr,
+ struct ieee80211_tx_info *info)
+{
+ unsigned long irqflags;
+
+ if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
+ return;
+
+ spin_lock_irqsave(&mac->seqlock, irqflags);
+
+ if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
+ mac->seqno += 0x10;
+ hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+ hdr->seq_ctrl |= cpu_to_le16(mac->seqno);
+
+ spin_unlock_irqrestore(&mac->seqlock, irqflags);
+}
+
static int fill_ctrlset(struct zd_mac *mac,
struct sk_buff *skb)
{
@@ -678,6 +696,8 @@ static int fill_ctrlset(struct zd_mac *mac,
cs_set_control(mac, cs, hdr, info);
+ create_tx_desc_seq(mac, hdr, info);
+
packet_length = frag_len + sizeof(struct zd_ctrlset) + 10;
ZD_ASSERT(packet_length <= 0xffff);
/* ZD1211B: Computing the length difference this way, gives us
@@ -1117,6 +1137,7 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
memset(mac, 0, sizeof(*mac));
spin_lock_init(&mac->lock);
+ spin_lock_init(&mac->seqlock);
mac->hw = hw;
mac->type = NL80211_IFTYPE_UNSPECIFIED;
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index 0ec6bde..e663451 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -186,6 +186,8 @@ struct zd_mac {
struct ieee80211_channel channels[14];
struct ieee80211_rate rates[12];
struct ieee80211_supported_band band;
+ spinlock_t seqlock;
+ u16 seqno;
/* Short preamble (used for RTS/CTS) */
unsigned int short_preamble:1;
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc()
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (8 preceding siblings ...)
2011-01-04 23:49 ` [RFC PATCH 09/17] zd1211rw: implement seq_num for IEEE80211_TX_CTL_ASSIGN_SEQ Jussi Kivilinna
@ 2011-01-04 23:49 ` Jussi Kivilinna
2011-01-06 21:46 ` Christian Lamparter
2011-01-04 23:49 ` [RFC PATCH 11/17] zd1211rw: add beacon watchdog and setting HW beacon more failsafe Jussi Kivilinna
` (6 subsequent siblings)
16 siblings, 1 reply; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:49 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_mac.c | 39 +++++++++++++++++++++++++++++---
1 files changed, 35 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index aace010..a3c7e8f 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -958,16 +958,46 @@ static int zd_op_config(struct ieee80211_hw *hw, u32 changed)
return zd_chip_set_channel(&mac->chip, conf->channel->hw_value);
}
+static void zd_beacon_done(struct zd_mac *mac)
+{
+ struct sk_buff *skb, *beacon;
+
+ if (!mac->vif || mac->vif->type != NL80211_IFTYPE_AP)
+ return;
+
+ /*
+ * Send out buffered broad- and multicast frames.
+ */
+ while (!ieee80211_queue_stopped(mac->hw, 0)) {
+ skb = ieee80211_get_buffered_bc(mac->hw, mac->vif);
+ if (!skb)
+ break;
+ zd_op_tx(mac->hw, skb);
+ }
+
+ /*
+ * Fetch next beacon so that tim_count is updated.
+ */
+ beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+ if (!beacon)
+ return;
+
+ zd_mac_config_beacon(mac->hw, beacon);
+ kfree_skb(beacon);
+}
+
static void zd_process_intr(struct work_struct *work)
{
u16 int_status;
struct zd_mac *mac = container_of(work, struct zd_mac, process_intr);
int_status = le16_to_cpu(*(__le16 *)(mac->intr_buffer+4));
- if (int_status & INT_CFG_NEXT_BCN)
- dev_dbg_f_limit(zd_mac_dev(mac), "INT_CFG_NEXT_BCN\n");
- else
+ if (int_status & INT_CFG_NEXT_BCN) {
+ /*dev_dbg_f_limit(zd_mac_dev(mac), "INT_CFG_NEXT_BCN\n");*/
+ zd_beacon_done(mac);
+ } else {
dev_dbg_f(zd_mac_dev(mac), "Unsupported interrupt\n");
+ }
zd_chip_enable_hwint(&mac->chip);
}
@@ -1152,7 +1182,8 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band;
hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
- IEEE80211_HW_SIGNAL_UNSPEC;
+ IEEE80211_HW_SIGNAL_UNSPEC |
+ IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING;
hw->wiphy->interface_modes =
BIT(NL80211_IFTYPE_MESH_POINT) |
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 11/17] zd1211rw: add beacon watchdog and setting HW beacon more failsafe
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (9 preceding siblings ...)
2011-01-04 23:49 ` [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc() Jussi Kivilinna
@ 2011-01-04 23:49 ` Jussi Kivilinna
2011-01-04 23:49 ` [RFC PATCH 12/17] zd1211rw: batch beacon config commands together Jussi Kivilinna
` (5 subsequent siblings)
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:49 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
When doing tx/rx at high packet rate (for example simply using ping -f),
device starts to fail to respond to control messages. On non-AP modes
this only causes problems for LED updating code but when we are running
in AP-mode we are writing new beacon to HW usually every 100ms. Now if
control message fails in HW beacon setup, device lock is kept locked
and beacon data partially written. This can and usually does cause:
1. HW beacon setup fail now on, as driver cannot acquire device lock.
2. Beacon-done interrupt stop working as device has incomplete beacon.
Therefore make zd_mac_config_beacon() always try to release device lock
and add beacon watchdog to restart beaconing when stall is detected.
Also fix zd_mac_config_beacon() try acquiring device lock for max 500ms,
as what old code appeared to be trying to do using loop and msleep(1).
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_mac.c | 189 +++++++++++++++++++++++++++-----
drivers/net/wireless/zd1211rw/zd_mac.h | 13 ++
2 files changed, 171 insertions(+), 31 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index a3c7e8f..d4b4036 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -138,6 +138,9 @@ static const struct ieee80211_channel zd_channels[] = {
static void housekeeping_init(struct zd_mac *mac);
static void housekeeping_enable(struct zd_mac *mac);
static void housekeeping_disable(struct zd_mac *mac);
+static void beacon_init(struct zd_mac *mac);
+static void beacon_enable(struct zd_mac *mac);
+static void beacon_disable(struct zd_mac *mac);
static int zd_reg2alpha2(u8 regdomain, char *alpha2)
{
@@ -295,6 +298,8 @@ static int zd_op_start(struct ieee80211_hw *hw)
goto disable_rxtx;
housekeeping_enable(mac);
+ beacon_enable(mac);
+ set_bit(ZD_DEVICE_RUNNING, &mac->flags);
return 0;
disable_rxtx:
zd_chip_disable_rxtx(chip);
@@ -313,12 +318,15 @@ static void zd_op_stop(struct ieee80211_hw *hw)
struct sk_buff *skb;
struct sk_buff_head *ack_wait_queue = &mac->ack_wait_queue;
+ clear_bit(ZD_DEVICE_RUNNING, &mac->flags);
+
/* The order here deliberately is a little different from the open()
* method, since we need to make sure there is no opportunity for RX
* frames to be processed by mac80211 after we have stopped it.
*/
zd_chip_disable_rxtx(chip);
+ beacon_disable(mac);
housekeeping_disable(mac);
flush_workqueue(zd_workqueue);
@@ -594,64 +602,99 @@ static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs,
static int zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon)
{
struct zd_mac *mac = zd_hw_mac(hw);
- int r;
+ int r, ret;
u32 tmp, j = 0;
/* 4 more bytes for tail CRC */
u32 full_len = beacon->len + 4;
+ unsigned long end_jiffies, message_jiffies;
+
+ mutex_lock(&mac->chip.mutex);
- r = zd_iowrite32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, 0);
+ r = zd_iowrite32_locked(&mac->chip, 0, CR_BCN_FIFO_SEMAPHORE);
if (r < 0)
- return r;
- r = zd_ioread32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, &tmp);
+ goto out;
+ r = zd_ioread32_locked(&mac->chip, &tmp, CR_BCN_FIFO_SEMAPHORE);
if (r < 0)
- return r;
+ goto release_sema;
+ end_jiffies = jiffies + HZ / 2; /*~500ms*/
+ message_jiffies = jiffies + HZ / 10; /*~100ms*/
while (tmp & 0x2) {
- r = zd_ioread32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, &tmp);
+ r = zd_ioread32_locked(&mac->chip, &tmp, CR_BCN_FIFO_SEMAPHORE);
if (r < 0)
- return r;
- if ((++j % 100) == 0) {
- printk(KERN_ERR "CR_BCN_FIFO_SEMAPHORE not ready\n");
- if (j >= 500) {
- printk(KERN_ERR "Giving up beacon config.\n");
- return -ETIMEDOUT;
+ goto release_sema;
+ if (time_is_before_eq_jiffies(message_jiffies)) {
+ message_jiffies = jiffies + HZ / 10;
+ dev_err(zd_mac_dev(mac),
+ "CR_BCN_FIFO_SEMAPHORE not ready\n");
+ if (time_is_before_eq_jiffies(end_jiffies)) {
+ dev_err(zd_mac_dev(mac),
+ "Giving up beacon config.\n");
+ r = -ETIMEDOUT;
+ goto release_sema;
}
}
- msleep(1);
+ msleep(20);
}
- r = zd_iowrite32(&mac->chip, CR_BCN_FIFO, full_len - 1);
+ r = zd_iowrite32_locked(&mac->chip, full_len - 1, CR_BCN_FIFO);
if (r < 0)
- return r;
+ goto release_sema;
if (zd_chip_is_zd1211b(&mac->chip)) {
- r = zd_iowrite32(&mac->chip, CR_BCN_LENGTH, full_len - 1);
+ r = zd_iowrite32_locked(&mac->chip, full_len - 1,
+ CR_BCN_LENGTH);
if (r < 0)
- return r;
+ goto release_sema;
}
for (j = 0 ; j < beacon->len; j++) {
- r = zd_iowrite32(&mac->chip, CR_BCN_FIFO,
- *((u8 *)(beacon->data + j)));
+ r = zd_iowrite32_locked(&mac->chip, *((u8 *)(beacon->data + j)),
+ CR_BCN_FIFO);
if (r < 0)
- return r;
+ goto release_sema;
}
for (j = 0; j < 4; j++) {
- r = zd_iowrite32(&mac->chip, CR_BCN_FIFO, 0x0);
+ r = zd_iowrite32_locked(&mac->chip, 0x0, CR_BCN_FIFO);
if (r < 0)
- return r;
+ goto release_sema;
}
- r = zd_iowrite32(&mac->chip, CR_BCN_FIFO_SEMAPHORE, 1);
- if (r < 0)
- return r;
+release_sema:
+ /*
+ * Try very hard to release device beacon semaphore, as otherwise
+ * device/driver can be left in unusable state.
+ */
+ end_jiffies = jiffies + HZ / 2; /*~500ms*/
+ ret = zd_iowrite32_locked(&mac->chip, 1, CR_BCN_FIFO_SEMAPHORE);
+ while (ret < 0) {
+ if (time_is_before_eq_jiffies(end_jiffies)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ msleep(20);
+ ret = zd_iowrite32_locked(&mac->chip, 1, CR_BCN_FIFO_SEMAPHORE);
+ }
+
+ if (ret < 0)
+ dev_err(zd_mac_dev(mac), "Could not release "
+ "CR_BCN_FIFO_SEMAPHORE!\n");
+ if (r < 0 || ret < 0) {
+ if (r >= 0)
+ r = ret;
+ goto out;
+ }
/* 802.11b/g 2.4G CCK 1Mb
* 802.11a, not yet implemented, uses different values (see GPL vendor
* driver)
*/
- return zd_iowrite32(&mac->chip, CR_BCN_PLCP_CFG, 0x00000400 |
- (full_len << 19));
+ r = zd_iowrite32_locked(&mac->chip, 0x00000400 | (full_len << 19),
+ CR_BCN_PLCP_CFG);
+out:
+ mutex_unlock(&mac->chip.mutex);
+ return r;
}
static void create_tx_desc_seq(struct zd_mac *mac, struct ieee80211_hdr *hdr,
@@ -962,6 +1005,8 @@ static void zd_beacon_done(struct zd_mac *mac)
{
struct sk_buff *skb, *beacon;
+ if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags))
+ return;
if (!mac->vif || mac->vif->type != NL80211_IFTYPE_AP)
return;
@@ -979,11 +1024,14 @@ static void zd_beacon_done(struct zd_mac *mac)
* Fetch next beacon so that tim_count is updated.
*/
beacon = ieee80211_beacon_get(mac->hw, mac->vif);
- if (!beacon)
- return;
+ if (beacon) {
+ zd_mac_config_beacon(mac->hw, beacon);
+ kfree_skb(beacon);
+ }
- zd_mac_config_beacon(mac->hw, beacon);
- kfree_skb(beacon);
+ spin_lock_irq(&mac->lock);
+ mac->beacon.last_update = jiffies;
+ spin_unlock_irq(&mac->lock);
}
static void zd_process_intr(struct work_struct *work)
@@ -1098,7 +1146,9 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw,
struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
if (beacon) {
+ zd_chip_disable_hwint(&mac->chip);
zd_mac_config_beacon(hw, beacon);
+ zd_chip_enable_hwint(&mac->chip);
kfree_skb(beacon);
}
}
@@ -1112,6 +1162,12 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw,
interval = bss_conf->beacon_int;
}
+ spin_lock_irq(&mac->lock);
+ mac->beacon.period = period;
+ mac->beacon.interval = interval;
+ mac->beacon.last_update = jiffies;
+ spin_unlock_irq(&mac->lock);
+
zd_set_beacon_interval(&mac->chip, interval, period,
mac->type);
}
@@ -1205,12 +1261,83 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
zd_chip_init(&mac->chip, hw, intf);
housekeeping_init(mac);
+ beacon_init(mac);
INIT_WORK(&mac->process_intr, zd_process_intr);
SET_IEEE80211_DEV(hw, &intf->dev);
return hw;
}
+#define BEACON_WATCHDOG_DELAY round_jiffies_relative(HZ)
+
+static void beacon_watchdog_handler(struct work_struct *work)
+{
+ struct zd_mac *mac =
+ container_of(work, struct zd_mac, beacon.watchdog_work.work);
+ struct sk_buff *beacon;
+ unsigned long timeout;
+ int interval, period;
+
+ if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags))
+ goto rearm;
+ if (mac->type != NL80211_IFTYPE_AP || !mac->vif)
+ goto rearm;
+
+ spin_lock_irq(&mac->lock);
+ interval = mac->beacon.interval;
+ period = mac->beacon.period;
+ timeout = mac->beacon.last_update + msecs_to_jiffies(interval) + HZ;
+ spin_unlock_irq(&mac->lock);
+
+ if (interval > 0 && time_is_before_jiffies(timeout)) {
+ dev_dbg_f(zd_mac_dev(mac), "beacon interrupt stalled, "
+ "restarting. "
+ "(interval: %d, dtim: %d)\n",
+ interval, period);
+
+ zd_chip_disable_hwint(&mac->chip);
+
+ beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+ if (beacon) {
+ zd_mac_config_beacon(mac->hw, beacon);
+ kfree_skb(beacon);
+ }
+
+ zd_set_beacon_interval(&mac->chip, interval, period, mac->type);
+
+ zd_chip_enable_hwint(&mac->chip);
+
+ spin_lock_irq(&mac->lock);
+ mac->beacon.last_update = jiffies;
+ spin_unlock_irq(&mac->lock);
+ }
+
+rearm:
+ queue_delayed_work(zd_workqueue, &mac->beacon.watchdog_work,
+ BEACON_WATCHDOG_DELAY);
+}
+
+static void beacon_init(struct zd_mac *mac)
+{
+ INIT_DELAYED_WORK(&mac->beacon.watchdog_work, beacon_watchdog_handler);
+}
+
+static void beacon_enable(struct zd_mac *mac)
+{
+ dev_dbg_f(zd_mac_dev(mac), "\n");
+
+ mac->beacon.last_update = jiffies;
+ queue_delayed_work(zd_workqueue, &mac->beacon.watchdog_work,
+ BEACON_WATCHDOG_DELAY);
+}
+
+static void beacon_disable(struct zd_mac *mac)
+{
+ dev_dbg_f(zd_mac_dev(mac), "\n");
+ cancel_rearming_delayed_workqueue(zd_workqueue,
+ &mac->beacon.watchdog_work);
+}
+
#define LINK_LED_WORK_DELAY HZ
static void link_led_handler(struct work_struct *work)
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index e663451..4b1edb2 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -163,6 +163,17 @@ struct housekeeping {
struct delayed_work link_led_work;
};
+struct beacon {
+ struct delayed_work watchdog_work;
+ unsigned long last_update;
+ u16 interval;
+ u8 period;
+};
+
+enum zd_device_flags {
+ ZD_DEVICE_RUNNING,
+};
+
#define ZD_MAC_STATS_BUFFER_SIZE 16
#define ZD_MAC_MAX_ACK_WAITERS 50
@@ -174,6 +185,7 @@ struct zd_mac {
struct ieee80211_hw *hw;
struct ieee80211_vif *vif;
struct housekeeping housekeeping;
+ struct beacon beacon;
struct work_struct set_rts_cts_work;
struct work_struct process_intr;
struct zd_mc_hash multicast_hash;
@@ -182,6 +194,7 @@ struct zd_mac {
u8 default_regdomain;
int type;
int associated;
+ unsigned long flags;
struct sk_buff_head ack_wait_queue;
struct ieee80211_channel channels[14];
struct ieee80211_rate rates[12];
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 12/17] zd1211rw: batch beacon config commands together
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (10 preceding siblings ...)
2011-01-04 23:49 ` [RFC PATCH 11/17] zd1211rw: add beacon watchdog and setting HW beacon more failsafe Jussi Kivilinna
@ 2011-01-04 23:49 ` Jussi Kivilinna
2011-01-04 23:49 ` [RFC PATCH 13/17] zd1211rw: use stack for small cmd-buffers Jussi Kivilinna
` (4 subsequent siblings)
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:49 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
Beacon config function writes beacon to hw one write per byte. This is very
slow (usually taking more than 100ms to finish) and causes high CPU usage
when in AP-mode (kworker at ~50% on Intel Atom N270). By batching commands
together zd_mac_config_beacon() runtime can be lowered to 1/5th and lower
CPU usage to saner levels (<10% on Atom).
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_mac.c | 40 ++++++++++++++++++++------------
1 files changed, 25 insertions(+), 15 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index d4b4036..bee3e21 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -602,11 +602,18 @@ static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs,
static int zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon)
{
struct zd_mac *mac = zd_hw_mac(hw);
- int r, ret;
+ int r, ret, num_cmds, req_pos = 0;
u32 tmp, j = 0;
/* 4 more bytes for tail CRC */
u32 full_len = beacon->len + 4;
unsigned long end_jiffies, message_jiffies;
+ struct zd_ioreq32 *ioreqs;
+
+ /* Alloc memory for full beacon write at once. */
+ num_cmds = 1 + zd_chip_is_zd1211b(&mac->chip) + full_len;
+ ioreqs = kmalloc(num_cmds * sizeof(struct zd_ioreq32), GFP_KERNEL);
+ if (!ioreqs)
+ return -ENOMEM;
mutex_lock(&mac->chip.mutex);
@@ -637,29 +644,31 @@ static int zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon)
msleep(20);
}
- r = zd_iowrite32_locked(&mac->chip, full_len - 1, CR_BCN_FIFO);
- if (r < 0)
- goto release_sema;
+ ioreqs[req_pos].addr = CR_BCN_FIFO;
+ ioreqs[req_pos].value = full_len - 1;
+ req_pos++;
if (zd_chip_is_zd1211b(&mac->chip)) {
- r = zd_iowrite32_locked(&mac->chip, full_len - 1,
- CR_BCN_LENGTH);
- if (r < 0)
- goto release_sema;
+ ioreqs[req_pos].addr = CR_BCN_LENGTH;
+ ioreqs[req_pos].value = full_len - 1;
+ req_pos++;
}
for (j = 0 ; j < beacon->len; j++) {
- r = zd_iowrite32_locked(&mac->chip, *((u8 *)(beacon->data + j)),
- CR_BCN_FIFO);
- if (r < 0)
- goto release_sema;
+ ioreqs[req_pos].addr = CR_BCN_FIFO;
+ ioreqs[req_pos].value = *((u8 *)(beacon->data + j));
+ req_pos++;
}
for (j = 0; j < 4; j++) {
- r = zd_iowrite32_locked(&mac->chip, 0x0, CR_BCN_FIFO);
- if (r < 0)
- goto release_sema;
+ ioreqs[req_pos].addr = CR_BCN_FIFO;
+ ioreqs[req_pos].value = 0x0;
+ req_pos++;
}
+ BUG_ON(req_pos != num_cmds);
+
+ r = zd_iowrite32a_locked(&mac->chip, ioreqs, num_cmds);
+
release_sema:
/*
* Try very hard to release device beacon semaphore, as otherwise
@@ -694,6 +703,7 @@ release_sema:
CR_BCN_PLCP_CFG);
out:
mutex_unlock(&mac->chip.mutex);
+ kfree(ioreqs);
return r;
}
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 13/17] zd1211rw: use stack for small cmd-buffers
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (11 preceding siblings ...)
2011-01-04 23:49 ` [RFC PATCH 12/17] zd1211rw: batch beacon config commands together Jussi Kivilinna
@ 2011-01-04 23:49 ` Jussi Kivilinna
2011-01-05 9:27 ` Johannes Berg
2011-01-04 23:49 ` [RFC PATCH 14/17] zd1211rw: lower hw command timeouts Jussi Kivilinna
` (3 subsequent siblings)
16 siblings, 1 reply; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:49 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
Use stack for allocing small < 64 byte arrays.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_chip.c | 43 +++++++---------------
drivers/net/wireless/zd1211rw/zd_usb.c | 60 ++++++++++++-------------------
2 files changed, 38 insertions(+), 65 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index fa90d06..8a286b8 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -108,24 +108,18 @@ int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr
{
int r;
int i;
- zd_addr_t *a16;
- u16 *v16;
+ zd_addr_t a16[USB_MAX_IOREAD32_COUNT * 2];
+ u16 v16[USB_MAX_IOREAD32_COUNT * 2];
unsigned int count16;
+ /* Use stack for values and addresses. */
+
if (count > USB_MAX_IOREAD32_COUNT)
return -EINVAL;
- /* Allocate a single memory block for values and addresses. */
- count16 = 2*count;
- a16 = (zd_addr_t *) kmalloc(count16 * (sizeof(zd_addr_t) + sizeof(u16)),
- GFP_KERNEL);
- if (!a16) {
- dev_dbg_f(zd_chip_dev(chip),
- "error ENOMEM in allocation of a16\n");
- r = -ENOMEM;
- goto out;
- }
- v16 = (u16 *)(a16 + count16);
+ count16 = 2 * count;
+ BUG_ON(count16 * sizeof(zd_addr_t) > sizeof(a16));
+ BUG_ON(count16 * sizeof(u16) > sizeof(v16));
for (i = 0; i < count; i++) {
int j = 2*i;
@@ -138,7 +132,7 @@ int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr
if (r) {
dev_dbg_f(zd_chip_dev(chip),
"error: zd_ioread16v_locked. Error number %d\n", r);
- goto out;
+ return r;
}
for (i = 0; i < count; i++) {
@@ -146,18 +140,18 @@ int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr
values[i] = (v16[j] << 16) | v16[j+1];
}
-out:
- kfree((void *)a16);
- return r;
+ return 0;
}
int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
unsigned int count)
{
int i, j, r;
- struct zd_ioreq16 *ioreqs16;
+ struct zd_ioreq16 ioreqs16[USB_MAX_IOWRITE32_COUNT * 2];
unsigned int count16;
+ /* Use stack for values and addresses. */
+
ZD_ASSERT(mutex_is_locked(&chip->mutex));
if (count == 0)
@@ -165,15 +159,8 @@ int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
if (count > USB_MAX_IOWRITE32_COUNT)
return -EINVAL;
- /* Allocate a single memory block for values and addresses. */
- count16 = 2*count;
- ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_KERNEL);
- if (!ioreqs16) {
- r = -ENOMEM;
- dev_dbg_f(zd_chip_dev(chip),
- "error %d in ioreqs16 allocation\n", r);
- goto out;
- }
+ count16 = 2 * count;
+ BUG_ON(count16 * sizeof(struct zd_ioreq16) > sizeof(ioreqs16));
for (i = 0; i < count; i++) {
j = 2*i;
@@ -191,8 +178,6 @@ int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
"error %d in zd_usb_write16v\n", r);
}
#endif /* DEBUG */
-out:
- kfree(ioreqs16);
return r;
}
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index ed5379b..13a089b 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -1358,10 +1358,12 @@ error_unlock:
int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
const zd_addr_t *addresses, unsigned int count)
{
+ unsigned char buf[sizeof(struct usb_req_read_regs) +
+ USB_MAX_IOREAD16_COUNT * sizeof(__le16)];
int r;
int i, req_len, actual_req_len;
struct usb_device *udev;
- struct usb_req_read_regs *req = NULL;
+ struct usb_req_read_regs *req = (void *)buf;
unsigned long timeout;
if (count < 1) {
@@ -1386,9 +1388,8 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
}
req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16);
- req = kmalloc(req_len, GFP_KERNEL);
- if (!req)
- return -ENOMEM;
+ BUG_ON(req_len > sizeof(buf));
+
req->id = cpu_to_le16(USB_REQ_READ_REGS);
for (i = 0; i < count; i++)
req->addr[i] = cpu_to_le16((u16)addresses[i]);
@@ -1400,14 +1401,13 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
if (r) {
dev_dbg_f(zd_usb_dev(usb),
"error in usb_bulk_msg(). Error number %d\n", r);
- goto error;
+ return r;
}
if (req_len != actual_req_len) {
dev_dbg_f(zd_usb_dev(usb), "error in usb_bulk_msg()\n"
" req_len %d != actual_req_len %d\n",
req_len, actual_req_len);
- r = -EIO;
- goto error;
+ return -EIO;
}
timeout = wait_for_completion_timeout(&usb->intr.read_regs.completion,
@@ -1415,22 +1415,20 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
if (!timeout) {
disable_read_regs_int(usb);
dev_dbg_f(zd_usb_dev(usb), "read timed out\n");
- r = -ETIMEDOUT;
- goto error;
+ return -ETIMEDOUT;
}
- r = get_results(usb, values, req, count);
-error:
- kfree(req);
- return r;
+ return get_results(usb, values, req, count);
}
int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
unsigned int count)
{
+ unsigned char buf[sizeof(struct usb_req_write_regs) +
+ USB_MAX_IOWRITE16_COUNT * sizeof(struct reg_data)];
int r;
struct usb_device *udev;
- struct usb_req_write_regs *req = NULL;
+ struct usb_req_write_regs *req = (void *)buf;
int i, req_len, actual_req_len;
if (count == 0)
@@ -1449,9 +1447,7 @@ int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
req_len = sizeof(struct usb_req_write_regs) +
count * sizeof(struct reg_data);
- req = kmalloc(req_len, GFP_KERNEL);
- if (!req)
- return -ENOMEM;
+ BUG_ON(req_len > sizeof(buf));
req->id = cpu_to_le16(USB_REQ_WRITE_REGS);
for (i = 0; i < count; i++) {
@@ -1466,28 +1462,26 @@ int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
if (r) {
dev_dbg_f(zd_usb_dev(usb),
"error in usb_bulk_msg(). Error number %d\n", r);
- goto error;
+ return r;
}
if (req_len != actual_req_len) {
dev_dbg_f(zd_usb_dev(usb),
"error in usb_bulk_msg()"
" req_len %d != actual_req_len %d\n",
req_len, actual_req_len);
- r = -EIO;
- goto error;
+ return -EIO;
}
- /* FALL-THROUGH with r == 0 */
-error:
- kfree(req);
- return r;
+ return 0;
}
int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits)
{
+ unsigned char buf[sizeof(struct usb_req_rfwrite) +
+ USB_MAX_RFWRITE_BIT_COUNT * sizeof(__le16)];
int r;
struct usb_device *udev;
- struct usb_req_rfwrite *req = NULL;
+ struct usb_req_rfwrite *req = (void *)buf;
int i, req_len, actual_req_len;
u16 bit_value_template;
@@ -1524,14 +1518,12 @@ int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits)
if (r) {
dev_dbg_f(zd_usb_dev(usb),
"error %d: Couldn't read CR203\n", r);
- goto out;
+ return r;
}
bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA);
req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16);
- req = kmalloc(req_len, GFP_KERNEL);
- if (!req)
- return -ENOMEM;
+ BUG_ON(req_len > sizeof(buf));
req->id = cpu_to_le16(USB_REQ_WRITE_RF);
/* 1: 3683a, but not used in ZYDAS driver */
@@ -1551,18 +1543,14 @@ int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits)
if (r) {
dev_dbg_f(zd_usb_dev(usb),
"error in usb_bulk_msg(). Error number %d\n", r);
- goto out;
+ return r;
}
if (req_len != actual_req_len) {
dev_dbg_f(zd_usb_dev(usb), "error in usb_bulk_msg()"
" req_len %d != actual_req_len %d\n",
req_len, actual_req_len);
- r = -EIO;
- goto out;
+ return -EIO;
}
- /* FALL-THROUGH with r == 0 */
-out:
- kfree(req);
- return r;
+ return 0;
}
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 14/17] zd1211rw: lower hw command timeouts
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (12 preceding siblings ...)
2011-01-04 23:49 ` [RFC PATCH 13/17] zd1211rw: use stack for small cmd-buffers Jussi Kivilinna
@ 2011-01-04 23:49 ` Jussi Kivilinna
2011-01-04 23:49 ` [RFC PATCH 15/17] zd1211rw: collect driver settings and add function to restore theim Jussi Kivilinna
` (2 subsequent siblings)
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:49 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
Device command timeouts are set up very high (1 sec) and this causes
AP beacon to lock up for long for example. Checking timeouts on device
it's easy to see that 1 sec timeout is not needed, when device fails
to response longer timeout doesn't help:
[ 473.074419] zd1211rw 1-1:1.0: print_times() Read times:
[ 473.175163] zd1211rw 1-1:1.0: print_time() 0 - 10 msec: 1506
[ 473.176429] zd1211rw 1-1:1.0: print_time() 11 - 50 msec: 0
[ 473.177955] zd1211rw 1-1:1.0: print_time() 51 - 100 msec: 0
[ 473.180703] zd1211rw 1-1:1.0: print_time() 101 - 250 msec: 0
[ 473.182101] zd1211rw 1-1:1.0: print_time() 251 - 500 msec: 0
[ 473.183221] zd1211rw 1-1:1.0: print_time() 500 - 1000 msec: 20
[ 473.184381] zd1211rw 1-1:1.0: print_time() 1000 - ... msec: 18
Also vendor driver doesn't use this long timeout. Therefore change
timeout to 50msec.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_usb.c | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index 13a089b..b709a9d 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -1397,7 +1397,7 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
udev = zd_usb_to_usbdev(usb);
prepare_read_regs_int(usb);
r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT),
- req, req_len, &actual_req_len, 1000 /* ms */);
+ req, req_len, &actual_req_len, 50 /* ms */);
if (r) {
dev_dbg_f(zd_usb_dev(usb),
"error in usb_bulk_msg(). Error number %d\n", r);
@@ -1411,7 +1411,7 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
}
timeout = wait_for_completion_timeout(&usb->intr.read_regs.completion,
- msecs_to_jiffies(1000));
+ msecs_to_jiffies(50));
if (!timeout) {
disable_read_regs_int(usb);
dev_dbg_f(zd_usb_dev(usb), "read timed out\n");
@@ -1458,7 +1458,7 @@ int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
udev = zd_usb_to_usbdev(usb);
r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT),
- req, req_len, &actual_req_len, 1000 /* ms */);
+ req, req_len, &actual_req_len, 50 /* ms */);
if (r) {
dev_dbg_f(zd_usb_dev(usb),
"error in usb_bulk_msg(). Error number %d\n", r);
@@ -1539,7 +1539,7 @@ int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits)
udev = zd_usb_to_usbdev(usb);
r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT),
- req, req_len, &actual_req_len, 1000 /* ms */);
+ req, req_len, &actual_req_len, 50 /* ms */);
if (r) {
dev_dbg_f(zd_usb_dev(usb),
"error in usb_bulk_msg(). Error number %d\n", r);
@@ -1554,3 +1554,4 @@ int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits)
return 0;
}
+
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 15/17] zd1211rw: collect driver settings and add function to restore theim
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (13 preceding siblings ...)
2011-01-04 23:49 ` [RFC PATCH 14/17] zd1211rw: lower hw command timeouts Jussi Kivilinna
@ 2011-01-04 23:49 ` Jussi Kivilinna
2011-01-04 23:50 ` [RFC PATCH 16/17] zd1211rw: add tx watchdog Jussi Kivilinna
2011-01-04 23:50 ` [RFC PATCH 17/17] zd1211rw: enable NL80211_IFTYPE_AP Jussi Kivilinna
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:49 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
We need HW hard reset later in patchset to reset device after TX-stall.
Collect all settings that we have set to driver for later reset and
add restore function.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_mac.c | 69 ++++++++++++++++++++++++++++++++
drivers/net/wireless/zd1211rw/zd_mac.h | 1
2 files changed, 70 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index bee3e21..4c3e92a 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -141,6 +141,9 @@ static void housekeeping_disable(struct zd_mac *mac);
static void beacon_init(struct zd_mac *mac);
static void beacon_enable(struct zd_mac *mac);
static void beacon_disable(struct zd_mac *mac);
+static void set_rts_cts(struct zd_mac *mac, unsigned int short_preamble);
+static int zd_mac_config_beacon(struct ieee80211_hw *hw,
+ struct sk_buff *beacon);
static int zd_reg2alpha2(u8 regdomain, char *alpha2)
{
@@ -339,6 +342,68 @@ static void zd_op_stop(struct ieee80211_hw *hw)
dev_kfree_skb_any(skb);
}
+static int zd_restore_settings(struct zd_mac *mac)
+{
+ struct sk_buff *beacon;
+ struct zd_mc_hash multicast_hash;
+ unsigned int short_preamble;
+ int r, beacon_interval, beacon_period;
+ u8 channel;
+
+ dev_dbg_f(zd_mac_dev(mac), "\n");
+
+ spin_lock_irq(&mac->lock);
+ multicast_hash = mac->multicast_hash;
+ short_preamble = mac->short_preamble;
+ beacon_interval = mac->beacon.interval;
+ beacon_period = mac->beacon.period;
+ channel = mac->channel;
+ spin_unlock_irq(&mac->lock);
+
+ r = set_mac_and_bssid(mac);
+ if (r < 0) {
+ dev_dbg_f(zd_mac_dev(mac), "set_mac_and_bssid failed, %d\n", r);
+ return r;
+ }
+
+ r = zd_chip_set_channel(&mac->chip, channel);
+ if (r < 0) {
+ dev_dbg_f(zd_mac_dev(mac), "zd_chip_set_channel failed, %d\n",
+ r);
+ return r;
+ }
+
+ set_rts_cts(mac, short_preamble);
+
+ r = zd_chip_set_multicast_hash(&mac->chip, &multicast_hash);
+ if (r < 0) {
+ dev_dbg_f(zd_mac_dev(mac),
+ "zd_chip_set_multicast_hash failed, %d\n", r);
+ return r;
+ }
+
+ if (mac->type == NL80211_IFTYPE_MESH_POINT ||
+ mac->type == NL80211_IFTYPE_ADHOC ||
+ mac->type == NL80211_IFTYPE_AP) {
+ if (mac->vif != NULL) {
+ beacon = ieee80211_beacon_get(mac->hw, mac->vif);
+ if (beacon) {
+ zd_mac_config_beacon(mac->hw, beacon);
+ kfree_skb(beacon);
+ }
+ }
+
+ zd_set_beacon_interval(&mac->chip, beacon_interval,
+ beacon_period, mac->type);
+
+ spin_lock_irq(&mac->lock);
+ mac->beacon.last_update = jiffies;
+ spin_unlock_irq(&mac->lock);
+ }
+
+ return 0;
+}
+
/**
* zd_mac_tx_status - reports tx status of a packet if required
* @hw - a &struct ieee80211_hw pointer
@@ -1008,6 +1073,10 @@ static int zd_op_config(struct ieee80211_hw *hw, u32 changed)
struct zd_mac *mac = zd_hw_mac(hw);
struct ieee80211_conf *conf = &hw->conf;
+ spin_lock_irq(&mac->lock);
+ mac->channel = conf->channel->hw_value;
+ spin_unlock_irq(&mac->lock);
+
return zd_chip_set_channel(&mac->chip, conf->channel->hw_value);
}
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index 4b1edb2..155d4b8 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -192,6 +192,7 @@ struct zd_mac {
u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
u8 regdomain;
u8 default_regdomain;
+ u8 channel;
int type;
int associated;
unsigned long flags;
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 16/17] zd1211rw: add tx watchdog
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (14 preceding siblings ...)
2011-01-04 23:49 ` [RFC PATCH 15/17] zd1211rw: collect driver settings and add function to restore theim Jussi Kivilinna
@ 2011-01-04 23:50 ` Jussi Kivilinna
2011-01-04 23:50 ` [RFC PATCH 17/17] zd1211rw: enable NL80211_IFTYPE_AP Jussi Kivilinna
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:50 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
When doing transfers at high speed for long time, tx queue can freeze. So add
tx watchdog. TX-watchdog checks for locked tx-urbs and reset hardware when
such is detected. Merely unlinking urb was not enough, device have to be
reseted. Hw settings are restored so that any open link will stay on after
reset.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_chip.c | 2
drivers/net/wireless/zd1211rw/zd_mac.c | 6 +
drivers/net/wireless/zd1211rw/zd_mac.h | 4 +
drivers/net/wireless/zd1211rw/zd_usb.c | 157 +++++++++++++++++++++++++++++++
drivers/net/wireless/zd1211rw/zd_usb.h | 9 ++
5 files changed, 173 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 8a286b8..bcc8ffc 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -1449,6 +1449,7 @@ int zd_chip_enable_rxtx(struct zd_chip *chip)
mutex_lock(&chip->mutex);
zd_usb_enable_tx(&chip->usb);
r = zd_usb_enable_rx(&chip->usb);
+ zd_tx_watchdog_enable(&chip->usb);
mutex_unlock(&chip->mutex);
return r;
}
@@ -1456,6 +1457,7 @@ int zd_chip_enable_rxtx(struct zd_chip *chip)
void zd_chip_disable_rxtx(struct zd_chip *chip)
{
mutex_lock(&chip->mutex);
+ zd_tx_watchdog_disable(&chip->usb);
zd_usb_disable_rx(&chip->usb);
zd_usb_disable_tx(&chip->usb);
mutex_unlock(&chip->mutex);
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 4c3e92a..1fc733c 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -264,7 +264,7 @@ static int set_mc_hash(struct zd_mac *mac)
return zd_chip_set_multicast_hash(&mac->chip, &hash);
}
-static int zd_op_start(struct ieee80211_hw *hw)
+int zd_op_start(struct ieee80211_hw *hw)
{
struct zd_mac *mac = zd_hw_mac(hw);
struct zd_chip *chip = &mac->chip;
@@ -314,7 +314,7 @@ out:
return r;
}
-static void zd_op_stop(struct ieee80211_hw *hw)
+void zd_op_stop(struct ieee80211_hw *hw)
{
struct zd_mac *mac = zd_hw_mac(hw);
struct zd_chip *chip = &mac->chip;
@@ -342,7 +342,7 @@ static void zd_op_stop(struct ieee80211_hw *hw)
dev_kfree_skb_any(skb);
}
-static int zd_restore_settings(struct zd_mac *mac)
+int zd_restore_settings(struct zd_mac *mac)
{
struct sk_buff *beacon;
struct zd_mc_hash multicast_hash;
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index 155d4b8..d769490 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -316,6 +316,10 @@ int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length);
void zd_mac_tx_failed(struct urb *urb);
void zd_mac_tx_to_dev(struct sk_buff *skb, int error);
+int zd_op_start(struct ieee80211_hw *hw);
+void zd_op_stop(struct ieee80211_hw *hw);
+int zd_restore_settings(struct zd_mac *mac);
+
#ifdef DEBUG
void zd_dump_rx_status(const struct rx_status *status);
#else
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index b709a9d..1ed7e12 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -605,6 +605,7 @@ static void rx_urb_complete(struct urb *urb)
struct zd_usb_rx *rx;
const u8 *buffer;
unsigned int length;
+ int r;
switch (urb->status) {
case 0:
@@ -654,7 +655,9 @@ static void rx_urb_complete(struct urb *urb)
}
resubmit:
- usb_submit_urb(urb, GFP_ATOMIC);
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r)
+ dev_dbg_f(urb_dev(urb), "urb %p resubmit error %d\n", urb, r);
}
static struct urb *alloc_rx_urb(struct zd_usb *usb)
@@ -950,6 +953,7 @@ int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb)
skb->data, skb->len, tx_urb_complete, skb);
info->rate_driver_data[1] = urb;
+ info->rate_driver_data[2] = (void *)jiffies;
skb_queue_tail(&tx->submitted_queue, skb);
r = usb_submit_urb(urb, GFP_ATOMIC);
@@ -965,6 +969,75 @@ out:
return r;
}
+static bool zd_tx_timeout(struct zd_usb *usb)
+{
+ struct zd_usb_tx *tx = &usb->tx;
+ struct sk_buff_head *q = &tx->submitted_queue;
+ struct sk_buff *skb, *skbnext;
+ struct ieee80211_tx_info *info;
+ unsigned long trans_start;
+ bool have_timedout = false;
+
+ spin_lock_irq(&tx->lock);
+ spin_lock(&q->lock);
+ skb_queue_walk_safe(q, skb, skbnext) {
+ info = IEEE80211_SKB_CB(skb);
+ trans_start = (unsigned long)info->rate_driver_data[2];
+
+ if (time_is_before_jiffies(trans_start + ZD_TX_TIMEOUT)) {
+ have_timedout = true;
+ break;
+ }
+ }
+ spin_unlock(&q->lock);
+ spin_unlock_irq(&tx->lock);
+
+ return have_timedout;
+}
+
+static void tx_watchdog_handler(struct work_struct *work)
+{
+ struct zd_usb *usb =
+ container_of(work, struct zd_usb, tx_watchdog_work.work);
+ struct zd_usb_tx *tx = &usb->tx;
+
+ if (!usb->watchdog_enabled || !tx->enabled)
+ goto out;
+ if (!zd_tx_timeout(usb))
+ goto out;
+
+ /* TX halted, try reset */
+ dev_warn(zd_usb_dev(usb), "TX-stall detected, reseting device...");
+
+ usb_queue_reset_device(usb->intf);
+
+ /* reset will stop this worker, don't rearm */
+ return;
+out:
+ queue_delayed_work(zd_workqueue, &usb->tx_watchdog_work,
+ ZD_TX_WATCHDOG_INTERVAL);
+}
+
+void zd_tx_watchdog_enable(struct zd_usb *usb)
+{
+ if (!usb->watchdog_enabled) {
+ dev_dbg_f(zd_usb_dev(usb), "\n");
+ queue_delayed_work(zd_workqueue, &usb->tx_watchdog_work,
+ ZD_TX_WATCHDOG_INTERVAL);
+ usb->watchdog_enabled = 1;
+ }
+}
+
+void zd_tx_watchdog_disable(struct zd_usb *usb)
+{
+ if (usb->watchdog_enabled) {
+ dev_dbg_f(zd_usb_dev(usb), "\n");
+ usb->watchdog_enabled = 0;
+ cancel_rearming_delayed_workqueue(zd_workqueue,
+ &usb->tx_watchdog_work);
+ }
+}
+
static inline void init_usb_interrupt(struct zd_usb *usb)
{
struct zd_usb_interrupt *intr = &usb->intr;
@@ -1006,6 +1079,7 @@ void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw,
init_usb_interrupt(usb);
init_usb_tx(usb);
init_usb_rx(usb);
+ INIT_DELAYED_WORK(&usb->tx_watchdog_work, tx_watchdog_handler);
}
void zd_usb_clear(struct zd_usb *usb)
@@ -1242,11 +1316,92 @@ static void disconnect(struct usb_interface *intf)
dev_dbg(&intf->dev, "disconnected\n");
}
+static void zd_usb_resume(struct zd_usb *usb)
+{
+ struct zd_mac *mac = zd_usb_to_mac(usb);
+ int r;
+
+ dev_dbg_f(zd_usb_dev(usb), "\n");
+
+ r = zd_op_start(zd_usb_to_hw(usb));
+ if (r < 0) {
+ dev_warn(zd_usb_dev(usb), "Device resume failed "
+ "with error code %d. Retrying...\n", r);
+ if (usb->was_running)
+ set_bit(ZD_DEVICE_RUNNING, &mac->flags);
+ usb_queue_reset_device(usb->intf);
+ return;
+ }
+
+ if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+ r = zd_restore_settings(mac);
+ if (r < 0) {
+ dev_dbg(zd_usb_dev(usb),
+ "failed to restore settings, %d\n", r);
+ return;
+ }
+ }
+}
+
+static void zd_usb_stop(struct zd_usb *usb)
+{
+ dev_dbg_f(zd_usb_dev(usb), "\n");
+
+ zd_op_stop(zd_usb_to_hw(usb));
+
+ zd_usb_disable_tx(usb);
+ zd_usb_disable_rx(usb);
+ zd_usb_disable_int(usb);
+
+ usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+ struct ieee80211_hw *hw = usb_get_intfdata(intf);
+ struct zd_mac *mac;
+ struct zd_usb *usb;
+
+ if (!hw || intf->condition != USB_INTERFACE_BOUND)
+ return 0;
+
+ mac = zd_hw_mac(hw);
+ usb = &mac->chip.usb;
+
+ usb->was_running = test_bit(ZD_DEVICE_RUNNING, &mac->flags);
+
+ zd_usb_stop(usb);
+
+ mutex_lock(&mac->chip.mutex);
+ return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+ struct ieee80211_hw *hw = usb_get_intfdata(intf);
+ struct zd_mac *mac;
+ struct zd_usb *usb;
+
+ if (!hw || intf->condition != USB_INTERFACE_BOUND)
+ return 0;
+
+ mac = zd_hw_mac(hw);
+ usb = &mac->chip.usb;
+
+ mutex_unlock(&mac->chip.mutex);
+
+ if (usb->was_running)
+ zd_usb_resume(usb);
+ return 0;
+}
+
static struct usb_driver driver = {
.name = KBUILD_MODNAME,
.id_table = usb_ids,
.probe = probe,
.disconnect = disconnect,
+ .pre_reset = pre_reset,
+ .post_reset = post_reset,
};
struct workqueue_struct *zd_workqueue;
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h
index b71baa0..31ca686 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.h
+++ b/drivers/net/wireless/zd1211rw/zd_usb.h
@@ -32,6 +32,9 @@
#define ZD_USB_TX_HIGH 5
#define ZD_USB_TX_LOW 2
+#define ZD_TX_TIMEOUT (HZ * 5)
+#define ZD_TX_WATCHDOG_INTERVAL round_jiffies_relative(HZ)
+
enum devicetype {
DEVICE_ZD1211 = 0,
DEVICE_ZD1211B = 1,
@@ -206,7 +209,8 @@ struct zd_usb {
struct zd_usb_rx rx;
struct zd_usb_tx tx;
struct usb_interface *intf;
- u8 is_zd1211b:1, initialized:1;
+ struct delayed_work tx_watchdog_work;
+ u8 is_zd1211b:1, initialized:1, watchdog_enabled:1, was_running:1;
};
#define zd_usb_dev(usb) (&usb->intf->dev)
@@ -233,6 +237,9 @@ void zd_usb_clear(struct zd_usb *usb);
int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size);
+void zd_tx_watchdog_enable(struct zd_usb *usb);
+void zd_tx_watchdog_disable(struct zd_usb *usb);
+
int zd_usb_enable_int(struct zd_usb *usb);
void zd_usb_disable_int(struct zd_usb *usb);
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [RFC PATCH 17/17] zd1211rw: enable NL80211_IFTYPE_AP
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
` (15 preceding siblings ...)
2011-01-04 23:50 ` [RFC PATCH 16/17] zd1211rw: add tx watchdog Jussi Kivilinna
@ 2011-01-04 23:50 ` Jussi Kivilinna
16 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-04 23:50 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Drake, Ulrich Kunitz
It should be safe to enable AP-mode now.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
drivers/net/wireless/zd1211rw/zd_mac.c | 7 +++++--
1 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 1fc733c..73006ac 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -1047,6 +1047,7 @@ static int zd_op_add_interface(struct ieee80211_hw *hw,
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP:
mac->type = vif->type;
break;
default:
@@ -1219,7 +1220,8 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw,
dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes);
if (mac->type == NL80211_IFTYPE_MESH_POINT ||
- mac->type == NL80211_IFTYPE_ADHOC) {
+ mac->type == NL80211_IFTYPE_ADHOC ||
+ mac->type == NL80211_IFTYPE_AP) {
associated = true;
if (changes & BSS_CHANGED_BEACON) {
struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
@@ -1323,7 +1325,8 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
hw->wiphy->interface_modes =
BIT(NL80211_IFTYPE_MESH_POINT) |
BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC);
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP);
hw->max_signal = 100;
hw->queues = 1;
^ permalink raw reply related [flat|nested] 30+ messages in thread
* Re: [RFC PATCH 13/17] zd1211rw: use stack for small cmd-buffers
2011-01-04 23:49 ` [RFC PATCH 13/17] zd1211rw: use stack for small cmd-buffers Jussi Kivilinna
@ 2011-01-05 9:27 ` Johannes Berg
2011-01-06 18:55 ` Dan Williams
0 siblings, 1 reply; 30+ messages in thread
From: Johannes Berg @ 2011-01-05 9:27 UTC (permalink / raw)
To: Jussi Kivilinna; +Cc: linux-wireless, Daniel Drake, Ulrich Kunitz
On Wed, 2011-01-05 at 01:49 +0200, Jussi Kivilinna wrote:
> Use stack for allocing small < 64 byte arrays.
I don't think this is valid -- DMA memory can't be from the stack.
johannes
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH 13/17] zd1211rw: use stack for small cmd-buffers
2011-01-05 9:27 ` Johannes Berg
@ 2011-01-06 18:55 ` Dan Williams
2011-01-06 21:53 ` Jussi Kivilinna
0 siblings, 1 reply; 30+ messages in thread
From: Dan Williams @ 2011-01-06 18:55 UTC (permalink / raw)
To: Johannes Berg
Cc: Jussi Kivilinna, linux-wireless, Daniel Drake, Ulrich Kunitz
On Wed, 2011-01-05 at 10:27 +0100, Johannes Berg wrote:
> On Wed, 2011-01-05 at 01:49 +0200, Jussi Kivilinna wrote:
> > Use stack for allocing small < 64 byte arrays.
>
> I don't think this is valid -- DMA memory can't be from the stack.
It's almost certainly not valid. You'll get BUGs from the kernel in
this case, though it'll still probably work.
Dan
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc()
2011-01-04 23:49 ` [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc() Jussi Kivilinna
@ 2011-01-06 21:46 ` Christian Lamparter
2011-01-06 21:55 ` Johannes Berg
2011-01-09 15:46 ` Jussi Kivilinna
0 siblings, 2 replies; 30+ messages in thread
From: Christian Lamparter @ 2011-01-06 21:46 UTC (permalink / raw)
To: Jussi Kivilinna; +Cc: linux-wireless, Daniel Drake, Ulrich Kunitz
On Wednesday 05 January 2011 00:49:10 Jussi Kivilinna wrote:
> diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
> index aace010..a3c7e8f 100644
> --- a/drivers/net/wireless/zd1211rw/zd_mac.c
> +++ b/drivers/net/wireless/zd1211rw/zd_mac.c
> @@ -958,16 +958,46 @@ static int zd_op_config(struct ieee80211_hw *hw, u32 changed)
> return zd_chip_set_channel(&mac->chip, conf->channel->hw_value);
> }
>
> +static void zd_beacon_done(struct zd_mac *mac)
> +{
this is an interesting one...
Since zd_beacon_done also uploads the next beacon so long in advance,
there could be an equally long race between the outdated state of the
next beacon's DTIM broadcast traffic indicator (802.11-2007 7.3.2.6)
which -in your case- was uploaded almost a beacon interval ago and
the xmit of ieee80211_get_buffered_bc *now*.
The dtim bc/mc bit might be not set, when a mc/bc arrived after the
beacon was uploaded, but before the "beacon done event" from the
hardware. So, dozing stations don't expect the broadcast traffic
and of course, they might miss it completely.
It's probably better to fix this in mac80211 (see the attached hack).
> + /*
> + * Send out buffered broad- and multicast frames.
> + */
> + while (!ieee80211_queue_stopped(mac->hw, 0)) {
> + skb = ieee80211_get_buffered_bc(mac->hw, mac->vif);
> + if (!skb)
> + break;
> + zd_op_tx(mac->hw, skb);
> + }
> +
> + /*
> + * Fetch next beacon so that tim_count is updated.
> + */
> + beacon = ieee80211_beacon_get(mac->hw, mac->vif);
> + if (!beacon)
> + return;
Ideally, you should assign a proper sequence # to beacon frames too.
(802.11-2007 7.1.3.4.1)
But as before, the long time between the upload and the actual beacon
xmit by the hardware make things really difficult. If you just call
"create_tx_desc_seq" right now, "theoretically" you have to buffer
all management and other Non-QoS data frames until the beacon was sent.
However, no one would do that :D. Either the hardware/firmware
controls the sequence counter or it's simply not implemented
(and nobody cares ;) ).
In fact, you could just as well drop "[09/17] zd1211rw: implement
seq_num for IEEE80211_TX_CTL_ASSIGN_SEQ"... unless of course, I'm
an idiot and there is a really clever way around these issues.
Regards,
Chr
---
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index c47d7c0..f71ed31 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -225,6 +225,7 @@ struct ieee80211_if_ap {
struct sk_buff_head ps_bc_buf;
atomic_t num_sta_ps; /* number of stations in PS mode */
int dtim_count;
+ bool dtim_bc_mc;
};
struct ieee80211_if_wds {
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 5950e3a..26b688b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2178,6 +2178,8 @@ static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss,
if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf))
aid0 = 1;
+ bss->dtim_bc_mc = aid0 == 1;
+
if (have_bits) {
/* Find largest even number N1 so that bits numbered 1 through
* (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits
@@ -2540,7 +2542,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
if (sdata->vif.type != NL80211_IFTYPE_AP || !beacon || !beacon->head)
goto out;
- if (bss->dtim_count != 0)
+ if (bss->dtim_count != 0 || !bss->dtim_bc_mc)
goto out; /* send buffered bc/mc only after DTIM beacon */
while (1) {
^ permalink raw reply related [flat|nested] 30+ messages in thread
* Re: [RFC PATCH 13/17] zd1211rw: use stack for small cmd-buffers
2011-01-06 18:55 ` Dan Williams
@ 2011-01-06 21:53 ` Jussi Kivilinna
0 siblings, 0 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-06 21:53 UTC (permalink / raw)
To: Dan Williams; +Cc: Johannes Berg, linux-wireless, Daniel Drake, Ulrich Kunitz
Quoting Dan Williams <dcbw@redhat.com>:
> On Wed, 2011-01-05 at 10:27 +0100, Johannes Berg wrote:
>> On Wed, 2011-01-05 at 01:49 +0200, Jussi Kivilinna wrote:
>> > Use stack for allocing small < 64 byte arrays.
>>
>> I don't think this is valid -- DMA memory can't be from the stack.
Oh, my reply to Johannes didn't go to list.
>
> It's almost certainly not valid. You'll get BUGs from the kernel in
> this case, though it'll still probably work.
Ok, will not use stack for urb transfer_buffers.
-Jussi
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc()
2011-01-06 21:46 ` Christian Lamparter
@ 2011-01-06 21:55 ` Johannes Berg
2011-01-09 15:46 ` Jussi Kivilinna
1 sibling, 0 replies; 30+ messages in thread
From: Johannes Berg @ 2011-01-06 21:55 UTC (permalink / raw)
To: Christian Lamparter
Cc: Jussi Kivilinna, linux-wireless, Daniel Drake, Ulrich Kunitz
On Thu, 2011-01-06 at 22:46 +0100, Christian Lamparter wrote:
> Ideally, you should assign a proper sequence # to beacon frames too.
> (802.11-2007 7.1.3.4.1)
>
> But as before, the long time between the upload and the actual beacon
> xmit by the hardware make things really difficult. If you just call
> "create_tx_desc_seq" right now, "theoretically" you have to buffer
> all management and other Non-QoS data frames until the beacon was sent.
>
> However, no one would do that :D. Either the hardware/firmware
> controls the sequence counter or it's simply not implemented
> (and nobody cares ;) ).
Yeah ... it turns out that nobody cares. It's probably easier/better to
actually send beacons with seqno 0 instead of trying to hack around it.
> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
> index c47d7c0..f71ed31 100644
> --- a/net/mac80211/ieee80211_i.h
> +++ b/net/mac80211/ieee80211_i.h
> @@ -225,6 +225,7 @@ struct ieee80211_if_ap {
> struct sk_buff_head ps_bc_buf;
> atomic_t num_sta_ps; /* number of stations in PS mode */
> int dtim_count;
> + bool dtim_bc_mc;
> };
>
> struct ieee80211_if_wds {
> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
> index 5950e3a..26b688b 100644
> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -2178,6 +2178,8 @@ static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss,
> if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf))
> aid0 = 1;
>
> + bss->dtim_bc_mc = aid0 == 1;
> +
> if (have_bits) {
> /* Find largest even number N1 so that bits numbered 1 through
> * (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits
> @@ -2540,7 +2542,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
> if (sdata->vif.type != NL80211_IFTYPE_AP || !beacon || !beacon->head)
> goto out;
>
> - if (bss->dtim_count != 0)
> + if (bss->dtim_count != 0 || !bss->dtim_bc_mc)
> goto out; /* send buffered bc/mc only after DTIM beacon */
Hmm, interesting. That makes some sense I guess. Luckily, I have
hardware offload for this (extra queue) so I don't have to worry about
it :-)
johannes
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc()
2011-01-06 21:46 ` Christian Lamparter
2011-01-06 21:55 ` Johannes Berg
@ 2011-01-09 15:46 ` Jussi Kivilinna
2011-01-09 20:33 ` Christian Lamparter
1 sibling, 1 reply; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-09 15:46 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, Daniel Drake, Ulrich Kunitz
Quoting Christian Lamparter <chunkeey@googlemail.com>:
> On Wednesday 05 January 2011 00:49:10 Jussi Kivilinna wrote:
>> diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c
>> b/drivers/net/wireless/zd1211rw/zd_mac.c
>> index aace010..a3c7e8f 100644
>> --- a/drivers/net/wireless/zd1211rw/zd_mac.c
>> +++ b/drivers/net/wireless/zd1211rw/zd_mac.c
>> @@ -958,16 +958,46 @@ static int zd_op_config(struct ieee80211_hw
>> *hw, u32 changed)
>> return zd_chip_set_channel(&mac->chip, conf->channel->hw_value);
>> }
>>
>> +static void zd_beacon_done(struct zd_mac *mac)
>> +{
>
> this is an interesting one...
>
> Since zd_beacon_done also uploads the next beacon so long in advance,
> there could be an equally long race between the outdated state of the
> next beacon's DTIM broadcast traffic indicator (802.11-2007 7.3.2.6)
> which -in your case- was uploaded almost a beacon interval ago and
> the xmit of ieee80211_get_buffered_bc *now*.
>
> The dtim bc/mc bit might be not set, when a mc/bc arrived after the
> beacon was uploaded, but before the "beacon done event" from the
> hardware. So, dozing stations don't expect the broadcast traffic
> and of course, they might miss it completely.
>
> It's probably better to fix this in mac80211 (see the attached hack).
Ok, should I add this to my patchset?
>
> In fact, you could just as well drop "[09/17] zd1211rw: implement
> seq_num for IEEE80211_TX_CTL_ASSIGN_SEQ"... unless of course, I'm
> an idiot and there is a really clever way around these issues.
Oh well, I should have checked this more closely before doing that
patch. HW is assigning seq-numbers already and that patch is not needed.
-Jussi
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc()
2011-01-09 15:46 ` Jussi Kivilinna
@ 2011-01-09 20:33 ` Christian Lamparter
2011-01-19 18:49 ` Jussi Kivilinna
0 siblings, 1 reply; 30+ messages in thread
From: Christian Lamparter @ 2011-01-09 20:33 UTC (permalink / raw)
To: Jussi Kivilinna; +Cc: linux-wireless, Daniel Drake, Ulrich Kunitz
On Sunday 09 January 2011 16:46:56 Jussi Kivilinna wrote:
> Quoting Christian Lamparter <chunkeey@googlemail.com>:
> > On Wednesday 05 January 2011 00:49:10 Jussi Kivilinna wrote:
> >> diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c
> >> b/drivers/net/wireless/zd1211rw/zd_mac.c
> >> index aace010..a3c7e8f 100644
> >> --- a/drivers/net/wireless/zd1211rw/zd_mac.c
> >> +++ b/drivers/net/wireless/zd1211rw/zd_mac.c
> >> @@ -958,16 +958,46 @@ static int zd_op_config(struct ieee80211_hw
> >> *hw, u32 changed)
> >> return zd_chip_set_channel(&mac->chip, conf->channel->hw_value);
> >> }
> >>
> >> +static void zd_beacon_done(struct zd_mac *mac)
> >> +{
> >
> > this is an interesting one...
> >
> > Since zd_beacon_done also uploads the next beacon so long in advance,
> > there could be an equally long race between the outdated state of the
> > next beacon's DTIM broadcast traffic indicator (802.11-2007 7.3.2.6)
> > which -in your case- was uploaded almost a beacon interval ago and
> > the xmit of ieee80211_get_buffered_bc *now*.
> >
> > The dtim bc/mc bit might be not set, when a mc/bc arrived after the
> > beacon was uploaded, but before the "beacon done event" from the
> > hardware. So, dozing stations don't expect the broadcast traffic
> > and of course, they might miss it completely.
> >
> > It's probably better to fix this in mac80211 (see the attached hack).
>
> Ok, should I add this to my patchset?
well, difficult to say. As far as I can say, it should be correct for "your
case". But, on the other hand: what about solutions that can't buffer
mc/bc frames (and needs to call ieee80211_get_buffered_bc), however the
firmware is clever enough to maintain a beacon internally (so they
won't call ieee80211_beacon_get and only relies on set_tim)?
Sure, it's just a unlikely corner case... In fact, I didn't check if
that's even possible, but it does sound reasonable to some extend.
> > In fact, you could just as well drop "[09/17] zd1211rw: implement
> > seq_num for IEEE80211_TX_CTL_ASSIGN_SEQ"... unless of course, I'm
> > an idiot and there is a really clever way around these issues.
>
> Oh well, I should have checked this more closely before doing that
> patch. HW is assigning seq-numbers already and that patch is not needed.
heh, p54's fw can assign sequence numbers as well, but there's a txdesc flag
to control the counter, so the firmware does not mess with the sequence control
of QoS-Data frames. I hope zd1211* has one too.
Regards,
Chr
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc()
2011-01-09 20:33 ` Christian Lamparter
@ 2011-01-19 18:49 ` Jussi Kivilinna
2011-01-19 20:22 ` Christian Lamparter
0 siblings, 1 reply; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-19 18:49 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, Daniel Drake, Ulrich Kunitz
Quoting Christian Lamparter <chunkeey@googlemail.com>:
> On Sunday 09 January 2011 16:46:56 Jussi Kivilinna wrote:
>> Quoting Christian Lamparter <chunkeey@googlemail.com>:
>> > On Wednesday 05 January 2011 00:49:10 Jussi Kivilinna wrote:
>> >> diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c
>> >> b/drivers/net/wireless/zd1211rw/zd_mac.c
>> >> index aace010..a3c7e8f 100644
>> >> --- a/drivers/net/wireless/zd1211rw/zd_mac.c
>> >> +++ b/drivers/net/wireless/zd1211rw/zd_mac.c
>> >> @@ -958,16 +958,46 @@ static int zd_op_config(struct ieee80211_hw
>> >> *hw, u32 changed)
>> >> return zd_chip_set_channel(&mac->chip, conf->channel->hw_value);
>> >> }
>> >>
>> >> +static void zd_beacon_done(struct zd_mac *mac)
>> >> +{
>> >
>> > this is an interesting one...
>> >
>> > Since zd_beacon_done also uploads the next beacon so long in advance,
>> > there could be an equally long race between the outdated state of the
>> > next beacon's DTIM broadcast traffic indicator (802.11-2007 7.3.2.6)
>> > which -in your case- was uploaded almost a beacon interval ago and
>> > the xmit of ieee80211_get_buffered_bc *now*.
>> >
>> > The dtim bc/mc bit might be not set, when a mc/bc arrived after the
>> > beacon was uploaded, but before the "beacon done event" from the
>> > hardware. So, dozing stations don't expect the broadcast traffic
>> > and of course, they might miss it completely.
>> >
>> > It's probably better to fix this in mac80211 (see the attached hack).
>>
>> Ok, should I add this to my patchset?
> well, difficult to say. As far as I can say, it should be correct for "your
> case". But, on the other hand: what about solutions that can't buffer
> mc/bc frames (and needs to call ieee80211_get_buffered_bc), however the
> firmware is clever enough to maintain a beacon internally (so they
> won't call ieee80211_beacon_get and only relies on set_tim)?
>
> Sure, it's just a unlikely corner case... In fact, I didn't check if
> that's even possible, but it does sound reasonable to some extend.
>
From what I checked none of currect driver/device is such. Maybe if
such device appears then !bss->dtim_bc_mc check in
ieee80211_beacon_add_tim() could be masked with driver flag (something
like
IEEE80211_HW_CAN_AND_HANDLE_BEACON_BUT_STILL_NEEDS_HOST_BROADCAST_PS_BUFFERING).
>> > In fact, you could just as well drop "[09/17] zd1211rw: implement
>> > seq_num for IEEE80211_TX_CTL_ASSIGN_SEQ"... unless of course, I'm
>> > an idiot and there is a really clever way around these issues.
>>
>> Oh well, I should have checked this more closely before doing that
>> patch. HW is assigning seq-numbers already and that patch is not needed.
>
> heh, p54's fw can assign sequence numbers as well, but there's a txdesc flag
> to control the counter, so the firmware does not mess with the
> sequence control
> of QoS-Data frames. I hope zd1211* has one too.
>
There is tx-control flag that doesn't have effect, but since zd1211
doesn't appear to support QoS it's not a problem.
-Jussi
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc()
2011-01-19 18:49 ` Jussi Kivilinna
@ 2011-01-19 20:22 ` Christian Lamparter
2011-01-20 8:16 ` Jussi Kivilinna
0 siblings, 1 reply; 30+ messages in thread
From: Christian Lamparter @ 2011-01-19 20:22 UTC (permalink / raw)
To: Jussi Kivilinna; +Cc: linux-wireless, Daniel Drake, Ulrich Kunitz, kvalo
On Wednesday 19 January 2011 19:49:03 Jussi Kivilinna wrote:
> Quoting Christian Lamparter <chunkeey@googlemail.com>:
>> On Sunday 09 January 2011 16:46:56 Jussi Kivilinna wrote:
>>> Quoting Christian Lamparter <chunkeey@googlemail.com>:
>>>> this is an interesting one...
>>>>
>>>> Since zd_beacon_done also uploads the next beacon so long in advance,
>>>> there could be an equally long race between the outdated state of the
>>>> next beacon's DTIM broadcast traffic indicator (802.11-2007 7.3.2.6)
>>>> which -in your case- was uploaded almost a beacon interval ago and
>>>> the xmit of ieee80211_get_buffered_bc *now*.
>>>>
>>>> The dtim bc/mc bit might be not set, when a mc/bc arrived after the
>>>> beacon was uploaded, but before the "beacon done event" from the
>>>> hardware. So, dozing stations don't expect the broadcast traffic
>>>> and of course, they might miss it completely.
>>>>
>>>> It's probably better to fix this in mac80211 (see the attached hack).
>>>
>>> Ok, should I add this to my patchset?
>> well, difficult to say. As far as I can say, it should be correct for "your
>> case". But, on the other hand: what about solutions that can't buffer
>> mc/bc frames (and needs to call ieee80211_get_buffered_bc), however the
>> firmware is clever enough to maintain a beacon internally (so they
>> won't call ieee80211_beacon_get and only relies on set_tim)?
>>
>> Sure, it's just a unlikely corner case... In fact, I didn't check if
>> that's even possible, but it does sound reasonable to some extend.
>
> From what I checked none of currect driver/device is such.
> Maybe if such device appears then !bss->dtim_bc_mc check in
> ieee80211_beacon_add_tim() could be masked with driver flag (something
> like
> IEEE80211_HW_CAN_AND_HANDLE_BEACON_BUT_STILL_NEEDS_HOST_BROADCAST_PS_BUFFERING).
True, but a recent discussion into this matter have made parts of the
API you are planning to use sort-of "deprecated"? [I think?]
http://marc.info/?l=linux-wireless&m=129463297300480
I don't know the exact details, but I'm sure Kvalo does have his reasons.
[afaik it has to do with wl12xx, he even explained it once, but I can't
find that mail anymore].
>>>> In fact, you could just as well drop "[09/17] zd1211rw: implement
>>>> seq_num for IEEE80211_TX_CTL_ASSIGN_SEQ"... unless of course, I'm
>>>> an idiot and there is a really clever way around these issues.
>>>
>>> Oh well, I should have checked this more closely before doing that
>>> patch. HW is assigning seq-numbers already and that patch is not needed.
>>
>> heh, p54's fw can assign sequence numbers as well, but there's a txdesc flag
>> to control the counter, so the firmware does not mess with the
>> sequence control
>> of QoS-Data frames. I hope zd1211* has one too.
>
> There is tx-control flag that doesn't have effect, but since zd1211
> doesn't appear to support QoS it's not a problem.
Ok, that sound reasonable.
One more thing, is there a tx-control flag to instruct the HW/FW to write
the TSF into probe response frames, just like it does for beacons frames?
Sure, this feature is far more important for IBSS, but the 802.11-2007
specs @11.1.4 says that a STA might use beacons or probe responses to
synchronize its timers. [However 11.1.1.1 and 11.1.3.4 say that STAs
should "only" pick this information from beacons, if I'm not mistaken?!]
(Also a uniform "0..0" timestamp in every probe-response looks so sad.)
Best Regards,
Christian
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc()
2011-01-19 20:22 ` Christian Lamparter
@ 2011-01-20 8:16 ` Jussi Kivilinna
2011-01-20 9:13 ` Helmut Schaa
2011-01-20 17:43 ` Christian Lamparter
0 siblings, 2 replies; 30+ messages in thread
From: Jussi Kivilinna @ 2011-01-20 8:16 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, Daniel Drake, Ulrich Kunitz, kvalo
Quoting Christian Lamparter <chunkeey@googlemail.com>:
> On Wednesday 19 January 2011 19:49:03 Jussi Kivilinna wrote:
>> Quoting Christian Lamparter <chunkeey@googlemail.com>:
>>> On Sunday 09 January 2011 16:46:56 Jussi Kivilinna wrote:
>>>> Quoting Christian Lamparter <chunkeey@googlemail.com>:
>>>>> this is an interesting one...
>>>>>
>>>>> Since zd_beacon_done also uploads the next beacon so long in advance,
>>>>> there could be an equally long race between the outdated state of the
>>>>> next beacon's DTIM broadcast traffic indicator (802.11-2007 7.3.2.6)
>>>>> which -in your case- was uploaded almost a beacon interval ago and
>>>>> the xmit of ieee80211_get_buffered_bc *now*.
>>>>>
>>>>> The dtim bc/mc bit might be not set, when a mc/bc arrived after the
>>>>> beacon was uploaded, but before the "beacon done event" from the
>>>>> hardware. So, dozing stations don't expect the broadcast traffic
>>>>> and of course, they might miss it completely.
>>>>>
>>>>> It's probably better to fix this in mac80211 (see the attached hack).
>>>>
>>>> Ok, should I add this to my patchset?
>>> well, difficult to say. As far as I can say, it should be correct for "your
>>> case". But, on the other hand: what about solutions that can't buffer
>>> mc/bc frames (and needs to call ieee80211_get_buffered_bc), however the
>>> firmware is clever enough to maintain a beacon internally (so they
>>> won't call ieee80211_beacon_get and only relies on set_tim)?
>>>
>>> Sure, it's just a unlikely corner case... In fact, I didn't check if
>>> that's even possible, but it does sound reasonable to some extend.
>>
>> From what I checked none of currect driver/device is such.
>> Maybe if such device appears then !bss->dtim_bc_mc check in
>> ieee80211_beacon_add_tim() could be masked with driver flag (something
>> like
>> IEEE80211_HW_CAN_AND_HANDLE_BEACON_BUT_STILL_NEEDS_HOST_BROADCAST_PS_BUFFERING).
>
> True, but a recent discussion into this matter have made parts of the
> API you are planning to use sort-of "deprecated"? [I think?]
>
> http://marc.info/?l=linux-wireless&m=129463297300480
>
> I don't know the exact details, but I'm sure Kvalo does have his reasons.
> [afaik it has to do with wl12xx, he even explained it once, but I can't
> find that mail anymore].
>
What I understood from that thread is that rt2x00/usb doesn't have HW
buffering and doesn't enable IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING
(rt2x00/pci does), and yet enables AP-mode. Driver has this comment:
/*
* Initialize all hw fields.
*
* Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING unless we are
* capable of sending the buffered frames out after the DTIM
* transmission using rt2x00lib_beacondone. This will send out
* multicast and broadcast traffic immediately instead of buffering it
* infinitly and thus dropping it after some time.
*/
>>>>> In fact, you could just as well drop "[09/17] zd1211rw: implement
>>>>> seq_num for IEEE80211_TX_CTL_ASSIGN_SEQ"... unless of course, I'm
>>>>> an idiot and there is a really clever way around these issues.
>>>>
>>>> Oh well, I should have checked this more closely before doing that
>>>> patch. HW is assigning seq-numbers already and that patch is not needed.
>>>
>>> heh, p54's fw can assign sequence numbers as well, but there's a
>>> txdesc flag
>>> to control the counter, so the firmware does not mess with the
>>> sequence control
>>> of QoS-Data frames. I hope zd1211* has one too.
>>
>> There is tx-control flag that doesn't have effect, but since zd1211
>> doesn't appear to support QoS it's not a problem.
>
> Ok, that sound reasonable.
>
> One more thing, is there a tx-control flag to instruct the HW/FW to write
> the TSF into probe response frames, just like it does for beacons frames?
>
> Sure, this feature is far more important for IBSS, but the 802.11-2007
> specs @11.1.4 says that a STA might use beacons or probe responses to
> synchronize its timers. [However 11.1.1.1 and 11.1.3.4 say that STAs
> should "only" pick this information from beacons, if I'm not mistaken?!]
> (Also a uniform "0..0" timestamp in every probe-response looks so sad.)
>
No such flag I'm afraid. Vendor driver appears to be reading
tsf-register from driver and writing value to probe response frames,
maybe something I should add to zd1211rw too.
-Jussi
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc()
2011-01-20 8:16 ` Jussi Kivilinna
@ 2011-01-20 9:13 ` Helmut Schaa
2011-01-20 17:43 ` Christian Lamparter
1 sibling, 0 replies; 30+ messages in thread
From: Helmut Schaa @ 2011-01-20 9:13 UTC (permalink / raw)
To: Jussi Kivilinna
Cc: Christian Lamparter, linux-wireless, Daniel Drake, Ulrich Kunitz,
kvalo
Am Donnerstag, 20. Januar 2011 schrieb Jussi Kivilinna:
> Quoting Christian Lamparter <chunkeey@googlemail.com>:
> > True, but a recent discussion into this matter have made parts of the
> > API you are planning to use sort-of "deprecated"? [I think?]
> >
> > http://marc.info/?l=linux-wireless&m=129463297300480
> >
> > I don't know the exact details, but I'm sure Kvalo does have his reasons.
> > [afaik it has to do with wl12xx, he even explained it once, but I can't
> > find that mail anymore].
> >
>
> What I understood from that thread is that rt2x00/usb doesn't have HW
> buffering and doesn't enable IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING
> (rt2x00/pci does), and yet enables AP-mode. Driver has this comment:
> /*
> * Initialize all hw fields.
> *
> * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING unless we are
> * capable of sending the buffered frames out after the DTIM
> * transmission using rt2x00lib_beacondone. This will send out
> * multicast and broadcast traffic immediately instead of buffering it
> * infinitly and thus dropping it after some time.
> */
Yep, your interpretation is correct. Multi- and broadcast buffering in mac80211
will stay, but only if your device is able to send them out in a timely manner,
which rt2x00 usb devices are not.
Helmut
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc()
2011-01-20 8:16 ` Jussi Kivilinna
2011-01-20 9:13 ` Helmut Schaa
@ 2011-01-20 17:43 ` Christian Lamparter
1 sibling, 0 replies; 30+ messages in thread
From: Christian Lamparter @ 2011-01-20 17:43 UTC (permalink / raw)
To: Jussi Kivilinna; +Cc: linux-wireless, Daniel Drake, Ulrich Kunitz
On Thursday 20 January 2011 09:16:06 Jussi Kivilinna wrote:
> Quoting Christian Lamparter <chunkeey@googlemail.com>:
>> On Wednesday 19 January 2011 19:49:03 Jussi Kivilinna wrote:
>>> Quoting Christian Lamparter <chunkeey@googlemail.com>:
>>>> On Sunday 09 January 2011 16:46:56 Jussi Kivilinna wrote:
>>>>> Quoting Christian Lamparter <chunkeey@googlemail.com>:
>>>>>> Since zd_beacon_done also uploads the next beacon so long in advance,
>>>>>> there could be an equally long race between the outdated state of the
>>>>>> next beacon's DTIM broadcast traffic indicator (802.11-2007 7.3.2.6)
>>>>>> which -in your case- was uploaded almost a beacon interval ago and
>>>>>> the xmit of ieee80211_get_buffered_bc *now*.
>>>>>>
>>>>>> The dtim bc/mc bit might be not set, when a mc/bc arrived after the
>>>>>> beacon was uploaded, but before the "beacon done event" from the
>>>>>> hardware. So, dozing stations don't expect the broadcast traffic
>>>>>> and of course, they might miss it completely.
>>>>>>
>>>>>> It's probably better to fix this in mac80211 (see the attached hack).
>>>>>
>>>>> Ok, should I add this to my patchset?
>>>> well, difficult to say. As far as I can say, it should be correct for "your
>>>> case". But, on the other hand: what about solutions that can't buffer
>>>> mc/bc frames (and needs to call ieee80211_get_buffered_bc), however the
>>>> firmware is clever enough to maintain a beacon internally (so they
>>>> won't call ieee80211_beacon_get and only relies on set_tim)?
>>>>
>>>> Sure, it's just a unlikely corner case... In fact, I didn't check if
>>>> that's even possible, but it does sound reasonable to some extend.
>>>
>>> From what I checked none of currect driver/device is such.
>>> Maybe if such device appears then !bss->dtim_bc_mc check in
>>> ieee80211_beacon_add_tim() could be masked with driver flag (something
>>> like
>>> IEEE80211_HW_CAN_AND_HANDLE_BEACON_BUT_STILL_NEEDS_HOST_BROADCAST_PS_BUFFERING).
>>
>> True, but a recent discussion into this matter have made parts of the
>> API you are planning to use sort-of "deprecated"? [I think?]
>>
>> http://marc.info/?l=linux-wireless&m=129463297300480
>>
>> I don't know the exact details, but I'm sure Kvalo does have his reasons.
>> [afaik it has to do with wl12xx, he even explained it once, but I can't
>> find that mail anymore].
>>
>
> What I understood from that thread is that rt2x00/usb doesn't have HW
> buffering and doesn't enable IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING
> (rt2x00/pci does), and yet enables AP-mode. Driver has this comment:
> /*
> * Initialize all hw fields.
> *
> * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING unless we are
> * capable of sending the buffered frames out after the DTIM
> * transmission using rt2x00lib_beacondone. This will send out
> * multicast and broadcast traffic immediately instead of buffering it
> * infinitly and thus dropping it after some time.
> */
My bad, you are right. This is not something to worry about anymore.
> > One more thing, is there a tx-control flag to instruct the HW/FW to write
> > the TSF into probe response frames, just like it does for beacons frames?
> >
> > Sure, this feature is far more important for IBSS, but the 802.11-2007
> > specs @11.1.4 says that a STA might use beacons or probe responses to
> > synchronize its timers. [However 11.1.1.1 and 11.1.3.4 say that STAs
> > should "only" pick this information from beacons, if I'm not mistaken?!]
> > (Also a uniform "0..0" timestamp in every probe-response looks so sad.)
> >
>
> No such flag I'm afraid. Vendor driver appears to be reading
> tsf-register from driver and writing value to probe response frames,
> maybe something I should add to zd1211rw too.
It's more of a cosmetic thing, but very difficult to get it right without
firmware/hw support [over an usb]. So you might just leave it as it
is...
Best regards,
Christian
^ permalink raw reply [flat|nested] 30+ messages in thread
end of thread, other threads:[~2011-01-20 17:51 UTC | newest]
Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-01-04 23:47 [RFC PATCH 0/17] zd1211rw: add support for AP mode Jussi Kivilinna
2011-01-04 23:47 ` [RFC PATCH 01/17] zd1211rw: fix tx-queue disabling Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 02/17] zd1211rw: cancel process_intr work on zd_chip_disable_int() Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 03/17] zd1211rw: fix beacon interval setup Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 04/17] zd1211rw: move set_multicast_hash and set_rx_filter from workers to configure_filter Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 05/17] zd1211rw: move set_rts_cts_work to bss_info_changed Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 06/17] zd1211rw: support setting BSSID for AP mode Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 07/17] zd1211rw: fix ack_pending in filter_ack causing tx-packet ordering problem on monitor Jussi Kivilinna
2011-01-04 23:48 ` [RFC PATCH 08/17] zd1211rw: let zd_set_beacon_interval() set dtim_period and add AP-beacon flag Jussi Kivilinna
2011-01-04 23:49 ` [RFC PATCH 09/17] zd1211rw: implement seq_num for IEEE80211_TX_CTL_ASSIGN_SEQ Jussi Kivilinna
2011-01-04 23:49 ` [RFC PATCH 10/17] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc() Jussi Kivilinna
2011-01-06 21:46 ` Christian Lamparter
2011-01-06 21:55 ` Johannes Berg
2011-01-09 15:46 ` Jussi Kivilinna
2011-01-09 20:33 ` Christian Lamparter
2011-01-19 18:49 ` Jussi Kivilinna
2011-01-19 20:22 ` Christian Lamparter
2011-01-20 8:16 ` Jussi Kivilinna
2011-01-20 9:13 ` Helmut Schaa
2011-01-20 17:43 ` Christian Lamparter
2011-01-04 23:49 ` [RFC PATCH 11/17] zd1211rw: add beacon watchdog and setting HW beacon more failsafe Jussi Kivilinna
2011-01-04 23:49 ` [RFC PATCH 12/17] zd1211rw: batch beacon config commands together Jussi Kivilinna
2011-01-04 23:49 ` [RFC PATCH 13/17] zd1211rw: use stack for small cmd-buffers Jussi Kivilinna
2011-01-05 9:27 ` Johannes Berg
2011-01-06 18:55 ` Dan Williams
2011-01-06 21:53 ` Jussi Kivilinna
2011-01-04 23:49 ` [RFC PATCH 14/17] zd1211rw: lower hw command timeouts Jussi Kivilinna
2011-01-04 23:49 ` [RFC PATCH 15/17] zd1211rw: collect driver settings and add function to restore theim Jussi Kivilinna
2011-01-04 23:50 ` [RFC PATCH 16/17] zd1211rw: add tx watchdog Jussi Kivilinna
2011-01-04 23:50 ` [RFC PATCH 17/17] zd1211rw: enable NL80211_IFTYPE_AP Jussi Kivilinna
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).