All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/22] zd1211rw: add support for AP-mode
@ 2011-01-31 18:46 Jussi Kivilinna
  2011-01-31 18:47 ` [PATCH 01/22] zd1211rw: use urb anchors for tx and fix tx-queue disabling Jussi Kivilinna
                   ` (22 more replies)
  0 siblings, 23 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:46 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz

This patchset adds AP-mode support to zd1211rw. Tested with zd1211b devices only.

Patches are mix of fixes and AP support adding code.
   [1-2]  fix oopses noticed while testing
     [3]  add missing locking
     [4]  fix beacon setup to match vendor driver (needed for AP mode)
   [5-6]  'may sleep' cleanups, move code from workers to mac80211-functions
  [7-11]  AP-mode supporting code
 [12-16]  beacon setup fixes/workarounds                                                          
 [17-20]  device reset and device/TX/RX stall workarounds
    [21]  enable AP mode
    [22]  add more debuging output

---

Christian Lamparter (1):
      mac80211: fix race between next beacon dtim and ieee80211_get_buffered_bc

Jussi Kivilinna (21):
      zd1211rw: use urb anchors for tx and fix tx-queue disabling
      zd1211rw: cancel process_intr work on zd_chip_disable_int()
      zd1211rw: add locking for mac->process_intr
      zd1211rw: fix beacon interval setup
      zd1211rw: move set_multicast_hash and set_rx_filter from workers to configure_filter
      zd1211rw: move set_rts_cts_work to bss_info_changed
      zd1211rw: support setting BSSID for AP mode
      zd1211rw: fix ack_pending in filter_ack causing tx-packet ordering problem on monitor
      zd1211rw: let zd_set_beacon_interval() set dtim_period and add AP-beacon flag
      zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc()
      zd1211rw: add beacon watchdog and setting HW beacon more failsafe
      zd1211rw: batch beacon config commands together
      [v2] zd1211rw: use stack and preallocated memory for small cmd-buffers
      zd1211rw: change interrupt URB buffer to DMA buffer
      zd1211rw: lower hw command timeouts
      zd1211rw: collect driver settings and add function to restore theim
      zd1211rw: add TX watchdog and device resetting
      zd1211rw: reset device when CR_BCN_FIFO_SEMAPHORE freezes in beacon setup
      zd1211rw: reset rx urbs after idle period of 30 seconds
      zd1211rw: enable NL80211_IFTYPE_AP
      zd1211rw: add useful debug output


 drivers/net/wireless/zd1211rw/zd_chip.c |  134 ++++++---
 drivers/net/wireless/zd1211rw/zd_chip.h |    5 
 drivers/net/wireless/zd1211rw/zd_mac.c  |  448 ++++++++++++++++++++++++-------
 drivers/net/wireless/zd1211rw/zd_mac.h  |   24 +-
 drivers/net/wireless/zd1211rw/zd_usb.c  |  445 ++++++++++++++++++++++++-------
 drivers/net/wireless/zd1211rw/zd_usb.h  |   30 ++
 net/mac80211/ieee80211_i.h              |    1 
 net/mac80211/tx.c                       |    4 
 8 files changed, 822 insertions(+), 269 deletions(-)

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [PATCH 01/22] zd1211rw: use urb anchors for tx and fix tx-queue disabling
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
@ 2011-01-31 18:47 ` Jussi Kivilinna
  2011-01-31 18:47 ` [PATCH 02/22] zd1211rw: cancel process_intr work on zd_chip_disable_int() Jussi Kivilinna
                   ` (21 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:47 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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.

Patch makes tx-urb handling saner with use of urb anchors.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_usb.c |  108 +++++++++++---------------------
 drivers/net/wireless/zd1211rw/zd_usb.h |    8 +-
 2 files changed, 41 insertions(+), 75 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index 06041cb..c32a247 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -779,19 +779,20 @@ void zd_usb_disable_tx(struct zd_usb *usb)
 {
 	struct zd_usb_tx *tx = &usb->tx;
 	unsigned long flags;
-	struct list_head *pos, *n;
+
+	atomic_set(&tx->enabled, 0);
+
+	/* kill all submitted tx-urbs */
+	usb_kill_anchored_urbs(&tx->submitted);
 
 	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;
+	WARN_ON(tx->submitted_urbs != 0);
 	tx->submitted_urbs = 0;
+	spin_unlock_irqrestore(&tx->lock, flags);
+
 	/* The stopped state is ignored, relying on ieee80211_wake_queues()
 	 * in a potentionally following zd_usb_enable_tx().
 	 */
-	spin_unlock_irqrestore(&tx->lock, flags);
 }
 
 /**
@@ -807,63 +808,13 @@ void zd_usb_enable_tx(struct zd_usb *usb)
 	struct zd_usb_tx *tx = &usb->tx;
 
 	spin_lock_irqsave(&tx->lock, flags);
-	tx->enabled = 1;
+	atomic_set(&tx->enabled, 1);
 	tx->submitted_urbs = 0;
 	ieee80211_wake_queues(zd_usb_to_hw(usb));
 	tx->stopped = 0;
 	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;
@@ -905,6 +856,16 @@ static void tx_urb_complete(struct urb *urb)
 	struct sk_buff *skb;
 	struct ieee80211_tx_info *info;
 	struct zd_usb *usb;
+	struct zd_usb_tx *tx;
+
+	skb = (struct sk_buff *)urb->context;
+	info = IEEE80211_SKB_CB(skb);
+	/*
+	 * grab 'usb' pointer before handing off the skb (since
+	 * it might be freed by zd_mac_tx_to_dev or mac80211)
+	 */
+	usb = &zd_hw_mac(info->rate_driver_data[0])->chip.usb;
+	tx = &usb->tx;
 
 	switch (urb->status) {
 	case 0:
@@ -922,20 +883,15 @@ static void tx_urb_complete(struct urb *urb)
 		goto resubmit;
 	}
 free_urb:
-	skb = (struct sk_buff *)urb->context;
-	/*
-	 * grab 'usb' pointer before handing off the skb (since
-	 * it might be freed by zd_mac_tx_to_dev or mac80211)
-	 */
-	info = IEEE80211_SKB_CB(skb);
-	usb = &zd_hw_mac(info->rate_driver_data[0])->chip.usb;
 	zd_mac_tx_to_dev(skb, urb->status);
-	free_tx_urb(usb, urb);
+	usb_free_urb(urb);
 	tx_dec_submitted_urbs(usb);
 	return;
 resubmit:
+	usb_anchor_urb(urb, &tx->submitted);
 	r = usb_submit_urb(urb, GFP_ATOMIC);
 	if (r) {
+		usb_unanchor_urb(urb);
 		dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r);
 		goto free_urb;
 	}
@@ -958,8 +914,14 @@ int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb)
 	int r;
 	struct usb_device *udev = zd_usb_to_usbdev(usb);
 	struct urb *urb;
+	struct zd_usb_tx *tx = &usb->tx;
 
-	urb = alloc_tx_urb(usb);
+	if (!atomic_read(&tx->enabled)) {
+		r = -ENOENT;
+		goto out;
+	}
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
 	if (!urb) {
 		r = -ENOMEM;
 		goto out;
@@ -968,13 +930,16 @@ 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);
 
+	usb_anchor_urb(urb, &tx->submitted);
 	r = usb_submit_urb(urb, GFP_ATOMIC);
-	if (r)
+	if (r) {
+		usb_unanchor_urb(urb);
 		goto error;
+	}
 	tx_inc_submitted_urbs(usb);
 	return 0;
 error:
-	free_tx_urb(usb, urb);
+	usb_free_urb(urb);
 out:
 	return r;
 }
@@ -1005,9 +970,9 @@ static inline void init_usb_tx(struct zd_usb *usb)
 {
 	struct zd_usb_tx *tx = &usb->tx;
 	spin_lock_init(&tx->lock);
-	tx->enabled = 0;
+	atomic_set(&tx->enabled, 0);
 	tx->stopped = 0;
-	INIT_LIST_HEAD(&tx->free_urb_list);
+	init_usb_anchor(&tx->submitted);
 	tx->submitted_urbs = 0;
 }
 
@@ -1240,6 +1205,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..233ce82 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.h
+++ b/drivers/net/wireless/zd1211rw/zd_usb.h
@@ -184,18 +184,18 @@ struct zd_usb_rx {
 
 /**
  * struct zd_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
  * @lock: lock for transmission
- * @free_urb_list: list of free URBs, contains all the URBs, which can be used
+ * @submitted: anchor for URBs sent to device
  * @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
  * @stopped: indicates whether higher level tx queues are stopped
  */
 struct zd_usb_tx {
+	atomic_t enabled;
 	spinlock_t lock;
-	struct list_head free_urb_list;
+	struct usb_anchor submitted;
 	int submitted_urbs;
-	int enabled;
 	int stopped;
 };
 


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 02/22] zd1211rw: cancel process_intr work on zd_chip_disable_int()
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
  2011-01-31 18:47 ` [PATCH 01/22] zd1211rw: use urb anchors for tx and fix tx-queue disabling Jussi Kivilinna
