Subject: Re: rate adaptation for BSD
To: David Young <dyoung@pobox.com>
From: Jason Thorpe <thorpej@wasabisystems.com>
List: tech-net
Date: 09/22/2003 23:01:02
--Apple-Mail-2-90317060
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	charset=US-ASCII;
	format=flowed


On Thursday, June 26, 2003, at 08:18  PM, David Young wrote:

> I have added rate adaptation for 802.11b APs and clients to wi.
> The performance of host APs will be especially improved. I have 
> attached
> patches in the hope of getting a some review and testing.

FWIW...

Here are the same patches, updated to -current.  These compile, but I 
have not yet booted the resulting kernel on my Soekris AP.  Tomorrow's 
task.

         -- Jason R. Thorpe <thorpej@wasabisystems.com>

--Apple-Mail-2-90317060
Content-Disposition: attachment;
	filename=linkadapt.patch
Content-Transfer-Encoding: 7bit
Content-Type: application/octet-stream;
	x-unix-mode=0644;
	name="linkadapt.patch"

Index: net/if_ieee80211.h
===================================================================
RCS file: /cvsroot/src/sys/net/if_ieee80211.h,v
retrieving revision 1.36
diff -c -r1.36 if_ieee80211.h
*** net/if_ieee80211.h	2003/07/06 20:54:24	1.36
--- net/if_ieee80211.h	2003/09/23 05:49:23
***************
*** 405,410 ****
--- 405,426 ----
  #define	IEEE80211_INACT_MAX	(300/IEEE80211_INACT_WAIT)
  
  /*
+  * Link adaptation.
+  */
+ 
+ /* Buckets for frames 0-128 bytes long, 129-1024, 1025-maximum. */
+ #define	IEEE80211_LADAPT_BUCKETS	3
+ #define	IEEE80211_LADAPT_BUCKET0	128
+ 
+ #define	IEEE80211_LADAPT_RETRY_LIMIT	3
+ #define	IEEE80211_LADAPT_THRESH_NEW	\
+     (ieee80211_ladapt_thresh_denom - ieee80211_ladapt_thresh_old)
+ #define	IEEE80211_LADAPT_DECAY_NEW	\
+     (ieee80211_ladapt_decay_denom - ieee80211_ladapt_decay_old)
+ #define	IEEE80211_LADAPT_AVGRSSI_NEW	\
+     (ieee80211_ladapt_avgrssi_denom - ieee80211_ladapt_avgrssi_old)
+ 
+ /*
   * Structure for IEEE 802.11 drivers.
   */
  
***************
*** 448,453 ****
--- 464,471 ----
  
  	/* hardware */
  	u_int8_t		ni_rssi;
+ 	/* exponential averave RSSI << 8 */
+ 	u_int16_t		ni_avg_rssi;
  	u_int32_t		ni_rstamp;
  
  	/* header */
***************
*** 478,486 ****
--- 496,520 ----
  	int			ni_fails;	/* failure count to associate */
  	int			ni_inact;	/* inactivity mark count */
  	int			ni_txrate;	/* index to ni_rates[] */
+ 	/* RSSI threshold for each Tx rate */
+ 	u_int16_t		ni_rate_thresh[IEEE80211_LADAPT_BUCKETS]
+ 					      [IEEE80211_RATE_SIZE];
+ 	u_char			ni_rate_used[IEEE80211_LADAPT_BUCKETS]
+ 					    [howmany(IEEE80211_RATE_SIZE,NBBY)];
+ 	u_char			ni_rate_fail[IEEE80211_LADAPT_BUCKETS]
+ 					    [howmany(IEEE80211_RATE_SIZE,NBBY)];
+ 
  	void			*ni_private;	/* driver private */
  };
  
+ /* Properties of a Tx packet, for link adaptation. */
+ struct ieee80211_txinfo {
+ 	u_int		i_len;		/* Tx packet length */
+ 	u_int		i_rateidx;	/* index into ni->ni_rates */
+ 	u_int8_t	i_dstaddr[IEEE80211_ADDR_LEN]; /* destination STA MAC */
+ 	u_int8_t	i_rssi;		/* destination STA avg RSS @ Tx time */
+ };
+ 
  /* ni_chan encoding for FH phy */
  #define	IEEE80211_FH_CHANMOD	80
  #define	IEEE80211_FH_CHAN(set,pat)	(((set)-1)*IEEE80211_FH_CHANMOD+(pat))
***************
*** 539,544 ****
--- 573,579 ----
  	int			ic_mgt_timer;	/* mgmt timeout */
  	int			ic_scan_timer;	/* scant wait */
  	int			ic_inact_timer;	/* inactivity timer wait */
+ 	int			ic_ladapt_timer;/* link adaptation debug timer*/
  	int			ic_des_esslen;
  	u_int8_t		ic_des_essid[IEEE80211_NWID_LEN];
  	int			ic_des_chan;	/* desired channel */
