*** ../wavelan.orig/if_wi.c	Thu Jan 17 11:21:23 2002
--- if_wi.c	Thu Jan 17 13:47:50 2002
***************
*** 145,150 ****
--- 145,151 ----
  static int wi_seek		__P((struct wi_softc *, int, int, int));
  static int wi_alloc_nicmem	__P((struct wi_softc *, int, int *));
  static void wi_inquire		__P((void *));
+ static void wi_wait_scan	__P((void *));
  static void wi_setdef		__P((struct wi_softc *, struct wi_req *));
  static int wi_mgmt_xmit		__P((struct wi_softc *, caddr_t, int));
  
***************
*** 166,171 ****
--- 167,175 ----
  
  static int wi_alloc		__P((device_t, int));
  static void wi_free		__P((device_t));
+ static void wi_get_firmware     __P((struct wi_softc *, u_int16_t *, u_int16_t *,
+  u_int16_t *));
+ 
  
  static int wi_get_cur_ssid	__P((struct wi_softc *, char *, int *));
  static int wi_media_change	__P((struct ifnet *));
***************
*** 436,441 ****
--- 440,446 ----
  	struct wi_ltv_gen	gen;
  	struct ifnet		*ifp;
  	int			error;
+  	u_int16_t               v, maj, min;
  
  	sc = device_get_softc(dev);
  	ifp = &sc->arpcom.ac_if;
***************
*** 472,477 ****
--- 477,486 ----
  	device_printf(dev, "Ethernet address: %6D\n",
  	    sc->arpcom.ac_enaddr, ":");
  
+         wi_get_firmware( sc, &v, &maj, &min);
+         printf("wi%d: (hw type=%d, firmware=0x%02x:%02x)\n",
+             sc->wi_unit, v, maj, min);
+ 
  	ifp->if_softc = sc;
  	ifp->if_unit = sc->wi_unit;
  	ifp->if_name = "wi";
***************
*** 506,511 ****
--- 515,521 ----
  	sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS;
  	sc->wi_pm_enabled = WI_DEFAULT_PM_ENABLED;
  	sc->wi_max_sleep = WI_DEFAULT_MAX_SLEEP;
+ 	sc->wi_monitor = WI_DEFAULT_MONITOR;
  
  	/*
  	 * Read the default channel from the NIC. This may vary
***************
*** 534,539 ****
--- 544,553 ----
  
  	bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats));
  
+ 	bzero((char *)&sc->wi_aps, sizeof(sc->wi_aps));
+ 	sc->wi_scanning=0;
+ 	sc->wi_naps=0;
+ 
  	wi_init(sc);
  	wi_stop(sc);
  
***************
*** 565,570 ****
--- 579,585 ----
  	 */
  	ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
  	callout_handle_init(&sc->wi_stat_ch);
+         callout_handle_init(&sc->wi_scan_ch);
  
  	return(0);
  }
***************
*** 577,582 ****
--- 592,598 ----
  	struct wi_frame		rx_frame;
  	struct mbuf		*m;
  	int			id;
+ 	int			hdrlen,hdroff,datalen;
  
  	ifp = &sc->arpcom.ac_if;
  
***************
*** 593,598 ****
--- 609,743 ----
  		return;
  	}
  
