Autospy link quality patch for ad-hoc orinoco networks

Jim Carter jimc at math.ucla.edu
Tue Feb 19 07:02:44 EST 2002


I usually use my 802.11b set in ad-hoc mode between a laptop and a
server-gateway running Linux, and I miss the link quality measures
available in managed mode.  While struggling with problems (reported
separately), I spotted and changed some additional minor items.

Here's what I patched:

1.  Added autospy, to give a link quality readout the same in ad-hoc
    mode as you would get in managed mode.  If enabled at compile and
    run time, if in ad-hoc mode, if no explicit spy list is set, if you
    send a packet to station X, then X becomes or replaces the current
    spy address, just one at a time.  With multiple destinations,
    attention stays on one until it is not sent to for a settable time
    (parameter autospy, in seconds; 0 turns it off).  There is an
    addition in orinoco_xmit to set the spy address, and in
    orinoco_ioctl_setspy to shut off autospy while there's an explicit
    spy list, and private ioctls to get and set the parameter.  One cell
    is added to orinoco_private.

2.  In ad-hoc mode, WinXP uses OWNSSID received from the partner to
    select the configuration; thus, in orinoco_reset, OWNSSID needs to
    be set in IBSS mode, not just Ad-Hoc Demo mode.

3.  The card really does generate Tx interrupts with void EVSTAT -- lots
    of them, for the Dell Truemobile 1150 (Agere) mini-PCI card.  The
    warning message in orinoco_interrupt was commented out.

4.  In orinoco_init, an arbitrary default channel (6, following WinXP)
    is set up, to keep iwconfig from showing the frequency of channel 0
    as 42.9 GHz.

5.  In orinoco_spy_gather, it breaks from the loop after the first address
    match, and a comment was augmented.  The call to gather stats is
    conditional on AUTO_SPY as well as SPY_NUMBER.

6.  In orinoco_ioctl_getiwrange, the range for quality is reported
    unconditionally, if autospy is compiled in.

7.  In orinoco_proc_get_hermes_recs, the first spy address and quality
    measure is dumped (if present).  The output format is broken out so
    all three sprintfs can use it.

8.  In hermes_read_ltv, if the returned RID is not what was requested,
    the program seeks and reads it again, up to 8 times until it's right.
    The error message was clarified.  A comment was changed in in the
    heading that doesn't match the actual code.  The FIXME about min(actual,
    requested) length was implemented.

James F. Carter          Voice 310 825 2897    FAX 310 206 6673
UCLA-Mathnet;  6115 MSA; 405 Hilgard Ave.; Los Angeles, CA, USA  90095-1555
Email: jimc at math.ucla.edu    http://www.math.ucla.edu/~jimc (q.v. for PGP key)


*** drivers/net/wireless/orinoco.c.orig18	Sat Feb  9 17:16:04 2002
--- drivers/net
/wireless/orinoco.c	Sun Feb 17 22:44:12 2002
***************
*** 331,336 ****
--- 331,347 ----
  MODULE_PARM(orinoco_debug, "i");
  #endif

+ /* Automatic spy parameter (jimc) */
+ #ifdef ORINOCO_AUTOSPY
+ static int autospy /* = 0 */;
+ MODULE_PARM(autospy, "i");
+ MODULE_PARM_DESC(autospy, "Turns on auto-spy; attention span in seconds.");
+ /* "if (AUTO_SPY(priv)) {}" can be optimized out of existence, if turned off.*/
+ #define AUTO_SPY(priv)	(autospy)
+ #else
+ #define AUTO_SPY(priv)	0
+ #endif /*ORINOCO_AUTOSPY*/
+
  #define ORINOCO_MIN_MTU		256
  #define ORINOCO_MAX_MTU		(HERMES_FRAME_LEN_MAX - ENCAPS_OVERHEAD)

***************
*** 658,667 ****
  	/* Set the desired ESSID */
  	idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
  	memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
! 	err = hermes_write_ltv(hw, USER_BAP, (priv->port_type == 3) ?
! 			       HERMES_RID_CNFOWNSSID : HERMES_RID_CNFDESIREDSSID,
! 			       HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
! 			       &idbuf);
  	if (err)
  		goto out;

--- 669,683 ----
  	/* Set the desired ESSID */
  	idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
  	memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