***************
*** 549,554 ****
--- 584,590 ----
  	u_int32_t		ic_iv;		/* initial vector for wep */
  	u_int32_t		ic_aid_bitmap[IEEE80211_MAX_AID / 32 + 1];
  	u_int16_t		ic_max_aid;
+ 	struct callout		ic_ladapt_ch;	/* decay of RSS thresholds */
  	struct ifmedia		ic_media;
  };
  #ifdef __NetBSD__
***************
*** 627,632 ****
--- 663,673 ----
  
  void	ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *, 
  			  struct mbuf *);
+ 
+ int	ieee80211_choose_rate(struct ieee80211com *, struct ieee80211_frame *,
+ 			      u_int);
+ void	ieee80211_lower_rate(struct ieee80211com *, struct ieee80211_txinfo *);
+ void	ieee80211_raise_rate(void *);
  
  int	ieee80211_media_change(struct ifnet *);
  void	ieee80211_media_status(struct ifnet *, struct ifmediareq *);
Index: net/if_ieee80211subr.c
===================================================================
RCS file: /cvsroot/src/sys/net/if_ieee80211subr.c,v
retrieving revision 1.43
diff -c -r1.43 if_ieee80211subr.c
*** net/if_ieee80211subr.c	2003/07/06 20:54:25	1.43
--- net/if_ieee80211subr.c	2003/09/23 05:49:28
***************
*** 82,90 ****
--- 82,113 ----
  
  #ifdef IEEE80211_DEBUG
  int ieee80211_debug = 0;
+ 
+ static struct timeval lastratechoice;	/* time of last rate choice msg */
+ static int curchoiceps = 0;		/* current rate-choice msgs/sec */
+ static int ieee80211_choicerate = 2;	/* rate-choice max msgs/sec */
+ 
+ static struct timeval lastrateadapt;	/* time of last rate adaptation msg */
+ static int curladaptps = 0;		/* current rate-adaptation msgs/sec */
+ static int ieee80211_adaptrate = 4;	/* rate-adaptation max msgs/sec */
+ 
+ #define	RCHOICE_PRINTF(__ic, X)						\
+ 	if ((__ic->ic_if.if_flags & IFF_DEBUG) &&			\
+ 	    ppsratecheck(&lastratechoice, &curchoiceps,			\
+ 			 ieee80211_choicerate))				\
+ 		printf X
+ 
+ #define	LADAPT_PRINTF(X)						\
+ 	if (ieee80211_debug > 0 &&					\
+ 	    ppsratecheck(&lastrateadapt, &curladaptps,			\
+ 			 ieee80211_adaptrate))				\
+ 		printf X
+ 
  #define	DPRINTF(X)	if (ieee80211_debug) printf X
  #define	DPRINTF2(X)	if (ieee80211_debug>1) printf X
  #else
+ #define	RCHOICE_PRINTF(__ic, X)
+ #define	LADAPT_PRINTF(X)
  #define	DPRINTF(X)
  #define	DPRINTF2(X)
  #endif
***************
*** 125,130 ****
--- 148,156 ----
  static void ieee80211_crc_init(void);
  static u_int32_t ieee80211_crc_update(u_int32_t, u_int8_t *, int);
  
+ static void ieee80211_node_raise_rate(struct ieee80211com *,
+ 				      struct ieee80211_node *);
+ 
  static const char *ieee80211_mgt_subtype_name[] = {
  	"assoc_req",	"assoc_resp",	"reassoc_req",	"reassoc_resp",
  	"probe_req",	"probe_resp",	"reserved#6",	"reserved#7",
***************
*** 132,137 ****
--- 158,173 ----
  	"deauth",	"reserved#13",	"reserved#14",	"reserved#15"
  };
  
+ /* RSS threshold decay. */
+ u_int ieee80211_ladapt_decay_denom = 8;
+ u_int ieee80211_ladapt_decay_old = 4;
+ /* RSS threshold update. */
+ u_int ieee80211_ladapt_thresh_denom = 8;
+ u_int ieee80211_ladapt_thresh_old = 4;
+ /* RSS average update. */
+ u_int ieee80211_ladapt_avgrssi_denom = 8;
+ u_int ieee80211_ladapt_avgrssi_old = 4;
+ 
  void
  ieee80211_ifattach(struct ifnet *ifp)
  {
***************
*** 177,182 ****
--- 213,221 ----
  	if (ic->ic_max_aid == 0)
  		ic->ic_max_aid = IEEE80211_MAX_AID;
  
+ 	/* rate threshold decay */
+ 	callout_init(&ic->ic_ladapt_ch);
+ 
  	/* initialize management frame handlers */
  	ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_RESP
  	    >> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_beacon;
***************
*** 271,276 ****
--- 310,316 ----
  		free(ic->ic_wep_ctx, M_DEVBUF);
  		ic->ic_wep_ctx = NULL;
  	}
