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