@ 2011-01-31 18:47 ` Jussi Kivilinna
  2011-01-31 18:47 ` [PATCH 03/22] zd1211rw: add locking for mac->process_intr Jussi Kivilinna
                   ` (20 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:47 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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 6a9b660..b644ced 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -1407,6 +1407,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] 25+ messages in thread

* [PATCH 03/22] zd1211rw: add locking for mac->process_intr
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
  2011-01-31 18:47 ` [PATCH 01/22] zd1211rw: use urb anchors for tx and fix tx-queue disabling Jussi Kivilinna
  2011-01-31 18:47 ` [PATCH 02/22] zd1211rw: cancel process_intr work on zd_chip_disable_int() Jussi Kivilinna
@ 2011-01-31 18:47 ` Jussi Kivilinna
  2011-01-31 18:47 ` [PATCH 04/22] zd1211rw: fix beacon interval setup Jussi Kivilinna
                   ` (19 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:47 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_mac.c |    6 +++++-
 drivers/net/wireless/zd1211rw/zd_usb.c |    2 ++
 2 files changed, 7 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 6107304..8b3d779 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -911,9 +911,13 @@ static int zd_op_config(struct ieee80211_hw *hw, u32 changed)
 static void zd_process_intr(struct work_struct *work)
 {
 	u16 int_status;
+	unsigned long flags;
 	struct zd_mac *mac = container_of(work, struct zd_mac, process_intr);
 
-	int_status = le16_to_cpu(*(__le16 *)(mac->intr_buffer+4));
+	spin_lock_irqsave(&mac->lock, flags);
+	int_status = le16_to_cpu(*(__le16 *)(mac->intr_buffer + 4));
+	spin_unlock_irqrestore(&mac->lock, flags);
+
 	if (int_status & INT_CFG_NEXT_BCN)
 		dev_dbg_f_limit(zd_mac_dev(mac), "INT_CFG_NEXT_BCN\n");
 	else
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index c32a247..9493ab8 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -377,8 +377,10 @@ static inline void handle_regs_int(struct urb *urb)
 	int_num = le16_to_cpu(*(__le16 *)(urb->transfer_buffer+2));
 	if (int_num == CR_INTERRUPT) {
 		struct zd_mac *mac = zd_hw_mac(zd_usb_to_hw(urb->context));
+		spin_lock(&mac->lock);
 		memcpy(&mac->intr_buffer, urb->transfer_buffer,
 				USB_MAX_EP_INT_BUFFER);
+		spin_unlock(&mac->lock);
 		schedule_work(&mac->process_intr);
 	} else if (intr->read_regs_enabled) {
 		intr->read_regs.length = len = urb->actual_length;


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 04/22] zd1211rw: fix beacon interval setup
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (2 preceding siblings ...)
  2011-01-31 18:47 ` [PATCH 03/22] zd1211rw: add locking for mac->process_intr Jussi Kivilinna
@ 2011-01-31 18:47 ` Jussi Kivilinna
  2011-01-31 18:47 ` [PATCH 05/22] zd1211rw: move set_multicast_hash and set_rx_filter from workers to configure_filter Jussi Kivilinna
                   ` (18 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:47 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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 b644ced..447f236 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -849,11 +849,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;
 
@@ -862,7 +863,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));
 }
@@ -874,10 +875,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] 25+ messages in thread

* [PATCH 05/22] zd1211rw: move set_multicast_hash and set_rx_filter from workers to configure_filter
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (3 preceding siblings ...)
  2011-01-31 18:47 ` [PATCH 04/22] zd1211rw: fix beacon interval setup Jussi Kivilinna
@ 2011-01-31 18:47 ` Jussi Kivilinna
  2011-01-31 18:47 ` [PATCH 06/22] zd1211rw: move set_rts_cts_work to bss_info_changed Jussi Kivilinna
                   ` (17 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:47 UTC (permalink / raw)
  To: linux-wireless
  Cc: Daniel Drake, Jussi Kivilinna, John W. Linville, 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 8b3d779..75d9a13 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -927,31 +927,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)
 {
@@ -983,6 +958,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;
@@ -1004,11 +980,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 */
@@ -1164,9 +1142,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] 25+ messages in thread

* [PATCH 06/22] zd1211rw: move set_rts_cts_work to bss_info_changed
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (4 preceding siblings ...)
  2011-01-31 18:47 ` [PATCH 05/22] zd1211rw: move set_multicast_hash and set_rx_filter from workers to configure_filter Jussi Kivilinna
@ 2011-01-31 18:47 ` Jussi Kivilinna
  2011-01-31 18:48 ` [PATCH 07/22] zd1211rw: support setting BSSID for AP mode Jussi Kivilinna
                   ` (16 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:47 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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 75d9a13..487ed33 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -998,20 +998,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);
 }
@@ -1022,7 +1011,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);
@@ -1060,15 +1048,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);
 	}
 }
 
@@ -1142,7 +1126,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] 25+ messages in thread

* [PATCH 07/22] zd1211rw: support setting BSSID for AP mode
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (5 preceding siblings ...)
  2011-01-31 18:47 ` [PATCH 06/22] zd1211rw: move set_rts_cts_work to bss_info_changed Jussi Kivilinna
@ 2011-01-31 18:48 ` Jussi Kivilinna
  2011-01-31 18:48 ` [PATCH 08/22] zd1211rw: fix ack_pending in filter_ack causing tx-packet ordering problem on monitor Jussi Kivilinna
                   ` (15 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:48 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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 447f236..71d3cde 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -370,16 +370,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)