+ 	callout_stop(&ic->ic_ladapt_ch);
  	ieee80211_free_allnodes(ic);
  	ifmedia_delete_instance(&ic->ic_media, IFM_INST_ANY);
  #if NBPFILTER > 0
***************
*** 289,295 ****
  	struct ether_header *eh;
  	void (*rh)(struct ieee80211com *, struct mbuf *, int, u_int);
  	struct mbuf *m1;
! 	int error, len;
  	u_int8_t dir, subtype;
  	u_int8_t *bssid;
  	u_int16_t rxseq;
--- 329,335 ----
  	struct ether_header *eh;
  	void (*rh)(struct ieee80211com *, struct mbuf *, int, u_int);
  	struct mbuf *m1;
! 	int error, len, last_avg;
  	u_int8_t dir, subtype;
  	u_int8_t *bssid;
  	u_int16_t rxseq;
***************
*** 350,355 ****
--- 390,403 ----
  			goto out;
  		}
  		ni->ni_rssi = rssi;
+ 		last_avg = ni->ni_avg_rssi;
+ 		ni->ni_avg_rssi =
+ 		    (ieee80211_ladapt_avgrssi_old * ni->ni_avg_rssi +
+ 		     IEEE80211_LADAPT_AVGRSSI_NEW * (ni->ni_rssi << 8)) /
+ 		    ieee80211_ladapt_avgrssi_denom;
+ 		LADAPT_PRINTF(("%s: src %s rssi %d avg %d -> %d\n",
+ 		    ic->ic_if.if_xname, ether_sprintf(wh->i_addr2),
+ 		    ni->ni_rssi, last_avg, ni->ni_avg_rssi));
  		ni->ni_rstamp = rstamp;
  		rxseq = ni->ni_rxseq;
  		ni->ni_rxseq =
***************
*** 2247,2252 ****
--- 2295,2302 ----
  		IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE);
  		return;
  	}
+ 	/* use highest rate (TBD rate selection) */
+ 	ni->ni_txrate = ni->ni_nrate - 1;
  	ni->ni_rssi = rssi;
  	ni->ni_rstamp = rstamp;
  	ni->ni_intval = bintval;
***************
*** 2502,2508 ****
  	(*ifp->if_start)(ifp);
  }
  