! 	/* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
! 	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
! 			HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
! 			&idbuf);
! 	if (err)
! 		goto out;
! 	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
! 			HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
! 			&idbuf);
  	if (err)
  		goto out;

***************
*** 1138,1144 ****

  	if (! events) { /* Sometimes the card generates Tx interrupts without setting EVSTAT,
  			   or so I've heard - FIXME does it really happen? */
! 		printk(KERN_WARNING "%s: Null event in orinoco_interrupt!\n", priv->ndev.name);
  		__orinoco_ev_alloc(priv, hw);
  	}

--- 1154,1160 ----

  	if (! events) { /* Sometimes the card generates Tx interrupts without setting EVSTAT,
  			   or so I've heard - FIXME does it really happen? */
! 		/* Yes.  Commented out by jimc: printk(KERN_WARNING "%s: Null event in orinoco_interrupt!\n", priv->ndev.name); */
  		__orinoco_ev_alloc(priv, hw);
  	}

***************
*** 1770,1775 ****
--- 1786,1793 ----

  	/* Set up the default configuration */
  	priv->iw_mode = IW_MODE_INFRA;
+ 	/* Pick an arbitrary default channel, not 0, for neat iwconfig output */
+ 	priv->channel = 6;
  	/* By default use IEEE/IBSS ad-hoc mode if we have it */
  	priv->prefer_port3 = priv->has_port3 && (! priv->has_ibss);
  	set_port_type(priv);
***************
*** 1856,1869 ****
  	int i;

  	/* Gather wireless spy statistics: for each packet, compare the
! 	 * source address with out list, and if match, get the stats... */
  	for (i = 0; i < priv->spy_number; i++)
  		if (!memcmp(mac, priv->spy_address[i], ETH_ALEN)) {
  			priv->spy_stat[i].level = level - 0x95;
  			priv->spy_stat[i].noise = noise - 0x95;
  			priv->spy_stat[i].qual = level - noise;
  			priv->spy_stat[i].updated = 7;
  		}
  }

  void
--- 1874,1890 ----
  	int i;

  	/* Gather wireless spy statistics: for each packet, compare the
! 	 * source address with our list, and if match, save the stats...
! 	 * level, noise in the range 0x95 (least) .. 0x2f (strongest). */
  	for (i = 0; i < priv->spy_number; i++)
  		if (!memcmp(mac, priv->spy_address[i], ETH_ALEN)) {
  			priv->spy_stat[i].level = level - 0x95;
  			priv->spy_stat[i].noise = noise - 0x95;
  			priv->spy_stat[i].qual = level - noise;
  			priv->spy_stat[i].updated = 7;
+ 			break;
  		}
+
  }

  void
***************
*** 1883,1889 ****
  	 */
  	/* Note : gcc will optimise the whole section away if
  	 * WIRELESS_SPY is not defined... - Jean II */
! 	if (SPY_NUMBER(priv)) {
  		orinoco_spy_gather(dev, skb->mac.raw + ETH_ALEN,
  				   hdr->desc.signal, hdr->desc.silence);
  	}
--- 1904,1910 ----
  	 */
  	/* Note : gcc will optimise the whole section away if
  	 * WIRELESS_SPY is not defined... - Jean II */
! 	if (SPY_NUMBER(priv) || AUTO_SPY(priv)) {
  		orinoco_spy_gather(dev, skb->mac.raw + ETH_ALEN,
  				   hdr->desc.signal, hdr->desc.silence);
  	}
***************
*** 1902,1907 ****
--- 1923,1932 ----
  	int len, data_len, data_off;
  	struct orinoco_txframe_hdr hdr;
  	hermes_response_t resp;