@@ -388,9 +384,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);
@@ -399,6 +395,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 487ed33..ab0d1b9 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] 25+ messages in thread

* [PATCH 08/22] zd1211rw: fix ack_pending in filter_ack causing tx-packet ordering problem on monitor
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (6 preceding siblings ...)
  2011-01-31 18:48 ` [PATCH 07/22] zd1211rw: support setting BSSID for AP mode Jussi Kivilinna
@ 2011-01-31 18:48 ` Jussi Kivilinna
  2011-01-31 18:48 ` [PATCH 09/22] zd1211rw: let zd_set_beacon_interval() set dtim_period and add AP-beacon flag Jussi Kivilinna
                   ` (14 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:48 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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 ab0d1b9..84ac95e 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] 25+ messages in thread

* [PATCH 09/22] zd1211rw: let zd_set_beacon_interval() set dtim_period and add AP-beacon flag
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (7 preceding siblings ...)
  2011-01-31 18:48 ` [PATCH 08/22] zd1211rw: fix ack_pending in filter_ack causing tx-packet ordering problem on monitor Jussi Kivilinna
@ 2011-01-31 18:48 ` Jussi Kivilinna
  2011-01-31 18:48 ` [PATCH 10/22] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc() Jussi Kivilinna
                   ` (13 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:48 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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 71d3cde..d8dc927 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -888,14 +888,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);
@@ -904,12 +926,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;
 }
@@ -928,7 +951,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 84ac95e..1bd275b 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);
 }
 
@@ -1058,15 +1058,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] 25+ messages in thread

* [PATCH 10/22] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc()
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (8 preceding siblings ...)
  2011-01-31 18:48 ` [PATCH 09/22] zd1211rw: let zd_set_beacon_interval() set dtim_period and add AP-beacon flag Jussi Kivilinna
@ 2011-01-31 18:48 ` Jussi Kivilinna
  2011-01-31 18:48 ` [PATCH 11/22] mac80211: fix race between next beacon dtim and ieee80211_get_buffered_bc Jussi Kivilinna
                   ` (12 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:48 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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 1bd275b..49ab3c3 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -938,6 +938,34 @@ 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;
@@ -948,10 +976,12 @@ static void zd_process_intr(struct work_struct *work)
 	int_status = le16_to_cpu(*(__le16 *)(mac->intr_buffer + 4));
 	spin_unlock_irqrestore(&mac->lock, flags);
 
-	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);
 }
@@ -1135,7 +1165,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] 25+ messages in thread

* [PATCH 11/22] mac80211: fix race between next beacon dtim and ieee80211_get_buffered_bc
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (9 preceding siblings ...)
  2011-01-31 18:48 ` [PATCH 10/22] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc() Jussi Kivilinna
@ 2011-01-31 18:48 ` Jussi Kivilinna
  2011-01-31 18:48 ` [PATCH 12/22] zd1211rw: add beacon watchdog and setting HW beacon more failsafe Jussi Kivilinna
                   ` (11 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:48 UTC (permalink / raw)
  To: linux-wireless
  Cc: Christian Lamparter, Daniel Drake, John W. Linville, Ulrich Kunitz

From: Christian Lamparter <chunkeey@googlemail.com>

On review of 'zd1211rw: implement beacon fetching and handling
ieee80211_get_buffered_bc()', Christian Lamparter noted that [1]:

   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).

[1] http://marc.info/?l=linux-wireless&m=129435041117256&w=2

CC: Christian Lamparter <chunkeey@googlemail.com>
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 net/mac80211/ieee80211_i.h |    1 +
 net/mac80211/tx.c          |    4 +++-
 2 files changed, 4 insertions(+), 1 deletions(-)

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 ffc6749..1c8cf41 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
@@ -2548,7 +2550,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] 25+ messages in thread

* [PATCH 12/22] zd1211rw: add beacon watchdog and setting HW beacon more failsafe
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (10 preceding siblings ...)
  2011-01-31 18:48 ` [PATCH 11/22] mac80211: fix race between next beacon dtim and ieee80211_get_buffered_bc Jussi Kivilinna
@ 2011-01-31 18:48 ` Jussi Kivilinna
  2011-01-31 18:49 ` [PATCH 13/22] zd1211rw: batch beacon config commands together Jussi Kivilinna
                   ` (10 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:48 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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 |  188 +++++++++++++++++++++++++++-----
 drivers/net/wireless/zd1211rw/zd_mac.h |   13 ++
 2 files changed, 170 insertions(+), 31 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 49ab3c3..78c8f8b 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 int fill_ctrlset(struct zd_mac *mac,
@@ -942,6 +985,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;
 
@@ -959,11 +1004,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)
@@ -1082,7 +1130,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);
 			}
 		}
@@ -1096,6 +1146,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);
 		}
@@ -1188,12 +1244,82 @@ 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_delayed_work_sync(&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 0ec6bde..281b307 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] 25+ messages in thread

* [PATCH 13/22] zd1211rw: batch beacon config commands together
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (11 preceding siblings ...)
  2011-01-31 18:48 ` [PATCH 12/22] zd1211rw: add beacon watchdog and setting HW beacon more failsafe Jussi Kivilinna
@ 2011-01-31 18:49 ` Jussi Kivilinna
  2011-01-31 18:49 ` [PATCH 14/22] [v2] zd1211rw: use stack and preallocated memory for small cmd-buffers Jussi Kivilinna
                   ` (9 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:49 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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 78c8f8b..84ee1b8 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] 25+ messages in thread

* [PATCH 14/22] [v2] zd1211rw: use stack and preallocated memory for small cmd-buffers
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (12 preceding siblings ...)
  2011-01-31 18:49 ` [PATCH 13/22] zd1211rw: batch beacon config commands together Jussi Kivilinna
@ 2011-01-31 18:49 ` Jussi Kivilinna
  2011-01-31 18:49 ` [PATCH 15/22] zd1211rw: change interrupt URB buffer to DMA buffer Jussi Kivilinna
                   ` (8 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:49 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz

Use stack for allocing small < 64 byte arrays in zd_chip.c and preallocated
buffer in zd_usb.c. This might lower CPU usage for beacon setup.

v2:
 - Do not use stack buffers in zd_usb.c as they would be used for urb
   transfer_buffer.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_chip.c |   43 +++++++++----------------------
 drivers/net/wireless/zd1211rw/zd_usb.c  |   42 ++++++++++++++++++++----------
 drivers/net/wireless/zd1211rw/zd_usb.h  |    1 +
 3 files changed, 42 insertions(+), 44 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index d8dc927..907e656 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -108,25 +108,17 @@ 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;
 
 	if (count > USB_MAX_IOREAD32_COUNT)
 		return -EINVAL;
 
-	/* Allocate a single memory block for values and addresses. */
-	count16 = 2*count;
-	/* zd_addr_t is __nocast, so the kmalloc needs an explicit cast */
-	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);
+	/* Use stack for values and addresses. */
+	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;
@@ -139,7 +131,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++) {
@@ -147,18 +139,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)
@@ -166,15 +158,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;
@@ -192,8 +177,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 9493ab8..6b604fc 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -1361,15 +1361,20 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
 		return -EWOULDBLOCK;
 	}
 	if (!usb_int_enabled(usb)) {
-		 dev_dbg_f(zd_usb_dev(usb),
+		dev_dbg_f(zd_usb_dev(usb),
 			  "error: usb interrupt not enabled\n");
 		return -EWOULDBLOCK;
 	}
 
+	ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex));
+	BUILD_BUG_ON(sizeof(struct usb_req_read_regs) + USB_MAX_IOREAD16_COUNT *
+		     sizeof(__le16) > sizeof(usb->req_buf));
+	BUG_ON(sizeof(struct usb_req_read_regs) + count * sizeof(__le16) >
+	       sizeof(usb->req_buf));
+
 	req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16);