- 
  int
  ieee80211_new_state(struct ifnet *ifp, enum ieee80211_state nstate, int mgt)
  {
--- 2552,2557 ----
***************
*** 2530,2535 ****
--- 2579,2585 ----
  	ic->ic_state = nstate;
  	switch (nstate) {
  	case IEEE80211_S_INIT:
+ 		callout_stop(&ic->ic_ladapt_ch);
  		switch (ostate) {
  		case IEEE80211_S_INIT:
  			break;
***************
*** 2585,2590 ****
--- 2635,2641 ----
  		}
  		break;
  	case IEEE80211_S_SCAN:
+ 		ieee80211_raise_rate((void*)ic);
  		ic->ic_flags &= ~IEEE80211_F_SIBSS;
  		ni = &ic->ic_bss;
  		/* initialize bss for probe request */
***************
*** 2687,2692 ****
--- 2738,2744 ----
  		}
  		break;
  	case IEEE80211_S_RUN:
+ 		ieee80211_raise_rate((void*)ic);
  		switch (ostate) {
  		case IEEE80211_S_INIT:
  		case IEEE80211_S_AUTH:
***************
*** 3706,3709 ****
--- 3758,3953 ----
  		break;
  	}
  	return error;
+ }
+ 
+ /*
+  * If we transmitted an l-length packet at rate n in this decay period,
+  * then the RSS threshold T[l, n+1] decays, approaching T[l, n].
+  */
+ void
+ ieee80211_raise_rate(void *xic)
+ {
+ 	struct ieee80211com *ic = (struct ieee80211com *)xic;
+ 	struct ieee80211_node *ni;
+ 	int s;
+ 
+ 	s = splnet();
+ 
+ 	++ic->ic_ladapt_timer;
+ 
+ 	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ 		TAILQ_FOREACH(ni, &ic->ic_node, ni_list)
+ 			ieee80211_node_raise_rate(ic, ni);
+ 	} else {
+ 		ieee80211_node_raise_rate(ic, &ic->ic_bss);
+ 	}
+ 
+ 	callout_reset(&ic->ic_ladapt_ch, hz, ieee80211_raise_rate, xic);
+ 
+ 	splx(s);
+ }
+ 
+ static void
+ ieee80211_node_raise_rate(struct ieee80211com *ic, struct ieee80211_node *ni)
+ {
+ 	u_int16_t (*thrs)[IEEE80211_RATE_SIZE];
+ 	int i, j, rate;
+ 
+ 	for (i = 0; i < IEEE80211_LADAPT_BUCKETS; i++) {
+ 		thrs = &ni->ni_rate_thresh[i];
+ 		for (j = ni->ni_nrate - 1; --j >= 0; ) {
+ 			if (isclr(ni->ni_rate_used[i], j) ||
+ 			    isset(ni->ni_rate_fail[i], j) ||
+ 			    (*thrs)[j + 1] <= (*thrs)[j])
+ 				continue;
+ 
+ 			rate = (ni->ni_rates[j + 1] & IEEE80211_RATE_VAL);
+ 
+ 			LADAPT_PRINTF(("%s: threshold[%d, %d.%d] decay %d ",
+ 			    ic->ic_if.if_xname,
+ 			    IEEE80211_LADAPT_BUCKET0 << (3 * i),
+ 			    rate / 2, rate * 5 % 10, (*thrs)[j + 1]));
+ 
+ 			(*thrs)[j + 1] =
+ 			    (ieee80211_ladapt_decay_old * (*thrs)[j + 1] +
+ 			     IEEE80211_LADAPT_DECAY_NEW * (*thrs)[j]) /
+ 			    ieee80211_ladapt_decay_denom;
+ 			
+ 			LADAPT_PRINTF(("-> %d\n", (*thrs)[j + 1]));
+ 		}
+ 	}
+ 	memset(ni->ni_rate_used, 0, sizeof(ni->ni_rate_used));
+ 	memset(ni->ni_rate_fail, 0, sizeof(ni->ni_rate_fail));
+ 
+ #ifdef IEEE80211_DEBUG
+ 	if ((ieee80211_debug > 0) && (ic->ic_ladapt_timer % 5 == 0)) {
+ 		printf("%s: dst %s thresholds\n", ic->ic_if.if_xname,
+ 		    ether_sprintf(ni->ni_macaddr));
+ 		for (i = 0; i < IEEE80211_LADAPT_BUCKETS; i++) {
+ 			printf("%d-byte", IEEE80211_LADAPT_BUCKET0 << (3 * i));
+ 			for (j = 0; j < ni->ni_nrate; j++) {
+ 				rate = (ni->ni_rates[j] & IEEE80211_RATE_VAL);
+ 				printf(", T[%d.%d] = %d", rate / 2,
+ 				    rate * 5 % 10, ni->ni_rate_thresh[i][j]);
+ 			}
+ 			printf("\n");
+ 		}
+ 	}
+ #endif /* IEEE80211_DEBUG */
+ }
+ 
+ /*
+  * Adapt the data rate to suit the conditions.  When a transmitted
+  * packet is dropped after IEEE80211_LADAPT_RETRY_LIMIT retransmissions,
+  * raise the RSS threshold for transmitting packets of similar length at
+  * the same data rate.
+  */
+ void
+ ieee80211_lower_rate(struct ieee80211com *ic, struct ieee80211_txinfo *txi)
+ {
+ 	struct ieee80211_node *ni;
+ 	u_int16_t last_thr;
+ 	u_int i, thridx, top;
+ 	int s;
+ 
+ 	s = splnet();
+ 
+ 	if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
+ 		ni = &ic->ic_bss;
+ 	} else if ((ni = ieee80211_find_node(ic, txi->i_dstaddr)) == NULL) {
+ 		DPRINTF(("ieee80211_lower_rate: missing node %s\n",
+ 		    ether_sprintf(txi->i_dstaddr)));
+ 		splx(s);
+ 		return;
+ 	}
+ 
+ 	if (txi->i_rateidx >= ni->ni_nrate) {
+ 		DPRINTF(("ieee80211_lower_rate: "
+ 		    "%s rate #%d > #%d out of bounds\n",
+ 		    ether_sprintf(txi->i_dstaddr), txi->i_rateidx,
+ 		    ni->ni_nrate - 1));
+ 		splx(s);
+ 		return;
+ 	}
+ 
+ 	for (i = 0, top = IEEE80211_LADAPT_BUCKET0;
+ 	     i < IEEE80211_LADAPT_BUCKETS; i++, top <<= 3) {
+ 		thridx = i;
+ 		if (txi->i_len <= top)
+ 			break;
+ 	}
+ 
+ 	last_thr = ni->ni_rate_thresh[thridx][txi->i_rateidx];
+ 	ni->ni_rate_thresh[thridx][txi->i_rateidx] =
+ 	    (ieee80211_ladapt_thresh_old * last_thr +
+ 	     IEEE80211_LADAPT_THRESH_NEW * (txi->i_rssi << 8)) /
+ 	    ieee80211_ladapt_thresh_denom;
+ 
+ 	setbit(ni->ni_rate_fail[thridx], txi->i_rateidx);
+ 
+ 	LADAPT_PRINTF(("%s: dst %s rssi %d threshold[%d, %d.%d] %d -> %d\n",
+ 	    ic->ic_if.if_xname, ether_sprintf(txi->i_dstaddr),
+ 	    txi->i_rssi, txi->i_len,
+ 	    (ni->ni_rates[txi->i_rateidx] & IEEE80211_RATE_VAL) / 2,
+ 	    (ni->ni_rates[txi->i_rateidx] & IEEE80211_RATE_VAL) * 5 % 10,
+ 	    last_thr, ni->ni_rate_thresh[thridx][txi->i_rateidx]));
+ 
+ 	splx(s);
+ }
+ 
+ /*
+  * Choose a data rate for a packet len bytes long that suits the wireless
+  * conditions.
+  *
+  * TBD Adapt fragmentation threshold.
+  */
+ int
+ ieee80211_choose_rate(struct ieee80211com *ic, struct ieee80211_frame *wh,
+     u_int len)
+ {
+ 	struct ieee80211_node *ni;
+ 	u_int16_t (*thrs)[IEEE80211_RATE_SIZE];
+ 	int flags = 0, i, rateidx = 0, thridx, s, top;
+ 
+ 	s = splnet();
+ 
+ 	if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ 		flags |= IEEE80211_RATE_BASIC;
+ 		ni = &ic->ic_bss;	/* TBD choose STA w/ lowest rate */
+ 	} else if (ic->ic_opmode == IEEE80211_M_STA)
+ 		ni = &ic->ic_bss;
+ 	else if ((ni = ieee80211_find_node(ic, wh->i_addr1)) == NULL)
+ 		goto out;
+ 
+ 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
+ 		flags |= IEEE80211_RATE_BASIC;
+ 
+ 	for (i = 0, top = IEEE80211_LADAPT_BUCKET0;
+ 	     i < IEEE80211_LADAPT_BUCKETS; i++, top <<= 3) {
+ 		thridx = i;
+ 		if (len <= top)
+ 			break;
+ 	}
+ 
+ 	thrs = &ni->ni_rate_thresh[thridx];
+ 
+ 	/* Choose the highest rate with all the flags set. */
+ 	for (i = ni->ni_nrate; --i >= 0; ) {
+ 		rateidx = i;
+ 		if ((ni->ni_rates[i] & flags) != flags)
+ 			continue;
+ 		if (i == ic->ic_fixed_rate || (*thrs)[i] < ni->ni_avg_rssi)
+ 			break;
+ 	}
+ 
+ 	setbit(ni->ni_rate_used[thridx], rateidx);
+ 
+ 	RCHOICE_PRINTF(ic, ("%s: dst %s threshold[%d, %d.%d] %d < %d\n",
+ 	    ic->ic_if.if_xname, ether_sprintf(wh->i_addr1), len,
+ 	    (ni->ni_rates[rateidx] & IEEE80211_RATE_VAL) / 2,
+ 	    (ni->ni_rates[rateidx] & IEEE80211_RATE_VAL) * 5 % 10,
+ 	    (*thrs)[rateidx], ni->ni_avg_rssi));
+  out:
+ 	splx(s);
+ 	return (rateidx);
  }