+ #ifdef ORINOCO_AUTOSPY
+ 	static const unsigned char broadcast[ETH_ALEN] =
+ 		{0xff,0xff,0xff,0xff,0xff,0xff};
+ #endif /*ORINOCO_AUTOSPY*/

  	if (! netif_running(dev)) {
  		printk(KERN_ERR "%s: Tx on stopped device!\n",
***************
*** 1999,2004 ****
--- 2024,2052 ----
  	dev->trans_start = jiffies;
  	stats->tx_bytes += data_off + data_len;

+ #ifdef ORINOCO_AUTOSPY
+ 	if (! AUTO_SPY(priv) || (priv->spy_number && !priv->autospy_when) ||
+ 				!memcmp(broadcast, eh->h_dest, ETH_ALEN)) {
+ 		/* Skip if turned off by module parm or explicit spy list */
+ 	} else if (!priv->spy_number || jiffies-priv->autospy_when > autospy) {
+ 			/* No spy target, or haven't sent a pkt for N seconds:
+ 			Add/replace spy target.  OK to replace with same tgt.*/
+ 		if (!priv->spy_number) {
+ 				/* Initialize stats only on new address. */
+ 			priv->spy_stat[0].level = 0;
+ 			priv->spy_stat[0].noise = 0;
+ 			priv->spy_stat[0].qual = 0;
+ 		}
+ 		priv->spy_stat[0].updated = 0;
+ 		memcpy(priv->spy_address[0], eh->h_dest, ETH_ALEN);
+ 		priv->autospy_when = jiffies;
+ 		priv->spy_number = 1;	/* Last step, let irpt path store data*/
+ 	} else if (memcmp(priv->spy_address[0], eh->h_dest, ETH_ALEN)) {
+ 			/* Still using the same spy target */
+ 		priv->autospy_when = jiffies;
+ 	}
+ #endif /*ORINOCO_AUTOSPY*/
+
  	netif_stop_queue(dev);

  	orinoco_unlock(priv);
***************
*** 2083,2090 ****

  	range.sensitivity = 3;

  	if ((mode == IW_MODE_ADHOC) && (priv->spy_number == 0)){
! 		/* Quality stats meaningless in ad-hoc mode */
  		range.max_qual.qual = 0;
  		range.max_qual.level = 0;
  		range.max_qual.noise = 0;
--- 2131,2139 ----

  	range.sensitivity = 3;

+ #ifndef ORINOCO_AUTOSPY
  	if ((mode == IW_MODE_ADHOC) && (priv->spy_number == 0)){
! 		/* Quality stats meaningless in ad-hoc mode, unless autospy! */
  		range.max_qual.qual = 0;
  		range.max_qual.level = 0;
  		range.max_qual.noise = 0;
***************
*** 2094,2100 ****
  		range.avg_qual.noise = 0;
  #endif /* WIRELESS_EXT > 11 */

! 	} else {
  		range.max_qual.qual = 0x8b - 0x2f;
  		range.max_qual.level = 0x2f - 0x95 - 1;
  		range.max_qual.noise = 0x2f - 0x95 - 1;
--- 2143,2151 ----
  		range.avg_qual.noise = 0;
  #endif /* WIRELESS_EXT > 11 */

! 	} else
! #endif /*ORINOCO_AUTOSPY*/
! 	{
  		range.max_qual.qual = 0x8b - 0x2f;
  		range.max_qual.level = 0x2f - 0x95 - 1;
  		range.max_qual.noise = 0x2f - 0x95 - 1;
***************
*** 2926,2931 ****
--- 2977,2987 ----
  		/* Reset stats */
  		memset(priv->spy_stat, 0,
  		       sizeof(struct iw_quality) * IW_MAX_SPY);
+ 		/* Shut off autospy temporarily */
+ #ifdef ORINOCO_AUTOSPY
+ 		if (AUTO_SPY(priv))
+ 		       priv->autospy_when = 0;
+ #endif /*ORINOCO_AUTOSPY*/
  		/* Set number of addresses */
  		priv->spy_number = number;
  	}
***************
*** 3240,3245 ****
--- 3296,3309 ----
  				{ SIOCIWFIRSTPRIV + 0x7, 0,
  				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
  				  "get_ibssport" }
+ #ifdef ORINOCO_AUTOSPY
+ 				,{ SIOCIWFIRSTPRIV + 0x8,
+ 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ 				  0, "set_autospy" }
+ 				,{ SIOCIWFIRSTPRIV + 0x9, 0,
+ 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ 				  "get_autospy" }
+ #endif /*ORINOCO_AUTOSPY*/
  			};

  			err = verify_area(VERIFY_WRITE, wrq->u.data.pointer, sizeof(privtab));
***************
*** 3336,3341 ****
--- 3400,3406 ----
  		} else
  			err = -EOPNOTSUPP;
  		break;
+
  	case SIOCIWFIRSTPRIV + 0x6: /* set_ibssport */
  		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x6 (set_ibssport)\n",
  		      dev->name);