+ 	/* If monitoring packet came in... */
+ 	if((rx_frame.wi_status&WI_STAT_MAC_PORT)==WI_STAT_MAC_PORT)
+ 	{
+ 		/* Depending on the monitoring mode... */
+ 		switch(sc->wi_monitor&WI_MONITOR_HTYPE)
+ 		{
+ 			case WI_MONITOR_WAVELAN:
+ 				/* Passing whole WaveLAN header */
+ 				hdroff = 0;
+ 				hdrlen = sizeof(struct wi_80211_hdr) + WI_802_11_OFFSET_HDR;
+ 				break;
+ 			case WI_MONITOR_80211:
+ 				/* Passing 802.11 header */
+ 				hdroff = WI_802_11_OFFSET_HDR;
+ 				hdrlen = sizeof(struct wi_80211_hdr);
+ 				break;
+ 			case WI_MONITOR_ETHER:
+ 				/* Faking Ethernet header */
+ 				hdroff = -1;
+ 				hdrlen = sizeof(struct ether_header);
+ 				break;
+ 			default:
+ 				return;
+ 		}
+ 
+ 		/* Depending on the packet type... */
+ 		switch(rx_frame.wi_frame_ctl&WI_FCTL_FTYPE)
+ 		{
+ 			case WI_FTYPE_MGMT:
+ 				if((rx_frame.wi_frame_ctl&WI_FCTL_STYPE)==WI_STYPE_MGMT_BEACON)
+ 				{ if(!(sc->wi_monitor&WI_MONITOR_BEACONS)) return; }
+ 				else
+ 				{ if(!(sc->wi_monitor&WI_MONITOR_MGMT)) return; }
+ 				datalen = rx_frame.wi_dat_len;
+ 				hdrlen -= 6;
+ 				break;
+ 			case WI_FTYPE_CTL:
+ 				if(!(sc->wi_monitor&WI_MONITOR_CTRL)) return;
+ 				datalen = 0;
+ 				switch(rx_frame.wi_frame_ctl&WI_FCTL_STYPE)
+ 				{
+ 					case WI_STYPE_CTL_ACK:
+ 					case WI_STYPE_CTL_CTS:
+ 						hdrlen -= 20;
+ 						break;
+ 					default:
+ 						hdrlen -= 14;
+ 						break;
+ 				}
+ 				break;
+ 			case WI_FTYPE_DATA:
+ 				if(!(sc->wi_monitor&WI_MONITOR_DATA)) return;
+ 				datalen = rx_frame.wi_dat_len;
+ 				break;
+ 			default:
+ 				return;
+ 		}
+ 
+ 		/* Check for mbuf overflow */
+ 		if(hdrlen+datalen>MCLBYTES)
+ 		{
+ 			device_printf
+ 			(
+ 				sc->dev,
+ 				"oversized packet received (wi_dat_len=%d, wi_frame_ctl=0x%04x)\n",
+ 				rx_frame.wi_dat_len,
+ 				rx_frame.wi_frame_ctl
+ 			);
+ 			ifp->if_ierrors++;
+ 			return;
+ 		}
+ 
+ 		MGETHDR(m,M_DONTWAIT,MT_DATA);
+ 		if(m==NULL)
+ 		{
+ 			ifp->if_ierrors++;
+ 			return;
+ 		}
+ 
+ 		MCLGET(m,M_DONTWAIT);
+ 		if(!(m->m_flags&M_EXT))
+ 		{
+ 			m_freem(m);
+ 			ifp->if_ierrors++;
+ 			return;
+ 		}
+ 
+ 		/* Set header/packet lengths and interface */
+ 		eh = mtod(m, struct ether_header *);
+ 		m->m_pkthdr.rcvif = ifp;
+ 		m->m_pkthdr.len   = m->m_len = hdrlen + datalen;
+ 
+ 		/* If real header requested, copy it */
+ 		if(hdroff>=0)
+ 			bcopy((char *)&rx_frame+hdroff,mtod(m,caddr_t),hdrlen);
+ 		else
+ 		{
+ 			/* Fake Ethernet header */
+ 			eh->ether_type = ETHERTYPE_LOOPBACK;
+ 			bcopy((char *)&rx_frame.wi_addr1,(char *)&eh->ether_dhost,ETHER_ADDR_LEN);
+ 			bcopy((char *)&rx_frame.wi_addr2,(char *)&eh->ether_shost,ETHER_ADDR_LEN);
+ 			/* We now have a header */
+ 			hdrlen = sizeof(struct ether_header);
+ 		}
+ 
+ 		/* Read data, if present */
+ 		if(datalen)
+ 			if(wi_read_data(sc,id,WI_802_11_OFFSET_RAW,mtod(m,caddr_t)+hdrlen,datalen))
+ 			{
+ 				m_freem(m);
+ 				ifp->if_ierrors++;
+ 				return;
+ 			}
+ 
+ #ifdef WICACHE
+ 		if(hdroff<0) wi_cache_store(sc,eh,m,rx_frame.wi_q_info);
+ #endif  
+ 
+ 		/* One more packet received */
+ 		ifp->if_ipackets++;
+ 
+ 		/* Feed packet to BPF */
+ 		if(ifp->if_bpf) bpf_mtap(ifp,m);
+ 
+ 		/* Done logging */
+ 		m_freem(m);
+ 		return;
+ 	}
+ 
  	MGETHDR(m, M_DONTWAIT, MT_DATA);
  	if (m == NULL) {
  		ifp->if_ierrors++;
***************
*** 610,616 ****
  
  	if (rx_frame.wi_status == WI_STAT_1042 ||
  	    rx_frame.wi_status == WI_STAT_TUNNEL ||
! 	    rx_frame.wi_status == WI_STAT_WMP_MSG) {
  		if((rx_frame.wi_dat_len + WI_SNAPHDR_LEN) > MCLBYTES) {
  			device_printf(sc->dev, "oversized packet received "
  			    "(wi_dat_len=%d, wi_status=0x%x)\n",
--- 755,762 ----
  
  	if (rx_frame.wi_status == WI_STAT_1042 ||
  	    rx_frame.wi_status == WI_STAT_TUNNEL ||
! 	    rx_frame.wi_status == WI_STAT_WMP_MSG ||
! 	    ((rx_frame.wi_status & WI_STAT_MAC_PORT) == WI_STAT_MAC_PORT)) {
  		if((rx_frame.wi_dat_len + WI_SNAPHDR_LEN) > MCLBYTES) {
  			device_printf(sc->dev, "oversized packet received "
  			    "(wi_dat_len=%d, wi_status=0x%x)\n",
***************
*** 719,732 ****
  	return;
  }
  
  void wi_update_stats(sc)
  	struct wi_softc		*sc;
  {
  	struct wi_ltv_gen	gen;
  	u_int16_t		id;
  	struct ifnet		*ifp;
  	u_int32_t		*ptr;
! 	int			len, i;
  	u_int16_t		t;
  
  	ifp = &sc->arpcom.ac_if;
--- 865,922 ----
  	return;
  }
  
+ void wi_wait_scan(xsc)
+  	void			*xsc;
+ {
+  	struct wi_softc		*sc;
+  	struct ifnet		*ifp;
+ 	int rc;
+  
+  	sc = xsc;
+  	ifp = &sc->arpcom.ac_if;
+  
+  	/* If not scanning, ignore */
+  	if (!sc->wi_scanning)
+  		return;
+ 
+  	/* If transmitting... wait for later to make INQUIRE
+ 	*/
+  	if (ifp->if_flags & IFF_OACTIVE) {
+  		sc->wi_scan_ch = timeout(wi_wait_scan, sc, hz*1);
+ 		return;
+ 	}
+  
+ 	/* try INQUIRE -- this will cause info interrupt 
+ 	*/
+  	rc = wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS);
+  
+  	/* If not ready, call this function later
+ 	*/
+ 	if (rc == ETIMEDOUT) {
+  		sc->wi_scan_ch = timeout(wi_wait_scan, sc, hz*1);
+ 	}
+  	return;
+ }
+ 
+ /*
+  * copy hw stats structure to softc clone of it
+  *
+  * important side effect: BSD if collisions are updated
+  * because of this, therefore this routine is called once
+  * a minute.  
+  *
+  * It is also printed out via wicontrol -i wi0 -o
+  */
  void wi_update_stats(sc)
  	struct wi_softc		*sc;
  {
  	struct wi_ltv_gen	gen;
+ 	struct wi_scan_data_p2  ap2;  /* prism2 output */
+ 	struct wi_scan_data     ap;   /* lucent output */
  	u_int16_t		id;
  	struct ifnet		*ifp;
  	u_int32_t		*ptr;
! 	int			len, i, j;
  	u_int16_t		t;
  
  	ifp = &sc->arpcom.ac_if;
***************
*** 735,761 ****
  
  	wi_read_data(sc, id, 0, (char *)&gen, 4);
  
! 	if (gen.wi_type != WI_INFO_COUNTERS)
! 		return;
  
! 	len = (gen.wi_len - 1 < sizeof(sc->wi_stats) / 4) ?
! 		gen.wi_len - 1 : sizeof(sc->wi_stats) / 4;
! 	ptr = (u_int32_t *)&sc->wi_stats;
  
! 	for (i = 0; i < len - 1; i++) {
! 		t = CSR_READ_2(sc, WI_DATA1);
  #ifdef WI_HERMES_STATS_WAR
! 		if (t > 0xF000)
! 			t = ~t & 0xFFFF;
  #endif
! 		ptr[i] += t;
! 	}
! 
! 	ifp->if_collisions = sc->wi_stats.wi_tx_single_retries +
! 	    sc->wi_stats.wi_tx_multi_retries +
! 	    sc->wi_stats.wi_tx_retry_limit;
  
! 	return;
  }
  
  static void wi_intr(xsc)
--- 925,1019 ----
  
  	wi_read_data(sc, id, 0, (char *)&gen, 4);
  
! 	switch(gen.wi_type)
! 	{
! 		case WI_INFO_SCAN_RESULTS:
! 			/* lucent entries are shorter than prism2 
! 			*/
! 			if(gen.wi_len<3)
! 				break;
! 			if (!sc->wi_prism2) {
!                         	len = 2*(gen.wi_len)/sizeof(ap);
! 				len = len>MAXAPINFO? MAXAPINFO:len;
! 				sc->wi_naps = len;
! 				for(i=0;i<len;i++)
! 				{
! 					for(j=0;j<sizeof(ap)/2;j++)
! 						((u_int16_t *)&ap)[j] = CSR_READ_2(sc, WI_DATA1);
! 					bcopy(ap.wi_bssid,sc->wi_aps[i].bssid,6);
! 					sc->wi_aps[i].channel = ap.wi_channel;
! 					sc->wi_aps[i].signal  = ap.wi_signal-149;
! 					sc->wi_aps[i].noise   = ap.wi_noise-149;
! 					sc->wi_aps[i].quality = sc->wi_aps[i].signal-sc->wi_aps[i].noise;
! 					sc->wi_aps[i].capinfo = ap.wi_capinfo;
! 					sc->wi_aps[i].interval = ap.wi_interval;
! 					/* prevent disasters
! 					*/
! 					if (ap.wi_namelen > 32)
! 						ap.wi_namelen = 32;
! 					sc->wi_aps[i].namelen = ap.wi_namelen;
! 					bcopy(ap.wi_name, sc->wi_aps[i].name,
! 						ap.wi_namelen);
! 				}
! 				sc->wi_scanning = 0;
! 				break;
! 			}
! 			/* prism2 
! 			*/
! 			/* Compute number of AP entries 
! 			 * Note: there is some sort of pre-header that
! 			 * is being skipped
! 			*/
!                         len = 2*(gen.wi_len-3)/sizeof(ap2);
! 			len = len>MAXAPINFO? MAXAPINFO:len;
! 			sc->wi_naps = len;
! 			/* Read and ignore the first word */
! 			t = CSR_READ_2(sc, WI_DATA1);
! 			/* Read the rest */
! 			for(i=0;i<len;i++)
! 			{
! 				for(j=0;j<sizeof(ap2)/2;j++)
! 					((u_int16_t *)&ap2)[j] = CSR_READ_2(sc, WI_DATA1);
! 				bcopy(ap2.wi_bssid,sc->wi_aps[i].bssid,6);
! 				sc->wi_aps[i].channel = ap2.wi_channel1;
! 				sc->wi_aps[i].signal  = ap2.wi_signal-149;
! 				sc->wi_aps[i].noise   = ap2.wi_noise-149;
! 				sc->wi_aps[i].quality = sc->wi_aps[i].signal-sc->wi_aps[i].noise;
! 				sc->wi_aps[i].capinfo = ap2.wi_capinfo;
! 				sc->wi_aps[i].interval = ap2.wi_interval;
! 				/* prevent disasters
! 				*/
! 				if (ap2.wi_namelen > 32)
! 					ap2.wi_namelen = 32;
! 				sc->wi_aps[i].namelen = ap2.wi_namelen;
! 				bcopy(ap2.wi_name, sc->wi_aps[i].name,
! 						ap2.wi_namelen);
  
! 			}
! 			/* Done scanning 
! 			*/
! 			sc->wi_scanning = 0;
! 			break;
! 
! 		case WI_INFO_COUNTERS:
! 			len = (gen.wi_len - 1 < sizeof(sc->wi_stats) / 4) ?
! 				gen.wi_len - 1 : sizeof(sc->wi_stats) / 4;
! 			ptr = (u_int32_t *)&sc->wi_stats;
  
! 			for (i = 0; i < len - 1; i++) {
! 				t = CSR_READ_2(sc, WI_DATA1);
  #ifdef WI_HERMES_STATS_WAR
! 				if (t > 0xF000)
! 					t = ~t & 0xFFFF;
  #endif
! 				ptr[i] += t;
! 			}
  
! 			ifp->if_collisions = sc->wi_stats.wi_tx_single_retries
! 					   + sc->wi_stats.wi_tx_multi_retries
! 					   + sc->wi_stats.wi_tx_retry_limit;
! 			break;
! 	}
  }
  
  static void wi_intr(xsc)
***************
*** 1402,1410 ****
--- 1660,1688 ----
  		if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS && suser(p))
  			break;
  		if (wreq.wi_type == WI_RID_IFACE_STATS) {
+ 			/* call update routine to make sure data
+ 			 * is as up to date as possible
+ 			*/
+ 			wi_update_stats(sc);
  			bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val,
  			    sizeof(sc->wi_stats));
  			wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1;
+ 		/* copyout AP info
+ 		*/
+ 		} else if (wreq.wi_type == WI_RID_READ_APS) {
+ 			if (sc->wi_scanning)
+ 			{
+ 				error = EINVAL;
+ 				break;
+ 			}
+ 			else
+ 			{
+ 				len = sc->wi_naps*sizeof(struct wi_apinfo);
+ 				len = len>WI_MAX_DATALEN? WI_MAX_DATALEN:len;
+ 				len = len/sizeof(struct wi_apinfo);	 
+ 				bcopy((char *)&len, (char *)&wreq.wi_val, sizeof(len));
+ 				bcopy((char *)&sc->wi_aps, (char *)&wreq.wi_val+sizeof(len), len*sizeof(struct wi_apinfo));
+ 			}
  		} else if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS) {
  			bcopy((char *)&sc->wi_keys, (char *)&wreq,
  			    sizeof(struct wi_ltv_keys));
***************
*** 1444,1449 ****
--- 1722,1762 ----
  		} else if (wreq.wi_type == WI_RID_MGMT_XMIT) {
  			error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val,
  			    wreq.wi_len);
+ 		} else if (wreq.wi_type == WI_RID_SCAN_APS) {
+ 			if (wreq.wi_len != 4)
+ 			{
+ 				error = EINVAL;
+ 				break;
+ 			}
+ 			if (!sc->wi_scanning)
+ 			{
+ 				if (sc->wi_prism2) {
+ 					wreq.wi_type = WI_RID_SCAN_REQUEST;
+ 					error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq);
+ 				}
+ 				if(!error)
+ 				{
+ 					sc->wi_scanning = 1;
+ 					sc->wi_scan_ch = timeout(wi_wait_scan, sc, hz*1);
+ 				}
+ 			}
+ 		} else if (wreq.wi_type == WI_RID_MONITOR_MODE) {
+ 			sc->wi_monitor = wreq.wi_val[0];
+ 			wi_init(sc);
+ 			switch(sc->wi_monitor&WI_MONITOR_HTYPE)
+ 			{
+ 				case WI_MONITOR_WAVELAN:
+ 					bpfattach(ifp,DLT_WAVELAN_HEADER,sizeof(struct wi_frame));
+ 					break;
+ 				case WI_MONITOR_80211:
+ 					bpfattach(ifp,DLT_IEEE802_11,sizeof(struct wi_80211_hdr));
+ 					break;
+ 				case WI_MONITOR_ETHER:
+ 				case WI_MONITOR_OFF:
+ 				default:
+ 					bpfattach(ifp,DLT_EN10MB,sizeof(struct wi_80211_hdr));
+ 					break;
+ 			}
  		} else {
  			error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq);
  			if (!error)
