Only in clients: .depfiles diff -ur ../temp/pcmcia-cs-3.0.8/clients/wavelan_cs.c clients/wavelan_cs.c --- ../temp/pcmcia-cs-3.0.8/clients/wavelan_cs.c Thu Dec 24 15:33:43 1998 +++ clients/wavelan_cs.c Wed Jan 27 14:45:56 1999 @@ -4,8 +4,27 @@ * Jean II - HPLB '96 * * Reorganisation and extension of the driver. - * Original copyrigth follow. See wavelan_cs.h for details. + * Original copyright follows. See wavelan_cs.h for details. + * + * Joseph O'Sullivan & John Langford (josullvn@cs.cmu.edu & jcl@cs.cmu.edu) + * feb 25 '98 made changes to bring the i82593 control/int handling + * in line with offical specs... + * Design Specification for Wavelan PCMCIA HOST Adapter + * Engineering Document 407-0024222 RevB + * Interface Specification for Wavelan Host Adapter + * TYPE IBM AT + * Engineering Document 407-0023326 RevA + * PSA Contents Definition + * Engineering Document 407-0030558 RevC + * CIS for Wavelan PCMCIA 915 + * Engineering Document 407-0024222 RevB + * rvb@cs provided the spec, and braam@cs a laptop. + * April 2 '98 Ready for release */ +/* Leave this defined for environments with lots of wavestations + * Mar 98: josullvn@cs.cmu.edu + */ +#define WAVELAN_HIGH_COVERAGE /* * This code is derived from Anthony D. Joseph's code and all the changes here @@ -518,6 +537,317 @@ } #endif /* WIRELESS_EXT */ +/******************* WaveLAN Roaming routines... ********************/ +#ifdef WAVELAN_ROAMING + +unsigned char WAVELAN_BEACON_ADDRESS[]= /* WavePoint Beacon Address... */ + {0x09,0x00,0x0e,0x20,0x03,0x00}; + +void +wv_roam_init(struct device *dev) +{ + net_local *lp= (net_local *)dev->priv; + + lp->wavepoint_table.head=NULL; /* Initialise WavePoint table */ + lp->wavepoint_table.num_wavepoints=0; + lp->wavepoint_table.locked=0; + lp->curr_point=NULL; /* No default WavePoint */ + lp->cell_search=0; + + lp->cell_timer.data=(int)lp; /* Start cell expiry timer */ + lp->cell_timer.function=wl_cell_expiry; + lp->cell_timer.expires=jiffies+CELL_TIMEOUT; + add_timer(&lp->cell_timer); + + wv_nwid_filter(NWID_PROMISC,lp) ; /* Enter NWID promiscuous mode */ + /* to build up a good WavePoint */ + /* table... */ + printk(KERN_DEBUG "WaveLAN: Roaming enabled on device %s\n",dev->name); +} + +void +wv_roam_cleanup(struct device *dev) +{ + wavepoint_history *ptr,*old_ptr; + net_local *lp= (net_local *)dev->priv; + + printk(KERN_DEBUG "WaveLAN: Roaming Disabled on device %s\n",dev->name); + + del_timer(&lp->cell_timer); /* Remove cell expiry timer */ + ptr=lp->wavepoint_table.head; /* Clear device's WavePoint table */ + while(ptr!=NULL){ + old_ptr=ptr; + ptr=ptr->next; + wl_del_wavepoint(old_ptr,lp); + } +} + +/* Enable/Disable NWID promiscuous mode on a given device */ +void wv_nwid_filter(unsigned char mode, net_local *lp) +{ + mm_t m; + unsigned long x; + +#ifdef WAVELAN_ROAMING_DEBUG + printk(KERN_DEBUG "WaveLAN: NWID promisc %s, device %s\n",(mode==NWID_PROMISC) ? "on" : "off", lp->dev->name); +#endif + + /* Disable interrupts & save flags */ + x = wv_splhi(); + + m.w.mmw_loopt_sel = (mode==NWID_PROMISC) ? MMW_LOOPT_SEL_DIS_NWID : 0x00; + mmc_write(lp->dev->base_addr, (char *)&m.w.mmw_loopt_sel - (char *)&m, (unsigned char *)&m.w.mmw_loopt_sel, 1); + + /* ReEnable interrupts & restore flags */ + wv_splx(x); + + if(mode==NWID_PROMISC) + lp->cell_search=1; + else + lp->cell_search=0; +} + +/* Find a record in the WavePoint table matching a given NWID */ +wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp) +{ + wavepoint_history *ptr=lp->wavepoint_table.head; + + while(ptr!=NULL){ + if(ptr->nwid==nwid) + return ptr; + ptr=ptr->next; + } + return NULL; +} + +/* Create a new wavepoint table entry */ +wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local* lp) +{ + wavepoint_history *new_wavepoint; +#ifdef WAVELAN_HIGH_COVERAGE + /* This prints out far too much information in a busy environment */ +#if WAVELAN_ROAMING_DEBUG>1 + printk(KERN_DEBUG "WaveLAN: New Wavepoint, NWID:%.4X\n",nwid); +#endif +#else +#ifdef WAVELAN_ROAMING_DEBUG + printk(KERN_DEBUG "WaveLAN: New Wavepoint, NWID:%.4X\n",nwid); +#endif +#endif + if(lp->wavepoint_table.num_wavepoints==MAX_WAVEPOINTS) + return NULL; + + new_wavepoint=(wavepoint_history *) kmalloc(sizeof(wavepoint_history),GFP_ATOMIC); + if(new_wavepoint==NULL) + return NULL; + + new_wavepoint->nwid=nwid; /* New WavePoints NWID */ + new_wavepoint->average_fast=0; /* Running Averages..*/ + new_wavepoint->average_slow=0; + new_wavepoint->qualptr=0; /* Start of ringbuffer */ + new_wavepoint->last_seq=seq-1; /* Last sequence no.seen */ + memset(new_wavepoint->sigqual,0,WAVEPOINT_HISTORY); /* Empty ringbuffer */ + + new_wavepoint->next=lp->wavepoint_table.head; /* Add to wavepoint table */ + new_wavepoint->prev=NULL; + if(lp->wavepoint_table.head!=NULL) + lp->wavepoint_table.head->prev=new_wavepoint; + lp->wavepoint_table.head=new_wavepoint; + + lp->wavepoint_table.num_wavepoints++; /* no. of visible wavepoints */ + + return new_wavepoint; +} + +/* Remove a wavepoint entry from WavePoint table */ +void +wl_del_wavepoint(wavepoint_history *wavepoint, struct net_local *lp) +{ + if(wavepoint==NULL) + return; + + if(lp->curr_point==wavepoint) + lp->curr_point=NULL; + + if(wavepoint->prev!=NULL) + wavepoint->prev->next=wavepoint->next; + + if(wavepoint->next!=NULL) + wavepoint->next->prev=wavepoint->prev; + + if(lp->wavepoint_table.head==wavepoint) + lp->wavepoint_table.head=wavepoint->next; + + lp->wavepoint_table.num_wavepoints--; + kfree(wavepoint); +} + +/* Timer callback function - checks WavePoint table for stale entries */ +void +wl_cell_expiry(unsigned long data) +{ + net_local *lp=(net_local *)data; + wavepoint_history *wavepoint=lp->wavepoint_table.head,*old_point; +#if WAVELAN_ROAMING_DEBUG > 1 + printk(KERN_DEBUG "WaveLAN: Wavepoint timeout, dev %s\n",lp->dev->name); +#endif + if(lp->wavepoint_table.locked){ +#if WAVELAN_ROAMING_DEBUG > 1 + printk(KERN_DEBUG "WaveLAN: Wavepoint table locked...\n"); +#endif + lp->cell_timer.expires=jiffies+1; /* If table in use, come back later */ + add_timer(&lp->cell_timer); + return; + } + + while(wavepoint!=NULL){ + if(wavepoint->last_seen < jiffies-CELL_TIMEOUT){ +#ifdef WAVELAN_HIGH_COVERAGE +#if WAVELAN_ROAMING_DEBUG>1 + printk(KERN_DEBUG "WaveLAN: Bye bye %.4X\n",wavepoint->nwid); +#endif +#else +#ifdef WAVELAN_ROAMING_DEBUG + printk(KERN_DEBUG "WaveLAN: New Wavepoint, NWID:%.4X\n",nwid); +#endif +#endif + old_point=wavepoint; + wavepoint=wavepoint->next; + wl_del_wavepoint(old_point,lp); + }else{ + wavepoint=wavepoint->next; + } + } + lp->cell_timer.expires=jiffies+CELL_TIMEOUT; + add_timer(&lp->cell_timer); +} + +/* Update SNR history of a wavepoint */ +void +wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq) +{ + int i=0,num_missed=0,ptr=0; + int average_fast=0,average_slow=0; + + num_missed=(seq-wavepoint->last_seq)%WAVEPOINT_HISTORY; /* Have we missed any beacons? */ + if(num_missed){ + for(i=0;isigqual[wavepoint->qualptr++]=0; /* If so, enter them as 0's in the */ + wavepoint->qualptr %=WAVEPOINT_HISTORY; /* ringbuffer. */ + } + } + wavepoint->last_seen=jiffies; /* Add beacon to history */ + wavepoint->last_seq=seq; + wavepoint->sigqual[wavepoint->qualptr++]=sigqual; + wavepoint->qualptr %=WAVEPOINT_HISTORY; + ptr=(wavepoint->qualptr-WAVEPOINT_FAST_HISTORY+WAVEPOINT_HISTORY)%WAVEPOINT_HISTORY; + + for(i=0;isigqual[ptr++]; + ptr %=WAVEPOINT_HISTORY; + } + average_slow=average_fast; + for(i=WAVEPOINT_FAST_HISTORY;isigqual[ptr++]; + ptr %=WAVEPOINT_HISTORY; + } + + wavepoint->average_fast=average_fast/WAVEPOINT_FAST_HISTORY; + wavepoint->average_slow=average_slow/WAVEPOINT_HISTORY; +} + +/* Perform a handover to a new WavePoint */ +void +wv_roam_handover(wavepoint_history *wavepoint, net_local *lp) +{ + unsigned short base = lp->dev->base_addr; + mm_t m; + unsigned long x; + + if(wavepoint==lp->curr_point){ /* Sanity check... */ + wv_nwid_filter(!NWID_PROMISC,lp); + return; + } + +#ifdef WAVELAN_ROAMING_DEBUG + printk(KERN_DEBUG "WaveLAN: Doing handover to %.4X, dev %s\n",wavepoint->nwid,lp->dev->name); +#endif + + /* Disable interrupts & save flags */ + x = wv_splhi(); + + m.w.mmw_netw_id_l = wavepoint->nwid & 0xFF; + m.w.mmw_netw_id_h = (wavepoint->nwid & 0xFF00) >> 8; + + mmc_write(base, (char *)&m.w.mmw_netw_id_l - (char *)&m, (unsigned char *)&m.w.mmw_netw_id_l, 2); + + /* ReEnable interrupts & restore flags */ + wv_splx(x); + + wv_nwid_filter(!NWID_PROMISC,lp); + lp->curr_point=wavepoint; +} + +/* Called when a WavePoint beacon is received */ +static inline void +wl_roam_gather(device * dev, + u_char * hdr, /* Beacon header */ + u_char * stats) /* SNR, Signal quality of packet*/ +{ + + wavepoint_beacon *beacon= (wavepoint_beacon *)hdr; /* Rcvd. Beacon */ + unsigned short nwid=ntohs(beacon->nwid); /* NWID of WavePoint */ + unsigned short domain=ntohs(beacon->domain_id); /* Domain of WavePoint */ + unsigned short sigqual=stats[2] & MMR_SGNL_QUAL; /* SNR of beacon */ + wavepoint_history *wavepoint=NULL; /* WavePoint table entry */ + net_local *lp=(net_local *)dev->priv; /* Device info */ + +#if WAVELAN_ROAMING_DEBUG > 1 + printk(KERN_DEBUG "WaveLAN: beacon, dev %s:\n",dev->name); + printk(KERN_DEBUG "Domain: %.4X NWID: %.4X SigQual=%d\n",domain,nwid,sigqual); +#endif + lp->wavepoint_table.locked=1; /* */ + + wavepoint=wl_roam_check(nwid,lp); /* Find WavePoint table entry */ + if(wavepoint==NULL){ /* If no entry, Create a new one... */ + wavepoint=wl_new_wavepoint(nwid,beacon->seq,lp); + if(wavepoint==NULL) + goto out; + } + if(lp->curr_point==NULL) /* If this is the only WavePoint, */ + wv_roam_handover(wavepoint, lp); /* Jump on it! */ + + wl_update_history(wavepoint, sigqual, beacon->seq); /* Update SNR history stats. */ + + if(lp->curr_point->average_slow < SEARCH_THRESH_LOW) /* If our current WavePoint is getting */ + if(!lp->cell_search) /* faint, start looking for a new one */ + wv_nwid_filter(NWID_PROMISC,lp); + + if(wavepoint->average_slow > lp->curr_point->average_slow + WAVELAN_ROAMING_DELTA) + wv_roam_handover(wavepoint, lp); /* Handover to a better WavePoint */ + + if(lp->curr_point->average_slow > SEARCH_THRESH_HIGH) /* If our SNR is getting better, drop */ + if(lp->cell_search) /* out of cell search mode */ + wv_nwid_filter(!NWID_PROMISC,lp); + +out: + lp->wavepoint_table.locked=0; /* :-) */ +} + +/* Test this MAC frame a WavePoint beacon */ +static inline int +WAVELAN_BEACON(unsigned char *data) +{ + wavepoint_beacon *beacon= (wavepoint_beacon *)data; + static wavepoint_beacon beacon_template={0xaa,0xaa,0x03,0x08,0x00,0x0e,0x20,0x03,0x00}; + + if(memcmp(beacon,&beacon_template,9)==0) + return 1; + else + return 0; +} +#endif + /************************ I82593 SUBROUTINES *************************/ /* * Usefull subroutines to manage the Ethernet controler @@ -2205,7 +2535,7 @@ /* Statistics gathering & stuff associated. * It seem a bit messy with all the define, but it's really simple... */ -#if defined(WIRELESS_SPY) || defined(HISTOGRAM) +#if defined(WIRELESS_SPY) || defined(HISTOGRAM) || defined(WAVELAN_ROAMING) if( #ifdef WIRELESS_SPY (lp->spy_number > 0) || @@ -2213,6 +2543,9 @@ #ifdef HISTOGRAM (lp->his_number > 0) || #endif /* HISTOGRAM */ +#ifdef WAVELAN_ROAMING + (1) || +#endif /* WAVELAN ROAMING */ 0) { u_char stats[3]; /* Signal level, Noise level, Signal quality */ @@ -2225,6 +2558,11 @@ dev->name, stats[0] & 0x3F, stats[1] & 0x3F, stats[2] & 0x0F); #endif +#ifdef WAVELAN_ROAMING + if(WAVELAN_BEACON(skb->data)){ + wl_roam_gather(dev, skb->data, stats); + } +#endif /* Spying stuff */ #ifdef WIRELESS_SPY /* Same as above */ @@ -2348,7 +2686,16 @@ len = c[2] | (c[3] << 8); /* Check status */ +#ifdef WAVELAN_HIGH_COVERAGE + /* josullvn@cs.cmu.edu: + * We are more restrictive about the types of errors that are problems. + * Most importantly RX_NO_AD_MATCH and RX_IA_MATCH are status messages in + * CMU like situations. + */ + if ((status & 0x4270) != 0x0010) +#else if(!(status & RX_RCV_OK)) +#endif { lp->stats.rx_errors++; if(status & RX_NO_SFD) @@ -2433,6 +2780,9 @@ /* Indicate end of transmit chain */ outb(OP0_NOP, PIOP(base)); +#ifdef WAVELAN_HIGH_COVERAGE + outb(OP0_NOP, PIOP(base)); /* josullvn@cs.cmu.edu: need to send a second NOP for alignment... */ +#endif /* Reset the transmit DMA pointer */ hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); @@ -2446,6 +2796,11 @@ lp->stats.tx_bytes += length; #endif /* 2.1.25 */ +#ifndef WAVELAN_HIGH_COVERAGE + /* josullvn@cs.cmu.edu: + * This watch dog thing is a nasty hack, and flares up + * badly in our environment + */ /* If watchdog not already active, activate it... */ if(lp->watchdog.prev == (timer_list *) NULL) { @@ -2453,6 +2808,7 @@ lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES; add_timer(&lp->watchdog); } +#endif wv_splx(x); @@ -2980,6 +3336,31 @@ cfblk.tx_eop = TRUE; /* Signal EOP on packet transmission */ cfblk.rbuf_size = RX_SIZE>>11; /* Set receive buffer size */ cfblk.rcvstop = TRUE; /* Enable Receive Stop Register */ +#ifdef WAVELAN_HIGH_COVERAGE + /* josullvn@cs.cmu.edu: + * This are some of the more significant changes I made. Not + * all of them may be necessary, and some may not be correct. + * Basically, the configuration now exactly matches that of the datasheets + * driver. This is obviously stupid if the Linux code doesn't handle + * those things the same way. However, wished to fix the occurances + * of NO_CTS errors and HEART_BEAT errors. If the new cfblk changes + * introduced below are removed, they return. However, these changes + * introduce some error in frame handling - those errors are recoverable + * but should be fixed. + */ + cfblk.fifo_limit = 8; + cfblk.fifo_32 = 1; + cfblk.throttle_enb = 0; // + cfblk.preamb_len = 0; // this seems to be necessary to kill + // those heartbeat messages... + cfblk.exp_prio = 5; ///* conform to 802.3 backoff algoritm */ + cfblk.bof_met = 1; ///* conform to 802.3 backoff algoritm */ + cfblk.ifrm_spc = 2; // + cfblk.slottim_low = 1; + cfblk.slottim_hi = 0; + cfblk.rcvstop = 0; // + cfblk.syncrqs = 0; +#endif #ifdef DEBUG_I82593_SHOW { @@ -3023,6 +3404,12 @@ OP0_IA_SETUP, SR0_IA_SETUP_DONE)) return(FALSE); +#ifdef WAVELAN_ROAMING + /* If roaming is enabled, join the "Beacon Request" multicast group...*/ + /* But only if it's not in there already! */ + + dev_mc_add(dev,WAVELAN_BEACON_ADDRESS, WAVELAN_ADDR_SIZE, 1); +#endif /* If any multicast address to set */ if(lp->mc_count) { @@ -3895,6 +4282,10 @@ /* If the device is currently in use, we won't release until it is * actually closed. */ + +#ifdef WAVELAN_ROAMING + wv_roam_cleanup(dev); +#endif if(link->open) { #ifdef DEBUG_CONFIG_INFO @@ -4049,6 +4440,10 @@ wavelan_detach(link); return NULL; } + +#ifdef WAVELAN_ROAMING + wv_roam_init(dev); +#endif #ifdef DEBUG_CALLBACK_TRACE printk(KERN_DEBUG "<- wavelan_attach()\n"); diff -ur ../temp/pcmcia-cs-3.0.8/clients/wavelan_cs.h clients/wavelan_cs.h --- ../temp/pcmcia-cs-3.0.8/clients/wavelan_cs.h Sun May 10 08:14:20 1998 +++ clients/wavelan_cs.h Wed Jan 27 14:45:56 1999 @@ -336,9 +336,57 @@ /* Wavelan declarations */ #include "i82593.h" /* Definitions for the Intel chip */ - #include "wavelan.h" /* Others bits of the hardware */ +/*************************** WaveLAN Roaming **************************/ +#define WAVELAN_ROAMING +#define WAVELAN_ROAMING_DEBUG 1 /* 1 = Trace of handover decisions */ + /* 2 = Info on each beacon rcvd... */ +#define MAX_WAVEPOINTS 7 /* Max visible at one time */ +#define WAVEPOINT_HISTORY 5 /* SNR sample hist. slow search */ +#define WAVEPOINT_FAST_HISTORY 2 /* SNR sample hist. fast search */ +#define SEARCH_THRESH_LOW 10 /* SNR to enter cell search */ +#define SEARCH_THRESH_HIGH 13 /* SNR to leave cell search */ +#define WAVELAN_ROAMING_DELTA 1 /* Hysteresis value (+/- SNR) */ +#define CELL_TIMEOUT 2*HZ /* in jiffies */ + +#define FAST_CELL_SEARCH 1 /* Boolean values... */ +#define NWID_PROMISC 1 /* for code clarity. */ + +typedef struct wavepoint_beacon +{ + unsigned char dsap, /* Unused */ + ssap, /* Unused */ + ctrl, /* Unused */ + O,U,I, /* Unused */ + spec_id1, /* Unused */ + spec_id2, /* Unused */ + pdu_type, /* Unused */ + seq; /* WavePoint beacon sequence no. */ + unsigned short domain_id, /* WavePoint Domain ID */ + nwid; /* WavePoint NWID */ +} wavepoint_beacon; + +typedef struct wavepoint_history +{ + unsigned short nwid; /* WavePoint's NWID */ + int average_slow; /* SNR running average */ + int average_fast; /* SNR running average */ + unsigned char sigqual[WAVEPOINT_HISTORY];/* Ringbuffer of recent SNR's */ + unsigned char qualptr; /* Index into ringbuffer */ + unsigned char last_seq; /* Last seq. no seen for WavePoint */ + struct wavepoint_history *next; /* Next WavePoint in table */ + struct wavepoint_history *prev; /* Prev Wavepoint in table */ + unsigned long last_seen; /* Time of last beacon recvd, jiffies */ +} wavepoint_history; + +struct wavepoint_table +{ + wavepoint_history *head; /* Start of ringbuffer */ + int num_wavepoints; /* No. of WavePoints visible */ + unsigned char locked; /* Table lock */ +}; + /****************************** DEBUG ******************************/ #undef DEBUG_MODULE_TRACE /* Module insertion/removal */ @@ -458,9 +506,28 @@ u_char his_range[16]; /* Boundaries of interval ]n-1; n] */ u_long his_sum[16]; /* Sum in interval */ #endif /* HISTOGRAM */ +#ifdef WAVELAN_ROAMING + struct wavepoint_table wavepoint_table; /* Table of visible WavePoints */ + wavepoint_history * curr_point; /* Current wavepoint */ + int cell_search; /* Searching for new cell? */ + struct timer_list cell_timer; /* Garbage collection */ +#endif }; /**************************** PROTOTYPES ****************************/ + +/* ----------------------- ROAMING SUBROUTINES --------------------- */ + +wavepoint_history *wl_roam_check(unsigned short nwid, net_local *lp); +wavepoint_history *wl_new_wavepoint(unsigned short nwid, unsigned char seq, net_local *lp); +void wl_del_wavepoint(wavepoint_history *wavepoint, net_local *lp); +void wl_cell_expiry(unsigned long data); +wavepoint_history *wl_best_sigqual(int fast_search, net_local *lp); +void wl_update_history(wavepoint_history *wavepoint, unsigned char sigqual, unsigned char seq); +void wv_roam_handover(wavepoint_history *wavepoint, net_local *lp); +void wv_nwid_filter(unsigned char mode, net_local *lp); +void wv_roam_init(struct device *dev); +void wv_roam_cleanup(struct device *dev); /* ----------------------- MISC SUBROUTINES ------------------------ */ static inline unsigned long /* flags */