-	req = kmalloc(req_len, GFP_KERNEL);
-	if (!req)
-		return -ENOMEM;
+	req = (void *)usb->req_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]);
@@ -1402,7 +1407,6 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
 
 	r = get_results(usb, values, req, count);
 error:
-	kfree(req);
 	return r;
 }
 
@@ -1428,11 +1432,17 @@ int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
 		return -EWOULDBLOCK;
 	}
 
+	ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex));
+	BUILD_BUG_ON(sizeof(struct usb_req_write_regs) +
+		     USB_MAX_IOWRITE16_COUNT * sizeof(struct reg_data) >
+		     sizeof(usb->req_buf));
+	BUG_ON(sizeof(struct usb_req_write_regs) +
+	       count * sizeof(struct reg_data) >
+	       sizeof(usb->req_buf));
+
 	req_len = sizeof(struct usb_req_write_regs) +
 		  count * sizeof(struct reg_data);
-	req = kmalloc(req_len, GFP_KERNEL);
-	if (!req)
-		return -ENOMEM;
+	req = (void *)usb->req_buf;
 
 	req->id = cpu_to_le16(USB_REQ_WRITE_REGS);
 	for (i = 0; i < count; i++) {
@@ -1460,7 +1470,6 @@ int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
 
 	/* FALL-THROUGH with r == 0 */
 error:
-	kfree(req);
 	return r;
 }
 
@@ -1505,14 +1514,19 @@ 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);
 
+	ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex));
+	BUILD_BUG_ON(sizeof(struct usb_req_rfwrite) +
+		     USB_MAX_RFWRITE_BIT_COUNT * sizeof(__le16) >
+		     sizeof(usb->req_buf));
+	BUG_ON(sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16) >
+	       sizeof(usb->req_buf));
+
 	req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16);
-	req = kmalloc(req_len, GFP_KERNEL);
-	if (!req)
-		return -ENOMEM;
+	req = (void *)usb->req_buf;
 
 	req->id = cpu_to_le16(USB_REQ_WRITE_RF);
 	/* 1: 3683a, but not used in ZYDAS driver */
@@ -1544,6 +1558,6 @@ int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits)
 
 	/* FALL-THROUGH with r == 0 */
 out:
-	kfree(req);
 	return r;
 }
+
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h
index 233ce82..2ed48ae 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.h
+++ b/drivers/net/wireless/zd1211rw/zd_usb.h
@@ -207,6 +207,7 @@ struct zd_usb {
 	struct zd_usb_rx rx;
 	struct zd_usb_tx tx;
 	struct usb_interface *intf;
+	u8 req_buf[64]; /* zd_usb_iowrite16v needs 62 bytes */
 	u8 is_zd1211b:1, initialized:1;
 };
 


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 15/22] zd1211rw: change interrupt URB buffer to DMA buffer
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (13 preceding siblings ...)
  2011-01-31 18:49 ` [PATCH 14/22] [v2] zd1211rw: use stack and preallocated memory for small cmd-buffers Jussi Kivilinna
@ 2011-01-31 18:49 ` Jussi Kivilinna
  2011-01-31 18:49 ` [PATCH 16/22] zd1211rw: lower hw command timeouts Jussi Kivilinna
                   ` (7 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:49 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz

As might lower beacon update CPU usage.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_usb.c |   36 ++++++++++++++++++++------------
 drivers/net/wireless/zd1211rw/zd_usb.h |    2 ++
 2 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index 6b604fc..b866f7a 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -411,7 +411,7 @@ static void int_urb_complete(struct urb *urb)
 	case -ENOENT:
 	case -ECONNRESET:
 	case -EPIPE:
-		goto kfree;
+		return;
 	default:
 		goto resubmit;
 	}
@@ -443,12 +443,11 @@ static void int_urb_complete(struct urb *urb)
 resubmit:
 	r = usb_submit_urb(urb, GFP_ATOMIC);
 	if (r) {
-		dev_dbg_f(urb_dev(urb), "resubmit urb %p\n", urb);
-		goto kfree;
+		dev_dbg_f(urb_dev(urb), "error: resubmit urb %p err code %d\n",
+			  urb, r);
+		/* TODO: add worker to reset intr->urb */
 	}
 	return;
-kfree:
-	kfree(urb->transfer_buffer);
 }
 
 static inline int int_urb_interval(struct usb_device *udev)
@@ -479,9 +478,8 @@ static inline int usb_int_enabled(struct zd_usb *usb)
 int zd_usb_enable_int(struct zd_usb *usb)
 {
 	int r;
-	struct usb_device *udev;
+	struct usb_device *udev = zd_usb_to_usbdev(usb);
 	struct zd_usb_interrupt *intr = &usb->intr;
-	void *transfer_buffer = NULL;
 	struct urb *urb;
 
 	dev_dbg_f(zd_usb_dev(usb), "\n");
@@ -502,20 +500,21 @@ int zd_usb_enable_int(struct zd_usb *usb)
 	intr->urb = urb;
 	spin_unlock_irq(&intr->lock);
 
-	/* TODO: make it a DMA buffer */
 	r = -ENOMEM;
-	transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_KERNEL);
-	if (!transfer_buffer) {
+	intr->buffer = usb_alloc_coherent(udev, USB_MAX_EP_INT_BUFFER,
+					  GFP_KERNEL, &intr->buffer_dma);
+	if (!intr->buffer) {
 		dev_dbg_f(zd_usb_dev(usb),
 			"couldn't allocate transfer_buffer\n");
 		goto error_set_urb_null;
 	}
 
-	udev = zd_usb_to_usbdev(usb);
 	usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, EP_INT_IN),
-			 transfer_buffer, USB_MAX_EP_INT_BUFFER,
+			 intr->buffer, USB_MAX_EP_INT_BUFFER,
 			 int_urb_complete, usb,
 			 intr->interval);
+	urb->transfer_dma = intr->buffer_dma;
+	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
 	dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb);
 	r = usb_submit_urb(urb, GFP_KERNEL);
@@ -527,7 +526,8 @@ int zd_usb_enable_int(struct zd_usb *usb)
 
 	return 0;
 error:
-	kfree(transfer_buffer);
+	usb_free_coherent(udev, USB_MAX_EP_INT_BUFFER,
+			  intr->buffer, intr->buffer_dma);
 error_set_urb_null:
 	spin_lock_irq(&intr->lock);
 	intr->urb = NULL;
@@ -541,8 +541,11 @@ out:
 void zd_usb_disable_int(struct zd_usb *usb)
 {
 	unsigned long flags;
+	struct usb_device *udev = zd_usb_to_usbdev(usb);
 	struct zd_usb_interrupt *intr = &usb->intr;
 	struct urb *urb;
+	void *buffer;
+	dma_addr_t buffer_dma;
 
 	spin_lock_irqsave(&intr->lock, flags);
 	urb = intr->urb;
@@ -551,11 +554,18 @@ void zd_usb_disable_int(struct zd_usb *usb)
 		return;
 	}
 	intr->urb = NULL;
