Possible fix for hostap on dual-slot sleeve iPaq

Wodecki, Victor Contractor Victor.Wodecki
Mon May 9 17:32:59 PDT 2005


This is related to the problem described at
http://lists.shmoo.com/pipermail/hostap/2005-April/009994.html

The scenario is when using hostap on an iPaq 5550, using a dual-slot
sleeve holding a D-Link DCF660W.

The symptom is numerous stalls when trying to use scp or any other
high data rate use of that interface.

After some amount of data, the card eventually goes from READY to
NOT READY, and doesn't return to READY without being reset.

The problem doesn't occur with single slot sleeves with part number
173396-001 but does occur in dual slot sleeves with part number
216198-B21.  And we have only today found that the problem also occurs
with new (?) model single slot sleeves with part number 249704-B22.

Experimentation has shown that getting the hostap driver to twiddle
its thumbs between reading hardware registers and writing hardware
registers in some particular circumstances avoids this problem.  A
patch for this is included below, comment welcome.  In effect, this
is a form of rate limiting, but the difference in throughput is quite
small, and the stability is vastly improved.

We have tested this patch on hostap 0.3.7 as well as 0.4.0, it applies
cleanly to both and has the desired effect on both.  Note that this
patch is additional to that shown in the above URL.

Testing has shown that it doesn't appear to be a power issue, and
given that the problem is observed on two models of the available
three different sleeve variants, the problem is most likely either
with the sleeves themselves, or with the sleeve driver.

The patch isn't perfect, it's really just a proof of concept that has
been tidied up.  The amount of time (microseconds) that the driver
twiddles its thumbs for is called 'twiddle'.  It defaults to 25
microseconds, but can be varied either by a procfs entry or via a
module parameter.

There is also an #ifdef to enable the patch; by default (at this stage)
it is enabled.

I welcome feedback from any hostap users using iPaq h5550.  I plan to
also post in the h5400-port mailing list at handhelds.org, but given
that this patch is in the hostap driver, I thought I'd start here.



Index: hostap_hw.c
===================================================================
--- hostap_hw.c	(revision 56)
+++ hostap_hw.c	(revision 64)
@@ -29,6 +29,20 @@
  *   (spin_lock_bh) to prevent concurrent use.
  */
 
+/*
+ * Enabling the following #define causes this driver to wait an
+ * arbitrary amount of time between reading and writing registers
+ * in some instances.  This avoids the problem of timeouts when
+ * using a Prism2-based card in a dual-slot sleeve on an iPaq 5500.
+ *
+ * The amount of time to wait (twiddle thumbs) is called 'twiddle',
+ * and can be specified either by module parameters or via
+ * /proc/net/hostap/twiddle
+ *
+ * The value is a number of microseconds; testing has shown that
+ * values in the range 10-30 work best.
+ */
+#define IPAQ_DUAL_SLEEVE
 
 #include <linux/config.h>
 #include <linux/version.h>
@@ -61,6 +75,13 @@
 MODULE_PARM(mtu, "i");
 MODULE_PARM_DESC(mtu, "Maximum transfer unit");
 
+#ifdef IPAQ_DUAL_SLEEVE
+static int twiddle = 25;
+MODULE_PARM(twiddle, "i");
+MODULE_PARM_DESC(twiddle, "Twiddle thumbs for N microseconds");
+static struct proc_dir_entry *twiddle_file;
+#endif
+
 static int channel[MAX_PARM_DEVICES] = { 3, DEF_INTS };
 MODULE_PARM(channel, PARM_MIN_MAX "i");
 MODULE_PARM_DESC(channel, "Initial channel");
@@ -1294,6 +1315,10 @@
 
 	PDEBUG(DEBUG_FLOW, "prism2_hw_init()\n");
 
+#ifdef IPAQ_DUAL_SLEEVE
+	printk(KERN_DEBUG "%s: twiddle=%d us\n", __FUNCTION__, twiddle);
+#endif
+
 	clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits);
 
  init:
@@ -2060,6 +2085,7 @@
 		/* Internal BAP0 offset points to the byte following rxdesc;
 		 * copy rest of the data using bus master */
 		addr = virt_to_phys(skb_put(skb, len));
+
 		HFA384X_OUTW((addr & 0xffff0000) >> 16,
 			     HFA384X_PCI_M0_ADDRH_OFF);
 		HFA384X_OUTW(addr & 0x0000ffff, HFA384X_PCI_M0_ADDRL_OFF);
@@ -2097,6 +2123,9 @@
  rx_exit:
 	prism2_callback(local, PRISM2_CALLBACK_RX_END);
 	if (!rx_pending) {
+#ifdef IPAQ_DUAL_SLEEVE
+	        udelay (twiddle);
+#endif
 		HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF);
 	}
 
@@ -2366,6 +2395,9 @@
 	kfree(payload);
 
  fail:
+#ifdef IPAQ_DUAL_SLEEVE
+	udelay (twiddle);
+#endif
 	HFA384X_OUTW(HFA384X_EV_TX, HFA384X_EVACK_OFF);
 }
 
@@ -2552,6 +2584,9 @@
 	tasklet_schedule(&local->info_tasklet);
 
  out:
+#ifdef IPAQ_DUAL_SLEEVE
+	udelay (twiddle);
+#endif
 	HFA384X_OUTW(HFA384X_EV_INFO, HFA384X_EVACK_OFF);
 }
 