Index: dev/ic/wi.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/wi.c,v
retrieving revision 1.132
diff -c -r1.132 wi.c
*** dev/ic/wi.c	2003/07/06 20:01:17	1.132
--- dev/ic/wi.c	2003/09/23 05:49:31
***************
*** 121,127 ****
  
  static int  wi_get_cfg(struct ifnet *, u_long, caddr_t);
  static int  wi_set_cfg(struct ifnet *, u_long, caddr_t);
! static int  wi_write_txrate(struct wi_softc *);
  static int  wi_write_wep(struct wi_softc *);
  static int  wi_write_multi(struct wi_softc *);
  static int  wi_alloc_fid(struct wi_softc *, int, int *);
--- 121,128 ----
  
  static int  wi_get_cfg(struct ifnet *, u_long, caddr_t);
  static int  wi_set_cfg(struct ifnet *, u_long, caddr_t);
! static int  wi_cfg_txrate(struct wi_softc *);
! static int  wi_write_txrate(struct wi_softc *, int);
  static int  wi_write_wep(struct wi_softc *);
  static int  wi_write_multi(struct wi_softc *);
  static int  wi_alloc_fid(struct wi_softc *, int, int *);
***************
*** 604,610 ****
  		wi_write_val(sc, WI_RID_ROAMING_MODE, sc->sc_roaming_mode);
  	if (sc->sc_flags & WI_FLAGS_HAS_MOR)
  		wi_write_val(sc, WI_RID_MICROWAVE_OVEN, sc->sc_microwave_oven);
! 	wi_write_txrate(sc);
  	wi_write_ssid(sc, WI_RID_NODENAME, sc->sc_nodename, sc->sc_nodelen);
  
  	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