+	buffer = intr->buffer;
+	buffer_dma = intr->buffer_dma;
+	intr->buffer = NULL;
 	spin_unlock_irqrestore(&intr->lock, flags);
 
 	usb_kill_urb(urb);
 	dev_dbg_f(zd_usb_dev(usb), "urb %p killed\n", urb);
 	usb_free_urb(urb);
+
+	if (buffer)
+		usb_free_coherent(udev, USB_MAX_EP_INT_BUFFER,
+				  buffer, buffer_dma);
 }
 
 static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer,
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h
index 2ed48ae..24db0dd 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.h
+++ b/drivers/net/wireless/zd1211rw/zd_usb.h
@@ -162,6 +162,8 @@ struct zd_usb_interrupt {
 	struct read_regs_int read_regs;
 	spinlock_t lock;
 	struct urb *urb;
+	void *buffer;
+	dma_addr_t buffer_dma;
 	int interval;
 	u8 read_regs_enabled:1;
 };


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 16/22] zd1211rw: lower hw command timeouts
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (14 preceding siblings ...)
  2011-01-31 18:49 ` [PATCH 15/22] zd1211rw: change interrupt URB buffer to DMA buffer Jussi Kivilinna
@ 2011-01-31 18:49 ` Jussi Kivilinna
  2011-01-31 18:49 ` [PATCH 17/22] zd1211rw: collect driver settings and add function to restore theim Jussi Kivilinna
                   ` (6 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:49 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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 |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index b866f7a..442c636 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -1392,7 +1392,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);
@@ -1407,7 +1407,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");
@@ -1463,7 +1463,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);
@@ -1552,7 +1552,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);


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 17/22] zd1211rw: collect driver settings and add function to restore theim
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (15 preceding siblings ...)
  2011-01-31 18:49 ` [PATCH 16/22] zd1211rw: lower hw command timeouts Jussi Kivilinna
@ 2011-01-31 18:49 ` Jussi Kivilinna
  2011-01-31 18:49 ` [PATCH 18/22] zd1211rw: add TX watchdog and device resetting Jussi Kivilinna
                   ` (5 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:49 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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 |    3 +
 2 files changed, 72 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 84ee1b8..e82f007 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);
 }
 
+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
@@ -988,6 +1053,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 281b307..c0f239e 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;
@@ -313,6 +314,8 @@ 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_restore_settings(struct zd_mac *mac);
+
 #ifdef DEBUG
 void zd_dump_rx_status(const struct rx_status *status);
 #else


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 18/22] zd1211rw: add TX watchdog and device resetting
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (16 preceding siblings ...)
  2011-01-31 18:49 ` [PATCH 17/22] zd1211rw: collect driver settings and add function to restore theim Jussi Kivilinna
@ 2011-01-31 18:49 ` Jussi Kivilinna
  2011-01-31 18:50 ` [PATCH 19/22] zd1211rw: reset device when CR_BCN_FIFO_SEMAPHORE freezes in beacon setup Jussi Kivilinna
                   ` (4 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:49 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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  |    8 +-
 drivers/net/wireless/zd1211rw/zd_mac.h  |    2 
 drivers/net/wireless/zd1211rw/zd_usb.c  |  161 +++++++++++++++++++++++++++++++
 drivers/net/wireless/zd1211rw/zd_usb.h  |   12 ++
 5 files changed, 181 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 907e656..54f68f1 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -1448,6 +1448,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;
 }
@@ -1455,6 +1456,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 e82f007..a590a94 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;
@@ -1409,6 +1409,9 @@ static void link_led_handler(struct work_struct *work)
 	int is_associated;
 	int r;
 
+	if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags))
+		goto requeue;
+
 	spin_lock_irq(&mac->lock);
 	is_associated = mac->associated;
 	spin_unlock_irq(&mac->lock);
@@ -1418,6 +1421,7 @@ static void link_led_handler(struct work_struct *work)
 	if (r)
 		dev_dbg_f(zd_mac_dev(mac), "zd_chip_control_leds error %d\n", r);
 
+requeue:
 	queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work,
 		           LINK_LED_WORK_DELAY);
 }
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index c0f239e..f8c93c3 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -314,6 +314,8 @@ 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
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index 442c636..00c333f 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -798,6 +798,7 @@ void zd_usb_disable_tx(struct zd_usb *usb)
 	usb_kill_anchored_urbs(&tx->submitted);
 
 	spin_lock_irqsave(&tx->lock, flags);
+	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
 	WARN_ON(tx->submitted_urbs != 0);
 	tx->submitted_urbs = 0;
 	spin_unlock_irqrestore(&tx->lock, flags);
@@ -895,6 +896,7 @@ static void tx_urb_complete(struct urb *urb)
 		goto resubmit;
 	}
 free_urb:
+	skb_unlink(skb, &usb->tx.submitted_skbs);
 	zd_mac_tx_to_dev(skb, urb->status);
 	usb_free_urb(urb);
 	tx_dec_submitted_urbs(usb);
@@ -924,6 +926,7 @@ resubmit:
 int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb)
 {
 	int r;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct usb_device *udev = zd_usb_to_usbdev(usb);
 	struct urb *urb;
 	struct zd_usb_tx *tx = &usb->tx;
@@ -942,10 +945,14 @@ 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] = (void *)jiffies;
+	skb_queue_tail(&tx->submitted_skbs, skb);
 	usb_anchor_urb(urb, &tx->submitted);
+
 	r = usb_submit_urb(urb, GFP_ATOMIC);
 	if (r) {
 		usb_unanchor_urb(urb);
+		skb_unlink(skb, &tx->submitted_skbs);
 		goto error;
 	}
 	tx_inc_submitted_urbs(usb);
@@ -956,6 +963,76 @@ 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_skbs;
+	struct sk_buff *skb, *skbnext;
+	struct ieee80211_tx_info *info;
+	unsigned long flags, trans_start;
+	bool have_timedout = false;
+
+	spin_lock_irqsave(&q->lock, flags);
+	skb_queue_walk_safe(q, skb, skbnext) {
+		info = IEEE80211_SKB_CB(skb);
+		trans_start = (unsigned long)info->rate_driver_data[1];
+
+		if (time_is_before_jiffies(trans_start + ZD_TX_TIMEOUT)) {
+			have_timedout = true;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&q->lock, flags);
+
+	return have_timedout;
+}
+
+static void zd_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 (!atomic_read(&tx->enabled) || !tx->watchdog_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, &tx->watchdog_work,
+			   ZD_TX_WATCHDOG_INTERVAL);
+}
+
+void zd_tx_watchdog_enable(struct zd_usb *usb)
+{
+	struct zd_usb_tx *tx = &usb->tx;
+
+	if (!tx->watchdog_enabled) {
+		dev_dbg_f(zd_usb_dev(usb), "\n");
+		queue_delayed_work(zd_workqueue, &tx->watchdog_work,
+				   ZD_TX_WATCHDOG_INTERVAL);
+		tx->watchdog_enabled = 1;
+	}
+}
+
+void zd_tx_watchdog_disable(struct zd_usb *usb)
+{
+	struct zd_usb_tx *tx = &usb->tx;
+
+	if (tx->watchdog_enabled) {
+		dev_dbg_f(zd_usb_dev(usb), "\n");
+		tx->watchdog_enabled = 0;
+		cancel_delayed_work_sync(&tx->watchdog_work);
+	}
+}
+
 static inline void init_usb_interrupt(struct zd_usb *usb)
 {
 	struct zd_usb_interrupt *intr = &usb->intr;
@@ -984,8 +1061,11 @@ static inline void init_usb_tx(struct zd_usb *usb)
 	spin_lock_init(&tx->lock);
 	atomic_set(&tx->enabled, 0);
 	tx->stopped = 0;
+	skb_queue_head_init(&tx->submitted_skbs);
 	init_usb_anchor(&tx->submitted);
 	tx->submitted_urbs = 0;
+	tx->watchdog_enabled = 0;
+	INIT_DELAYED_WORK(&tx->watchdog_work, zd_tx_watchdog_handler);
 }
 
 void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw,