@@ -2687,6 +2722,9 @@
 	if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) {
 		if (!local->hw_ready)
 			return;
+#ifdef IPAQ_DUAL_SLEEVE
+		udelay (twiddle);
+#endif
 		HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF);
 		if (time_after(jiffies, last_magic_err + 10 * HZ)) {
 			printk("%s: Interrupt, but SWSUPPORT0 does not match: "
@@ -2731,6 +2769,9 @@
 	prism2_check_magic(local);
 
 	for (;;) {
+#ifdef IPAQ_DUAL_SLEEVE
+	        udelay (twiddle);
+#endif
 		ev = HFA384X_INW(HFA384X_EVSTAT_OFF);
 		if (ev == 0xffff) {
 			if (local->shutdown)
@@ -2773,12 +2814,18 @@
 				       !local->dev_enabled ?
 				       " (!dev_enabled)" : "");
 			}
+#ifdef IPAQ_DUAL_SLEEVE
+			udelay (twiddle);
+#endif
 			HFA384X_OUTW(ev, HFA384X_EVACK_OFF);
 			return IRQ_HANDLED;
 		}
 
 		if (ev & HFA384X_EV_TICK) {
 			prism2_ev_tick(dev);
+#ifdef IPAQ_DUAL_SLEEVE
+			udelay (twiddle);
+#endif
 			HFA384X_OUTW(HFA384X_EV_TICK, HFA384X_EVACK_OFF);
 		}
 
@@ -2799,6 +2846,9 @@
 
 		if (ev & HFA384X_EV_ALLOC) {
 			prism2_alloc_ev(dev);
+#ifdef IPAQ_DUAL_SLEEVE
+			udelay (twiddle);
+#endif
 			HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF);
 		}
 
@@ -2806,18 +2856,27 @@
 		 * in tasklets. TX, TXEXC, RX, and INFO events will be ACKed
 		 * and unmasked after needed data has been read completely. */
 		if (ev & HFA384X_BAP0_EVENTS) {
+#ifdef IPAQ_DUAL_SLEEVE
+		        udelay (twiddle);
+#endif
 			hfa384x_events_no_bap0(dev);
 			tasklet_schedule(&local->bap_tasklet);
 		}
 
 #ifndef final_version
 		if (ev & HFA384X_EV_WTERR) {
+#ifdef IPAQ_DUAL_SLEEVE
+		        udelay (twiddle);
+#endif
 			PDEBUG(DEBUG_EXTRA, "%s: WTERR event\n", dev->name);
 			HFA384X_OUTW(HFA384X_EV_WTERR, HFA384X_EVACK_OFF);
 		}
 #endif /* final_version */
 
 		if (ev & HFA384X_EV_INFDROP) {
+#ifdef IPAQ_DUAL_SLEEVE
+		  	udelay (twiddle);
+#endif
 			prism2_infdrop(dev);
 			HFA384X_OUTW(HFA384X_EV_INFDROP, HFA384X_EVACK_OFF);
 		}
@@ -3203,6 +3262,46 @@
 }
 
 
+#ifndef PRISM2_NO_PROCFS_DEBUG
+#ifdef IPAQ_DUAL_SLEEVE
+static int proc_read_twiddle(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+        int len;
+
+	MOD_INC_USE_COUNT;
+	len = sprintf (page, "%d\n", twiddle);
+	MOD_DEC_USE_COUNT;
+
+	return len;
+}
+
+
+static int proc_write_twiddle(struct file *file, const char *buffer, unsigned long count, void *data)
+{
+#define TWIDDLELENGTH 20
+        char ourcopy [TWIDDLELENGTH + 1];
+	int len;
+
+	MOD_INC_USE_COUNT;
+	len = (count < TWIDDLELENGTH) ? count : TWIDDLELENGTH;
+	if (copy_from_user (ourcopy, buffer, len)) {
+	        MOD_DEC_USE_COUNT;
+		return -EFAULT;
+	}
+	ourcopy [len] = '\0';
+	twiddle = simple_strtol (ourcopy, NULL, 10);
+#undef TWIDDLELENGTH
+
+	printk(KERN_DEBUG "%s(): twiddle now %d\n", __FUNCTION__, twiddle);
+
+	MOD_DEC_USE_COUNT;
+
+	return len;
+}
+#endif /* IPAQ_DUAL_SLEEVE */
+#endif /* PRISM2_NO_PROCFS_DEBUG */
+
+
 static struct net_device *
 prism2_init_local_data(struct prism2_helper_functions *funcs, int card_idx)
 {
@@ -3386,6 +3485,14 @@
 #ifndef PRISM2_NO_PROCFS_DEBUG
 	create_proc_read_entry("registers", 0, local->proc,
 			       prism2_registers_proc_read, local);
+#ifdef IPAQ_DUAL_SLEEVE
+	twiddle_file = create_proc_entry("net/hostap/twiddle", 0644, local->proc);
+	if (twiddle_file) {
+	        twiddle_file->read_proc = proc_read_twiddle;
+		twiddle_file->write_proc = proc_write_twiddle;
+		twiddle_file->owner = THIS_MODULE;
+	}
+#endif /* IPAQ_DUAL_SLEEVE */
 #endif /* PRISM2_NO_PROCFS_DEBUG */
 
 	hostap_init_data(local);
@@ -3474,6 +3581,10 @@
 #ifndef PRISM2_NO_PROCFS_DEBUG
 	if (local->proc != NULL)
 		remove_proc_entry("registers", local->proc);
+#ifdef IPAQ_DUAL_SLEEVE
+	if (twiddle_file != NULL)
+	        remove_proc_entry("twiddle", twiddle_file);
+#endif /* IPAQ_DUAL_SLEEVE */
 #endif /* PRISM2_NO_PROCFS_DEBUG */
 	hostap_remove_proc(local);


-- 

This email remains the property of the Australian Defence Organisation and is subject to the jurisdiction of section 70 of the Crimes Act 1914.  If you have received this email in error, you are requested to contact the sender and delete the email.




More information about the Hostap mailing list