--- 605,611 ----
  		wi_write_val(sc, WI_RID_ROAMING_MODE, sc->sc_roaming_mode);
  	if (sc->sc_flags & WI_FLAGS_HAS_MOR)
  		wi_write_val(sc, WI_RID_MICROWAVE_OVEN, sc->sc_microwave_oven);
! 	wi_cfg_txrate(sc);
  	wi_write_ssid(sc, WI_RID_NODENAME, sc->sc_nodename, sc->sc_nodelen);
  
  	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
***************
*** 615,620 ****
--- 616,627 ----
  		wi_write_val(sc, WI_RID_DTIM_PERIOD, 1);
  	}
  
+ 	if (sc->sc_firmware_type == WI_INTERSIL &&
+ 	    (ic->ic_opmode == IEEE80211_M_STA ||
+ 	     ic->ic_opmode == IEEE80211_M_HOSTAP))
+ 		wi_write_val(sc, WI_RID_ALT_RETRY_COUNT,
+ 		    IEEE80211_LADAPT_RETRY_LIMIT);
+ 
  	/*
  	 * Initialize promisc mode.
  	 *	Being in the Host-AP mode causes a great
***************
*** 654,660 ****
  			sc->sc_txd[i].d_len = 0;
  		}
  	}
! 	sc->sc_txcur = sc->sc_txnext = 0;
  
  	/* Enable desired port */
  	wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0);
--- 661,668 ----
  			sc->sc_txd[i].d_len = 0;
  		}
  	}
! 	memset(sc->sc_txinfo, 0, sizeof(sc->sc_txinfo));
! 	 sc->sc_txcur = sc->sc_txnext = sc->sc_txinfonext = 0;
  
  	/* Enable desired port */
  	wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0);
***************
*** 842,847 ****
--- 850,891 ----
  			}
  			frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
  		}
+ 		if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ 		    ic->ic_opmode == IEEE80211_M_STA) {
+ 			struct ieee80211_txinfo *txi;
+ 
+ 			if (ic->ic_opmode == IEEE80211_M_STA) {
+ 				ni = &ic->ic_bss;
+ 				ni->ni_txrate = ieee80211_choose_rate(ic, wh,
+ 				    m0->m_pkthdr.len);
+ 				wi_write_txrate(sc,
+ 				    ni->ni_rates[ni->ni_txrate]);
+ 			} else {
+ 				ni = ieee80211_find_node(ic, wh->i_addr1);
+ 				if (ni == NULL) {
+ 					m_freem(m0);
+ 					ifp->if_oerrors++;
+ 					continue;
+ 				}
+ 				ni->ni_txrate = ieee80211_choose_rate(ic, wh,
+ 				    m0->m_pkthdr.len);
+ 				frmhdr.wi_tx_rate =
+ 				    (ni->ni_rates[ni->ni_txrate] &
+ 				     IEEE80211_RATE_VAL) * 5;
+ 			}
+ 
+ 			frmhdr.wi_tx_idx = sc->sc_txinfonext;
+ 			sc->sc_txinfonext = (sc->sc_txinfonext + 1) % WI_NTXBUF;
+ 
+ 			txi = &sc->sc_txinfo[frmhdr.wi_tx_idx];
+ 
+ 			txi->i_rateidx = ni->ni_txrate;
+ 			txi->i_rssi = ni->ni_rssi;
+ 			txi->i_len = m0->m_pkthdr.len;
+ 
+ 			IEEE80211_ADDR_COPY(txi->i_dstaddr, wh->i_addr1);
+ 			frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_ALTRTRY);
+ 		}
  		m_copydata(m0, 0, sizeof(struct ieee80211_frame),
  		    (caddr_t)&frmhdr.wi_whdr);
  		m_adj(m0, sizeof(struct ieee80211_frame));
***************
*** 1294,1335 ****
  	struct ifnet *ifp = &ic->ic_if;
  	struct wi_frame frmhdr;
  	int fid;
  
  	fid = CSR_READ_2(sc, WI_TX_CMP_FID);
  	/* Read in the frame header */
! 	if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) == 0) {
! 		u_int16_t status = le16toh(frmhdr.wi_status);
  
! 		/*
! 		 * Spontaneous station disconnects appear as xmit
! 		 * errors.  Don't announce them and/or count them
! 		 * as an output error.
! 		 */
! 		if ((status & WI_TXSTAT_DISCONNECT) == 0) {
! 			if (ppsratecheck(&lasttxerror, &curtxeps, wi_txerate)) {
! 				curtxeps = 0;
! 				printf("%s: tx failed", sc->sc_dev.dv_xname);
! 				if (status & WI_TXSTAT_RET_ERR)
! 					printf(", retry limit exceeded");
! 				if (status & WI_TXSTAT_AGED_ERR)
! 					printf(", max transmit lifetime exceeded");
! 				if (status & WI_TXSTAT_DISCONNECT)
! 					printf(", port disconnected");
! 				if (status & WI_TXSTAT_FORM_ERR)
! 					printf(", invalid format (data len %u src %s)",
! 						le16toh(frmhdr.wi_dat_len),
! 						ether_sprintf(frmhdr.wi_ehdr.ether_shost));
! 				if (status & ~0xf)
! 					printf(", status=0x%x", status);
! 				printf("\n");
! 			}
! 			ifp->if_oerrors++;
! 		} else {
! 			DPRINTF(("port disconnected\n"));
! 			ifp->if_collisions++;	/* XXX */
  		}
! 	} else
! 		DPRINTF(("wi_tx_ex_intr: read fid %x failed\n", fid));
  	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC);
  }
  