@@ -1233,11 +1313,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 24db0dd..98f09c2 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,
@@ -196,9 +199,11 @@ struct zd_usb_rx {
 struct zd_usb_tx {
 	atomic_t enabled;
 	spinlock_t lock;
+	struct delayed_work watchdog_work;
+	struct sk_buff_head submitted_skbs;
 	struct usb_anchor submitted;
 	int submitted_urbs;
-	int stopped;
+	u8 stopped:1, watchdog_enabled:1;
 };
 
 /* Contains the usb parts. The structure doesn't require a lock because intf
@@ -210,7 +215,7 @@ struct zd_usb {
 	struct zd_usb_tx tx;
 	struct usb_interface *intf;
 	u8 req_buf[64]; /* zd_usb_iowrite16v needs 62 bytes */
-	u8 is_zd1211b:1, initialized:1;
+	u8 is_zd1211b:1, initialized:1, was_running:1;
 };
 
 #define zd_usb_dev(usb) (&usb->intf->dev)
@@ -237,6 +242,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] 25+ messages in thread

* [PATCH 19/22] zd1211rw: reset device when CR_BCN_FIFO_SEMAPHORE freezes in beacon setup
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (17 preceding siblings ...)
  2011-01-31 18:49 ` [PATCH 18/22] zd1211rw: add TX watchdog and device resetting Jussi Kivilinna
@ 2011-01-31 18:50 ` Jussi Kivilinna
  2011-01-31 18:50 ` [PATCH 20/22] zd1211rw: reset rx urbs after idle period of 30 seconds Jussi Kivilinna
                   ` (3 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:50 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz

When driver fails to acquire device semaphore lock, device usually
freezes soon afterwards. So failing to acquire lock indicates us that
not everything is going right in device/fw. So reset device when
this happens.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_mac.c |   13 ++++++++++++-
 1 files changed, 12 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index a590a94..beaa969 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -703,7 +703,7 @@ static int zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon)
 				dev_err(zd_mac_dev(mac),
 						"Giving up beacon config.\n");
 				r = -ETIMEDOUT;
-				goto release_sema;
+				goto reset_device;
 			}
 		}
 		msleep(20);
@@ -770,6 +770,17 @@ out:
 	mutex_unlock(&mac->chip.mutex);
 	kfree(ioreqs);
 	return r;
+
+reset_device:
+	mutex_unlock(&mac->chip.mutex);
+	kfree(ioreqs);
+
+	/* semaphore stuck, reset device to avoid fw freeze later */
+	dev_warn(zd_mac_dev(mac), "CR_BCN_FIFO_SEMAPHORE stuck, "
+				  "reseting device...");
+	usb_queue_reset_device(mac->chip.usb.intf);
+
+	return r;
 }
 
 static int fill_ctrlset(struct zd_mac *mac,


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 20/22] zd1211rw: reset rx urbs after idle period of 30 seconds
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (18 preceding siblings ...)
  2011-01-31 18:50 ` [PATCH 19/22] zd1211rw: reset device when CR_BCN_FIFO_SEMAPHORE freezes in beacon setup Jussi Kivilinna
@ 2011-01-31 18:50 ` Jussi Kivilinna
  2011-01-31 18:50 ` [PATCH 21/22] zd1211rw: enable NL80211_IFTYPE_AP Jussi Kivilinna
                   ` (2 subsequent siblings)
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:50 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz

RX appears to freeze while idle. Resetting rx-urbs appears to be enough to fix
this. Do reset 30 seconds after last rx.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_usb.c |   79 +++++++++++++++++++++++++++++++-
 drivers/net/wireless/zd1211rw/zd_usb.h |    7 ++-
 2 files changed, 83 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index 00c333f..5cedfea 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -638,6 +638,8 @@ static void rx_urb_complete(struct urb *urb)
 	usb = urb->context;
 	rx = &usb->rx;
 
+	zd_usb_reset_rx_idle_timer(usb);
+
 	if (length%rx->usb_packet_size > rx->usb_packet_size-4) {
 		/* If there is an old first fragment, we don't care. */
 		dev_dbg_f(urb_dev(urb), "*** first fragment ***\n");
@@ -702,7 +704,7 @@ static void free_rx_urb(struct urb *urb)
 	usb_free_urb(urb);
 }
 
-int zd_usb_enable_rx(struct zd_usb *usb)
+static int __zd_usb_enable_rx(struct zd_usb *usb)
 {
 	int i, r;
 	struct zd_usb_rx *rx = &usb->rx;
@@ -754,7 +756,21 @@ error:
 	return r;
 }
 
-void zd_usb_disable_rx(struct zd_usb *usb)
+int zd_usb_enable_rx(struct zd_usb *usb)
+{
+	int r;
+	struct zd_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	r = __zd_usb_enable_rx(usb);
+	mutex_unlock(&rx->setup_mutex);
+
+	zd_usb_reset_rx_idle_timer(usb);
+
+	return r;
+}
+
+static void __zd_usb_disable_rx(struct zd_usb *usb)
 {
 	int i;
 	unsigned long flags;
@@ -781,6 +797,40 @@ void zd_usb_disable_rx(struct zd_usb *usb)
 	spin_unlock_irqrestore(&rx->lock, flags);
 }
 