***************
*** 3355,3360 ****
--- 3420,3442 ----
  		err = orinoco_ioctl_getibssport(dev, wrq);
  		break;

+ 	case SIOCIWFIRSTPRIV + 0x8: /* set_autospy */
+ 		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x8 (set_autospy)\n",
+ 		      dev->name);
+ 		if (! capable(CAP_NET_ADMIN)) {
+ 			err = -EPERM;
+ 			break;
+ 		}
+
+ 		autospy = *( (int *) wrq->u.name );
+ 		break;
+
+ 	case SIOCIWFIRSTPRIV + 0x9: /* get_autospy */
+ 		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x9 (get_autospy)\n",
+ 		      dev->name);
+ 		*( (int *) wrq->u.name ) = autospy;
+ 		break;
+

  	default:
  		err = -EOPNOTSUPP;
***************
*** 3733,3738 ****
--- 3815,3822 ----
  	int i;
  	u16 length;
  	int err;
+ 	static const char rid_fmt[] =
+ 		"%-15s (0x%04x): length=%d (%d bytes)\tvalue=";

  	/* Hum, in this case hardware register are probably not readable... */
  	if (! netif_device_present(dev))
***************
*** 3765,3771 ****
  		if (length == 0)
  			continue;

! 		buf += sprintf(buf, "%-15s (0x%04x): length=%d (%d bytes)\tvalue=", record_table[i].name,
  			       rid, length, (length-1)*2);
  		len = min( (int)max(minlen, ((int)length-1)*2), maxlen);

--- 3849,3855 ----
  		if (length == 0)
  			continue;

! 		buf += sprintf(buf, rid_fmt, record_table[i].name,
  			       rid, length, (length-1)*2);
  		len = min( (int)max(minlen, ((int)length-1)*2), maxlen);

***************
*** 3807,3812 ****
--- 3891,3920 ----
  			break;
  	}

+ 	while (SPY_NUMBER(priv)) {
+ 		u_char* p = priv->spy_address[0];
+
+ 		if ( (buf - page) > PROC_SAFE_SIZE )
+ 			break;
+ 		buf += sprintf(buf, rid_fmt, "SPYMAC0", 0,
+ 			HERMES_BYTES_TO_RECLEN(ETH_ALEN), ETH_ALEN);
+ 		for (i = ETH_ALEN; --i >= 0; ) {
+ 			buf += sprintf(buf, "%02x%c", *p++, (i ? ':' : '\n'));
+ 		}
+
+ 		if (shift_buffer(page, requested_offset, requested_len,
+ 				 &total, &slop, &buf))
+ 			break;
+ 		if ( (buf - page) > PROC_SAFE_SIZE )
+ 			break;
+
+ 		buf += sprintf(buf, rid_fmt, "SPYQUAL0", 0, 4, 6);
+ 		buf += sprintf(buf, "%04x-%04x-%04x\n", priv->spy_stat[0].qual,
+ 			priv->spy_stat[0].level, priv->spy_stat[0].noise);
+ 		shift_buffer(page, requested_offset, requested_len,
+ 				 &total, &slop, &buf);
+ 		break;
+ 	}
  	return calc_start_len(page, start, requested_offset, requested_len,
  			      total, buf);
  }
*** drivers/net/wireless/orinoco.h.orig18	Sat Feb  9 17:16:04 2002
--- drivers/net/wireless/orinoco.h	Thu Feb 14 22:26:49 2002
***************
*** 10,15 ****
--- 10,18 ----
  /* To enable debug messages */
  //#define ORINOCO_DEBUG		3

+ /* To enable auto-spy mode (JIMC) */
+ #define ORINOCO_AUTOSPY
+
  #if (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10)
  #error "orinoco driver requires Wireless extensions v10 or later."
  #endif /* (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10) */
***************
*** 83,89 ****
  	int			spy_number;
  	u_char			spy_address[IW_MAX_SPY][ETH_ALEN];
  	struct iw_quality	spy_stat[IW_MAX_SPY];
! #endif

  	/* /proc based debugging stuff */
  	struct proc_dir_entry *dir_dev;
--- 86,95 ----
  	int			spy_number;
  	u_char			spy_address[IW_MAX_SPY][ETH_ALEN];
  	struct iw_quality	spy_stat[IW_MAX_SPY];