***************
*** 1742,1747 ****
--- 2055,2076 ----
  	/* Enable desired port */
  	wi_cmd(sc, WI_CMD_ENABLE|sc->wi_portnum, 0);
  
+ 	/* Turn monitoring on/off */
+ 	if(sc->wi_monitor)
+ 	{
+ 		if(wi_cmd(sc,WI_CMD_TEST|WI_TEST_MONITOR,0)) sc->wi_monitor=0;
+ 	} 
+ 	else
+ 	{
+ 		wi_cmd(sc,WI_CMD_TEST|WI_TEST_STOP,0);
+ 	}
+ 
+ 	/*  scanning variable is modal, therefore reinit to OFF, in
+ 	*  case it was on.
+ 	*/
+ 	sc->wi_scanning=0;
+ 	sc->wi_naps=0;
+ 
  	if (wi_alloc_nicmem(sc, ETHER_MAX_LEN + sizeof(struct wi_frame) + 8, &id))
  		device_printf(sc->dev, "tx buffer allocation failed\n");
  	sc->wi_tx_data_id = id;
***************
*** 1913,1918 ****
--- 2242,2248 ----
  	}
  
  	untimeout(wi_inquire, sc, sc->wi_stat_ch);
+ 	untimeout(wi_wait_scan, sc, sc->wi_scan_ch);
  
  	ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
  
***************
*** 1998,2003 ****
--- 2328,2360 ----
  
  	return;
  }
+ 
+ /* borrowed from linux wvlan driver.
+  * get version, firmware info.  There is more info
+  * but don't know what it means.
+  * data[1] is vendor, 2 & 3 are firmware # in major/minor order.
+  * lucent is vendor 1, 2 and 6 are prism apparently.
+ */      
+ 
+ static void wi_get_firmware( struct wi_softc *sc, u_int16_t *v,
+         u_int16_t *maj, u_int16_t *min)
+ {
+         struct wi_ltv_staid     staid;
+         int rc;
+ 
+         staid.wi_len = 32;
+         staid.wi_type = WI_RID_STAID;
+         rc = wi_read_record(sc, (struct wi_ltv_gen *)&staid);
+         /*
+         if (rc < 0) {
+                 printf("rc %d\n", rc);
+         }
+         */
+         *v = staid.wi_data[1];  /* hardware type */
+         *maj = staid.wi_data[2];  /* firmware major */
+         *min =  staid.wi_data[3];  /* firmware minor */
+ }
+ 
  
  #ifdef WICACHE
  /* wavelan signal strength cache code.