+void zd_usb_disable_rx(struct zd_usb *usb)
+{
+	struct zd_usb_rx *rx = &usb->rx;
+
+	mutex_lock(&rx->setup_mutex);
+	__zd_usb_disable_rx(usb);
+	mutex_unlock(&rx->setup_mutex);
+
+	cancel_delayed_work_sync(&rx->idle_work);
+}
+
+static void zd_usb_reset_rx(struct zd_usb *usb)
+{
+	bool do_reset;
+	struct zd_usb_rx *rx = &usb->rx;
+	unsigned long flags;
+
+	mutex_lock(&rx->setup_mutex);
+
+	spin_lock_irqsave(&rx->lock, flags);
+	do_reset = rx->urbs != NULL;
+	spin_unlock_irqrestore(&rx->lock, flags);
+
+	if (do_reset) {
+		__zd_usb_disable_rx(usb);
+		__zd_usb_enable_rx(usb);
+	}
+
+	mutex_unlock(&rx->setup_mutex);
+
+	if (do_reset)
+		zd_usb_reset_rx_idle_timer(usb);
+}
+
 /**
  * zd_usb_disable_tx - disable transmission
  * @usb: the zd1211rw-private USB structure
@@ -1033,6 +1083,29 @@ void zd_tx_watchdog_disable(struct zd_usb *usb)
 	}
 }
 
+static void zd_rx_idle_timer_handler(struct work_struct *work)
+{
+	struct zd_usb *usb =
+		container_of(work, struct zd_usb, rx.idle_work.work);
+	struct zd_mac *mac = zd_usb_to_mac(usb);
+
+	if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags))
+		return;
+
+	dev_dbg_f(zd_usb_dev(usb), "\n");
+
+	/* 30 seconds since last rx, reset rx */
+	zd_usb_reset_rx(usb);
+}
+
+void zd_usb_reset_rx_idle_timer(struct zd_usb *usb)
+{
+	struct zd_usb_rx *rx = &usb->rx;
+
+	cancel_delayed_work(&rx->idle_work);
+	queue_delayed_work(zd_workqueue, &rx->idle_work, ZD_RX_IDLE_INTERVAL);
+}
+
 static inline void init_usb_interrupt(struct zd_usb *usb)
 {
 	struct zd_usb_interrupt *intr = &usb->intr;
@@ -1047,12 +1120,14 @@ static inline void init_usb_rx(struct zd_usb *usb)
 {
 	struct zd_usb_rx *rx = &usb->rx;
 	spin_lock_init(&rx->lock);
+	mutex_init(&rx->setup_mutex);
 	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH) {
 		rx->usb_packet_size = 512;
 	} else {
 		rx->usb_packet_size = 64;
 	}
 	ZD_ASSERT(rx->fragment_length == 0);
+	INIT_DELAYED_WORK(&rx->idle_work, zd_rx_idle_timer_handler);
 }
 
 static inline void init_usb_tx(struct zd_usb *usb)
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h
index 98f09c2..2d688f4 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.h
+++ b/drivers/net/wireless/zd1211rw/zd_usb.h
@@ -34,6 +34,7 @@
 
 #define ZD_TX_TIMEOUT		(HZ * 5)
 #define ZD_TX_WATCHDOG_INTERVAL	round_jiffies_relative(HZ)