! #ifdef ORINOCO_AUTOSPY
! 	int			autospy_when;	/*Time of last tx pkt, 0 = off*/
! #endif /*ORINOCO_AUTOSPY*/
! #endif /*WIRELESS_SPY*/

  	/* /proc based debugging stuff */
  	struct proc_dir_entry *dir_dev;
*** drivers/net/wireless/hermes.c.orig18	Sat Feb  9 17:16:04 2002
--- drivers/net/wireless/hermes.c	Sun Feb 17 11:15:53 2002
***************
*** 424,433 ****

  /* Read a Length-Type-Value record from the card.
   *
!  * If length is NULL, we ignore the length read from the card, and
!  * read the entire buffer regardless. This is useful because some of
!  * the configuration records appear to have incorrect lengths in
!  * practice.
   *
   * Callable from user or bh context.  */
  int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize,
--- 424,430 ----

  /* Read a Length-Type-Value record from the card.
   *
!  * If nonnull, *length gets the number of 2-octet units stored in *buf.
   *
   * Callable from user or bh context.  */
  int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize,
***************
*** 435,471 ****
  {
  	int err = 0;
  	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
! 	u16 rlength, rtype;
  	hermes_response_t resp;

  	if (bufsize % 2)
  		return -EINVAL;

! 	err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, &resp);
! 	if (err)
! 		goto out;
!
! 	err = hermes_bap_seek(hw, bap, rid, 0);
! 	if (err)
! 		goto out;
!
! 	rlength = hermes_read_reg(hw, dreg);
! 	rtype = hermes_read_reg(hw, dreg);
!
! 	if (length)
! 		*length = rlength;
!
  	if (rtype != rid)
! 		printk(KERN_WARNING "hermes_read_ltv(): rid  (0x%04x) does "
! 		       "not match type (0x%04x)\n", rid, rtype);
  	if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize)
  		printk(KERN_WARNING "hermes @ 0x%x: Truncating LTV record from %d to %d bytes. "
! 		       "(rid=0x%04x, len=0x%04x)\n", hw->iobase,
! 		       HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength);
!
! 	/* FIXME: we should read the min of the requested length and
!            the actual record length */
! 	hermes_read_words(hw, dreg, buf, bufsize / 2);

   out:
  	return err;
--- 432,476 ----
  {
  	int err = 0;
  	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
! 	u16 rlength, alength, rtype;
  	hermes_response_t resp;
+ 	int redo = 8; /*JIMC*/

  	if (bufsize % 2)
  		return -EINVAL;
+         /* JIMC hack: flush twice, it's a long way to the kitchen :-) For Dell
+ 	    Truemobile 1150 (Agere) Mini-PCI, firmware A00, vers. 6.16, when
+ 	    you send a ton of data to the card in Ad-Hoc (IBSS) mode at 2 Mb/s
+ 	    from a Linksys WPC11 (Intersil firmware 1.00), the Agere card
+ 	    invariably gets into a mode where you ask for RID 0xfc0a (for
+ 	    example) and you get 0xfc09.  Ask twice for the same RID and the
+ 	    problem vanishes. */
+ 	do {
+ 		err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, &resp);
+ 		if (err)
+ 			goto out;
+
+ 		err = hermes_bap_seek(hw, bap, rid, 0);
+ 		if (err)
+ 			goto out;
+
+ 		rlength = hermes_read_reg(hw, dreg);
+ 		alength = min(rlength, bufsize/2);
+ 		rtype = hermes_read_reg(hw, dreg);
+
+ 		if (length)
+ 			*length = alength;
+
+ 		hermes_read_words(hw, dreg, buf, alength);

! 	} while (rtype != rid && --redo >= 0); /*JIMC*/
  	if (rtype != rid)
! 		printk(KERN_WARNING "hermes_read_ltv(): asked for rid 0x%04x,"
! 		       " got 0x%04x\n", rid, rtype);
  	if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize)
  		printk(KERN_WARNING "hermes @ 0x%x: Truncating LTV record from %d to %d bytes. "
! 			"(rid=0x%04x, len=0x%04x)\n", hw->iobase,
! 			HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength);

   out:
  	return err;





More information about the wireless mailing list