--- 1338,1391 ----
  	struct ifnet *ifp = &ic->ic_if;
  	struct wi_frame frmhdr;
  	int fid;
+ 	u_int16_t status;
  
  	fid = CSR_READ_2(sc, WI_TX_CMP_FID);
  	/* Read in the frame header */
! 	if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) != 0) {
! 		DPRINTF(("wi_tx_ex_intr: read fid %x failed\n", fid));
! 		goto out;
! 	}
  
! 	status = le16toh(frmhdr.wi_status);
! 
! 	/*
! 	 * Spontaneous station disconnects appear as xmit
! 	 * errors.  Don't announce them and/or count them
! 	 * as an output error.
! 	 */
! 	if ((status & WI_TXSTAT_DISCONNECT) != 0) {
! 		DPRINTF(("port disconnected\n"));
! 		ifp->if_collisions++;	/* XXX */
! 		goto out;
! 	}
! 	if (ppsratecheck(&lasttxerror, &curtxeps, wi_txerate)) {
! 		curtxeps = 0;
! 		printf("%s: tx failed", sc->sc_dev.dv_xname);
! 		if (status & WI_TXSTAT_RET_ERR)
! 			printf(", retry limit exceeded");
! 		if (status & WI_TXSTAT_AGED_ERR)
! 			printf(", max transmit lifetime exceeded");
! 		if (status & WI_TXSTAT_DISCONNECT)
! 			printf(", port disconnected");
! 		if (status & WI_TXSTAT_FORM_ERR)
! 			printf(", invalid format (data len %u src %s)",
! 			       le16toh(frmhdr.wi_dat_len),
! 			       ether_sprintf(frmhdr.wi_ehdr.ether_shost));
! 		if (status & ~0xf)
! 			printf(", status=0x%x", status);
! 		printf("\n");
! 	}
! 	ifp->if_oerrors++;
! 	if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
! 	     ic->ic_opmode == IEEE80211_M_STA) &&
! 	    (status & WI_TXSTAT_RET_ERR) != 0) {
! 		if (frmhdr.wi_tx_idx >= WI_NTXBUF) {
! 			goto out;
  		}
! 		ieee80211_lower_rate(ic, &sc->sc_txinfo[frmhdr.wi_tx_idx]);
! 	}
!  out:
  	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC);
  }
  
***************
*** 1824,1830 ****
  			ic->ic_fixed_rate = i;
  		}
  		if (sc->sc_enabled)
! 			error = wi_write_txrate(sc);
  		break;
  
  	case WI_RID_SCAN_APS:
--- 1880,1886 ----
  			ic->ic_fixed_rate = i;
  		}
  		if (sc->sc_enabled)
! 			error = wi_cfg_txrate(sc);
  		break;
  
  	case WI_RID_SCAN_APS:
***************
*** 1864,1887 ****
  }
  
  static int
! wi_write_txrate(struct wi_softc *sc)
  {
  	struct ieee80211com *ic = &sc->sc_ic;
! 	int i;
! 	u_int16_t rate;
  
  	if (ic->ic_fixed_rate < 0)
  		rate = 0;	/* auto */
  	else
! 		rate = (ic->ic_sup_rates[ic->ic_fixed_rate] &
! 		    IEEE80211_RATE_VAL) / 2;
  
  	/* rate: 0, 1, 2, 5, 11 */
  
  	switch (sc->sc_firmware_type) {
  	case WI_LUCENT:
! 		if (rate == 0)
! 			rate = 3;	/* auto */
  		break;
  	default:
  		/* Choose a bit according to this table.
--- 1920,1969 ----
  }
  
  static int
! wi_cfg_txrate(struct wi_softc *sc)
  {
  	struct ieee80211com *ic = &sc->sc_ic;
! 	int rate;
  
+ 	sc->sc_tx_rate = 0;	/* force write to RID */
+ 
  	if (ic->ic_fixed_rate < 0)
  		rate = 0;	/* auto */
  	else
! 		 rate = ic->ic_sup_rates[ic->ic_fixed_rate];
! 
! 	return wi_write_txrate(sc, rate);
! }
! 
! /*
!  * Rate is 0 for hardware auto-select, otherwise rate is
!  * 2, 4, 11, or 22 (units of 500Kbps).
!  */
! static int
! wi_write_txrate(struct wi_softc *sc, int rate)
! {
! 	u_int16_t hwrate;
! 	int i;
! 
! 	rate = (rate & IEEE80211_RATE_VAL) / 2;
  
  	/* rate: 0, 1, 2, 5, 11 */
  
  	switch (sc->sc_firmware_type) {
  	case WI_LUCENT:
! 		switch (rate) {
! 		case 0:
! 			hwrate = 3;	/* auto */
! 			break;
! 		case 5:
! 			hwrate = 4;
! 			break;
! 		case 11:
! 			hwrate = 5;
! 			break;
! 		default:
! 			hwrate = rate;
! 		}
  		break;
  	default:
  		/* Choose a bit according to this table.
***************
*** 1898,1909 ****
  				break;
  		}
  		if (i == 0)
! 			rate = 0xf;	/* auto */
  		else
! 			rate = i;
  		break;
  	}
! 	return wi_write_val(sc, WI_RID_TX_RATE, rate);
  }
  
  static int
--- 1980,1997 ----
  				break;
  		}
  		if (i == 0)