+#define ZD_RX_IDLE_INTERVAL	round_jiffies_relative(30 * HZ)
 
 enum devicetype {
 	DEVICE_ZD1211  = 0,
@@ -180,7 +181,9 @@ static inline struct usb_int_regs *get_read_regs(struct zd_usb_interrupt *intr)
 
 struct zd_usb_rx {
 	spinlock_t lock;
-	u8 fragment[2*USB_MAX_RX_SIZE];
+	struct mutex setup_mutex;
+	struct delayed_work idle_work;
+	u8 fragment[2 * USB_MAX_RX_SIZE];
 	unsigned int fragment_length;
 	unsigned int usb_packet_size;
 	struct urb **urbs;
@@ -251,6 +254,8 @@ void zd_usb_disable_int(struct zd_usb *usb);
 int zd_usb_enable_rx(struct zd_usb *usb);
 void zd_usb_disable_rx(struct zd_usb *usb);
 
+void zd_usb_reset_rx_idle_timer(struct zd_usb *usb);
+
 void zd_usb_enable_tx(struct zd_usb *usb);
 void zd_usb_disable_tx(struct zd_usb *usb);
 


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* [PATCH 21/22] zd1211rw: enable NL80211_IFTYPE_AP
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (19 preceding siblings ...)
  2011-01-31 18:50 ` [PATCH 20/22] zd1211rw: reset rx urbs after idle period of 30 seconds Jussi Kivilinna
@ 2011-01-31 18:50 ` Jussi Kivilinna
  2011-01-31 18:50 ` [PATCH 22/22] zd1211rw: add useful debug output Jussi Kivilinna
  2011-02-04 14:06 ` [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:50 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, 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 beaa969..74a269e 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -1038,6 +1038,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:
@@ -1214,7 +1215,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);
@@ -1317,7 +1319,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] 25+ messages in thread

* [PATCH 22/22] zd1211rw: add useful debug output
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (20 preceding siblings ...)
  2011-01-31 18:50 ` [PATCH 21/22] zd1211rw: enable NL80211_IFTYPE_AP Jussi Kivilinna
@ 2011-01-31 18:50 ` Jussi Kivilinna
  2011-02-04 14:06 ` [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
  22 siblings, 0 replies; 25+ messages in thread
From: Jussi Kivilinna @ 2011-01-31 18:50 UTC (permalink / raw)
  To: linux-wireless; +Cc: Daniel Drake, John W. Linville, Ulrich Kunitz

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
---
 drivers/net/wireless/zd1211rw/zd_usb.c |    9 ++++++++-
 1 files changed, 8 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index 5cedfea..4e6c06a 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -411,8 +411,10 @@ static void int_urb_complete(struct urb *urb)
 	case -ENOENT:
 	case -ECONNRESET:
 	case -EPIPE:
+		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
 		return;
 	default:
+		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
 		goto resubmit;
 	}
 
@@ -613,6 +615,7 @@ static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer,
 
 static void rx_urb_complete(struct urb *urb)
 {
+	int r;
 	struct zd_usb *usb;
 	struct zd_usb_rx *rx;
 	const u8 *buffer;
@@ -627,6 +630,7 @@ static void rx_urb_complete(struct urb *urb)
 	case -ENOENT:
 	case -ECONNRESET:
 	case -EPIPE:
+		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
 		return;
 	default:
 		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
@@ -668,7 +672,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)
@@ -1001,6 +1007,7 @@ int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb)
 
 	r = usb_submit_urb(urb, GFP_ATOMIC);
 	if (r) {
+		dev_dbg_f(zd_usb_dev(usb), "error submit urb %p %d\n", urb, r);
 		usb_unanchor_urb(urb);
 		skb_unlink(skb, &tx->submitted_skbs);
 		goto error;


^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: [PATCH 00/22] zd1211rw: add support for AP-mode
  2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
                   ` (21 preceding siblings ...)
  2011-01-31 18:50 ` [PATCH 22/22] zd1211rw: add useful debug output Jussi Kivilinna
@ 2011-02-04 14:06 ` Jussi Kivilinna
  2011-02-04 21:25   ` John W. Linville
  22 siblings, 1 reply; 25+ messages in thread
From: Jussi Kivilinna @ 2011-02-04 14:06 UTC (permalink / raw)
  To: Jussi Kivilinna
  Cc: linux-wireless, Daniel Drake, John W. Linville, Ulrich Kunitz

Hello,

Is this too much, should I split fixes (oops and device reset for  
TX-stall) to separate patchset?

OOPS fixed by first patch and TX-stall/device freeze under heavy load  
happens in station mode (and AP, probably adhoc too). Heavy load here  
means connected at rate 54Mbit, with iperf running both ways, freezes  
usually with in ~20 minutes.

-Jussi

Quoting Jussi Kivilinna <jussi.kivilinna@mbnet.fi>:

> This patchset adds AP-mode support to zd1211rw. Tested with zd1211b  
> devices only.
>
> Patches are mix of fixes and AP support adding code.
>    [1-2]  fix oopses noticed while testing
>      [3]  add missing locking
>      [4]  fix beacon setup to match vendor driver (needed for AP mode)
>    [5-6]  'may sleep' cleanups, move code from workers to mac80211-functions
>   [7-11]  AP-mode supporting code
>  [12-16]  beacon setup fixes/workarounds
>  [17-20]  device reset and device/TX/RX stall workarounds
>     [21]  enable AP mode
>     [22]  add more debuging output
>
> ---
>
> Christian Lamparter (1):
>       mac80211: fix race between next beacon dtim and  
> ieee80211_get_buffered_bc
>
> Jussi Kivilinna (21):
>       zd1211rw: use urb anchors for tx and fix tx-queue disabling
>       zd1211rw: cancel process_intr work on zd_chip_disable_int()
>       zd1211rw: add locking for mac->process_intr
>       zd1211rw: fix beacon interval setup
>       zd1211rw: move set_multicast_hash and set_rx_filter from  
> workers to configure_filter
>       zd1211rw: move set_rts_cts_work to bss_info_changed
>       zd1211rw: support setting BSSID for AP mode
>       zd1211rw: fix ack_pending in filter_ack causing tx-packet  
> ordering problem on monitor
>       zd1211rw: let zd_set_beacon_interval() set dtim_period and add  
> AP-beacon flag
>       zd1211rw: implement beacon fetching and handling  
> ieee80211_get_buffered_bc()
>       zd1211rw: add beacon watchdog and setting HW beacon more failsafe
>       zd1211rw: batch beacon config commands together
>       [v2] zd1211rw: use stack and preallocated memory for small cmd-buffers
>       zd1211rw: change interrupt URB buffer to DMA buffer
>       zd1211rw: lower hw command timeouts
>       zd1211rw: collect driver settings and add function to restore theim
>       zd1211rw: add TX watchdog and device resetting
>       zd1211rw: reset device when CR_BCN_FIFO_SEMAPHORE freezes in  
> beacon setup
>       zd1211rw: reset rx urbs after idle period of 30 seconds
>       zd1211rw: enable NL80211_IFTYPE_AP
>       zd1211rw: add useful debug output
>
>
>  drivers/net/wireless/zd1211rw/zd_chip.c |  134 ++++++---
>  drivers/net/wireless/zd1211rw/zd_chip.h |    5
>  drivers/net/wireless/zd1211rw/zd_mac.c  |  448  
> ++++++++++++++++++++++++-------
>  drivers/net/wireless/zd1211rw/zd_mac.h  |   24 +-
>  drivers/net/wireless/zd1211rw/zd_usb.c  |  445  
> ++++++++++++++++++++++++-------
>  drivers/net/wireless/zd1211rw/zd_usb.h  |   30 ++
>  net/mac80211/ieee80211_i.h              |    1
>  net/mac80211/tx.c                       |    4
>  8 files changed, 822 insertions(+), 269 deletions(-)
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>




^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH 00/22] zd1211rw: add support for AP-mode
  2011-02-04 14:06 ` [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
@ 2011-02-04 21:25   ` John W. Linville
  0 siblings, 0 replies; 25+ messages in thread
From: John W. Linville @ 2011-02-04 21:25 UTC (permalink / raw)
  To: Jussi Kivilinna; +Cc: linux-wireless, Daniel Drake, Ulrich Kunitz

On Fri, Feb 04, 2011 at 04:06:08PM +0200, Jussi Kivilinna wrote:
> Hello,
> 
> Is this too much, should I split fixes (oops and device reset for
> TX-stall) to separate patchset?
> 
> OOPS fixed by first patch and TX-stall/device freeze under heavy
> load happens in station mode (and AP, probably adhoc too). Heavy
> load here means connected at rate 54Mbit, with iperf running both
> ways, freezes usually with in ~20 minutes.
> 
> -Jussi

It looks basically fine to me.

I'll just take the series for 2.6.39, plenty of time to deal with
any problems... :-)

John
-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2011-02-04 21:30 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-01-31 18:46 [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
2011-01-31 18:47 ` [PATCH 01/22] zd1211rw: use urb anchors for tx and fix tx-queue disabling Jussi Kivilinna
2011-01-31 18:47 ` [PATCH 02/22] zd1211rw: cancel process_intr work on zd_chip_disable_int() Jussi Kivilinna
2011-01-31 18:47 ` [PATCH 03/22] zd1211rw: add locking for mac->process_intr Jussi Kivilinna
2011-01-31 18:47 ` [PATCH 04/22] zd1211rw: fix beacon interval setup Jussi Kivilinna
2011-01-31 18:47 ` [PATCH 05/22] zd1211rw: move set_multicast_hash and set_rx_filter from workers to configure_filter Jussi Kivilinna
2011-01-31 18:47 ` [PATCH 06/22] zd1211rw: move set_rts_cts_work to bss_info_changed Jussi Kivilinna
2011-01-31 18:48 ` [PATCH 07/22] zd1211rw: support setting BSSID for AP mode Jussi Kivilinna
2011-01-31 18:48 ` [PATCH 08/22] zd1211rw: fix ack_pending in filter_ack causing tx-packet ordering problem on monitor Jussi Kivilinna
2011-01-31 18:48 ` [PATCH 09/22] zd1211rw: let zd_set_beacon_interval() set dtim_period and add AP-beacon flag Jussi Kivilinna
2011-01-31 18:48 ` [PATCH 10/22] zd1211rw: implement beacon fetching and handling ieee80211_get_buffered_bc() Jussi Kivilinna
2011-01-31 18:48 ` [PATCH 11/22] mac80211: fix race between next beacon dtim and ieee80211_get_buffered_bc Jussi Kivilinna
2011-01-31 18:48 ` [PATCH 12/22] zd1211rw: add beacon watchdog and setting HW beacon more failsafe Jussi Kivilinna
2011-01-31 18:49 ` [PATCH 13/22] zd1211rw: batch beacon config commands together Jussi Kivilinna
2011-01-31 18:49 ` [PATCH 14/22] [v2] zd1211rw: use stack and preallocated memory for small cmd-buffers Jussi Kivilinna
2011-01-31 18:49 ` [PATCH 15/22] zd1211rw: change interrupt URB buffer to DMA buffer Jussi Kivilinna
2011-01-31 18:49 ` [PATCH 16/22] zd1211rw: lower hw command timeouts Jussi Kivilinna
2011-01-31 18:49 ` [PATCH 17/22] zd1211rw: collect driver settings and add function to restore theim Jussi Kivilinna
2011-01-31 18:49 ` [PATCH 18/22] zd1211rw: add TX watchdog and device resetting Jussi Kivilinna
2011-01-31 18:50 ` [PATCH 19/22] zd1211rw: reset device when CR_BCN_FIFO_SEMAPHORE freezes in beacon setup Jussi Kivilinna
2011-01-31 18:50 ` [PATCH 20/22] zd1211rw: reset rx urbs after idle period of 30 seconds Jussi Kivilinna
2011-01-31 18:50 ` [PATCH 21/22] zd1211rw: enable NL80211_IFTYPE_AP Jussi Kivilinna
2011-01-31 18:50 ` [PATCH 22/22] zd1211rw: add useful debug output Jussi Kivilinna
2011-02-04 14:06 ` [PATCH 00/22] zd1211rw: add support for AP-mode Jussi Kivilinna
2011-02-04 21:25   ` John W. Linville

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.