! 			hwrate = 0xf;	/* auto */
  		else
! 			hwrate = i;
  		break;
  	}
! 
! 	if (sc->sc_tx_rate == hwrate)
! 		return 0;
! 
! 	sc->sc_tx_rate = hwrate;
! 
! 	return wi_write_val(sc, WI_RID_TX_RATE, sc->sc_tx_rate);
  }
  
  static int
***************
*** 2308,2316 ****
--- 2396,2406 ----
  				ni->ni_esslen = IEEE80211_NWID_LEN;	/*XXX*/
  			memcpy(ni->ni_essid, ssid.wi_ssid, ni->ni_esslen);
  		}
+ 		ieee80211_raise_rate((void*)ic);
  		break;
  
  	case IEEE80211_S_SCAN:
+ 		ieee80211_raise_rate((void*)ic);
  	case IEEE80211_S_AUTH:
  	case IEEE80211_S_ASSOC:
  		break;
Index: dev/ic/wi_ieee.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/wi_ieee.h,v
retrieving revision 1.20
diff -c -r1.20 wi_ieee.h
*** dev/ic/wi_ieee.h	2003/04/08 04:31:25	1.20
--- dev/ic/wi_ieee.h	2003/09/23 05:49:31
***************
*** 236,241 ****
--- 236,242 ----
  #define WI_RID_WEP_MAPTABLE	0xFC29
  #define WI_RID_CNFAUTHMODE	0xFC2A
  #define WI_RID_ROAMING_MODE	0xFC2D
+ #define	WI_RID_ALT_RETRY_COUNT	0xFC32 /* retry count if WI_TXCNTL_ALTRTRY */
  #define WI_RID_OWN_BEACON_INT	0xFC33 /* beacon xmit time for BSS creation */
  #define WI_RID_SET_TIM		0xFC40
  #define WI_RID_DBM_ADJUST	0xFC46 /* RSSI - WI_RID_DBM_ADJUST ~ dBm */
Index: dev/ic/wireg.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/wireg.h,v
retrieving revision 1.45
diff -c -r1.45 wireg.h
*** dev/ic/wireg.h	2003/05/13 08:35:58	1.45
--- dev/ic/wireg.h	2003/09/23 05:49:32
***************
*** 544,549 ****
--- 544,555 ----
  	struct ether_header	wi_ehdr;	/* 0x2e */
  } __attribute__((__packed__));
  
+ /* Software support fields are returned untouched by TxOK, TxExc events. */
+ #define	wi_tx_swsup0		wi_rx_silence
+ #define	wi_tx_swsup1		wi_rx_signal
+ #define	wi_tx_swsup2		wi_rx_rate
+ #define	wi_tx_idx		wi_rx_flow
+ 
  /* Tx Status Field */
  #define	WI_TXSTAT_RET_ERR	0x0001
  #define	WI_TXSTAT_AGED_ERR	0x0002
Index: dev/ic/wivar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/wivar.h,v
retrieving revision 1.36
diff -c -r1.36 wivar.h
*** dev/ic/wivar.h	2003/07/06 20:01:18	1.36
--- dev/ic/wivar.h	2003/09/23 05:49:32
***************
*** 90,95 ****
--- 90,97 ----
  	}			sc_txd[WI_NTXBUF];
  	int			sc_txnext;
  	int			sc_txcur;
+ 	struct ieee80211_txinfo	sc_txinfo[WI_NTXBUF];
+ 	int			sc_txinfonext;
  	int			sc_tx_timer;
  	int			sc_scan_timer;
  	int			sc_syn_timer;

--Apple-Mail-2-90317060--