/*******************************************************************************
  
  Copyright(c) 2004 Intel Corporation. All rights reserved.

  Portions of this file are based on the WEP enablement code provided by the
  Host AP project hostap-drivers v0.1.3
  Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
  <jkmaline@cc.hut.fi>
  Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
  
  Some management frame logic and misc modifications for the rtl8180-sa2004 
  driver by Andrea Merello <andreamrl@tiscali.it>
  
  This program is free software; you can redistribute it and/or modify it 
  under the terms of version 2 of the GNU General Public License as 
  published by the Free Software Foundation.

  This program is distributed in the hope that it will be useful, but WITHOUT 
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
  more details.
  
  You should have received a copy of the GNU General Public License along with
  this program; if not, write to the Free Software Foundation, Inc., 59 
  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  
  The full GNU General Public License is included in this distribution in the
  file called LICENSE.
  
  Contact Information:
  James P. Ketrenos <ipw2100-admin@linux.intel.com>
  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497

  Please note that this file has been modified, so please don't report bugs
  to the contact above uless you are sure it is really a bug in the *ORIGINAL*
  code.
  
*******************************************************************************/
#include <linux/init.h>
#include <linux/compiler.h>
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/if_arp.h>
#include <linux/in6.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/tcp.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/wireless.h>
#include <linux/etherdevice.h>
#include <asm/uaccess.h>

#include "ieee802_11.h"
#include "ieee80211.h"

#define MODULE_NAME	"ieee80211-r8180"

MODULE_DESCRIPTION("802.11 data/management/control stack");

MODULE_AUTHOR("Copyright (C) 2004 Intel Corporation \
<jketreno@linux.intel.com>.. Modified by others...");

MODULE_LICENSE("GPL");



const long wlan_frequencies[] = {  
	2412, 2417, 2422, 2427, 
	2432, 2437, 2442, 2447, 
	2452, 2457, 2462, 2467, 
	2472, 2484  
};

inline struct sk_buff *ieee80211_authentication_req(struct ieee80211_beacon *beacon,struct ieee80211_device *ieee)
{

	struct sk_buff *skb;
	
	struct ieee80211_authentication *auth;
	
	int len=30;
	skb=dev_alloc_skb(len+1); 
	skb->len=len;
	
	auth = (struct ieee80211_authentication *)skb->data;
	auth->header.frame_control=0xb0;
	auth->header.duration_id=0x013a;
	memcpy(auth->header.addr1,beacon->bssid,ETH_ALEN);
	memcpy(auth->header.addr2,ieee->dev->dev_addr,ETH_ALEN);
	memcpy(auth->header.addr3,beacon->bssid,ETH_ALEN);
	auth->header.seq_ctrl= cpu_to_le16(ieee->seq_ctrl <<4);
	
	if(ieee->seq_ctrl == 0xFFF)
		ieee->seq_ctrl =0;
	else
		ieee->seq_ctrl++;
	
	
	auth->algorithm=0;
	auth->transaction=ieee->associate_seq;
	ieee->associate_seq++;
	
	auth->status=0; //success
	
	return skb;
	
}

inline struct sk_buff *ieee80211_probe_req(struct ieee80211_device *ieee)
{
	unsigned int len;
	u8 *tag;
	struct sk_buff *skb;
	struct ieee80211_probe_request *req;
	len = strlen(ieee->ssid);
	skb = dev_alloc_skb(sizeof(struct ieee80211_probe_request) +
			    2 + len + 2 + 4);
	skb->len = sizeof(struct ieee80211_probe_request) + 2 + len + 2 + 4;

	req = (struct ieee80211_probe_request *)skb->data;
	req->header.frame_control = cpu_to_le16(IEEE802_11_STYPE_PROBE_REQ);
	req->header.duration_id = 0x013a;
	memset(req->header.addr1, 0xff, ETH_ALEN);
	memcpy(req->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
	memset(req->header.addr3, 0xff, ETH_ALEN);
	req->header.seq_ctrl = cpu_to_le16(ieee->seq_ctrl << 4);

	if(ieee->seq_ctrl == 0xFFF)
		ieee->seq_ctrl =0;
	else
		ieee->seq_ctrl++;

	tag = ((u8 *)req) + sizeof(struct ieee80211_probe_request);
	
	*tag++ = MFIE_TYPE_SSID;
	*tag++ = len;
	memcpy(tag, ieee->ssid, len);
	tag += len;
	*tag++ = MFIE_TYPE_RATES;
	*tag++ = 4;
	*tag++ = 0x82;
	*tag++ = 0x84;
	*tag++ = 0x8b;
	*tag++ = 0x96;

	return skb;
}

static struct sk_buff* ieee80211_probe_resp(struct ieee80211_device *ieee, u8 *dest)
{
	char *ssid = ieee->master_essid;
	u8 *tag;
	int beacon_size;
	short encrypt;
	struct ieee80211_crypt_data* crypt;
	
	struct ieee_tx_beacon *beacon_buf;
	struct sk_buff *skb;
	
	beacon_size= sizeof(struct ieee_tx_beacon)+strlen(ssid)+11;
	
	//beacon_buf = kmalloc(beacon_size);
	skb=dev_alloc_skb(beacon_size+1);
	beacon_buf = (struct ieee_tx_beacon*) skb->data;
	
	memcpy (beacon_buf->addr1,dest,ETH_ALEN);
	memcpy (beacon_buf->addr2,ieee->dev->dev_addr,ETH_ALEN);
	memcpy (beacon_buf->addr3,ieee->beacon_cell_ssid,ETH_ALEN);

	
	//b->seq_ctrl
	//b->timestamp;
	beacon_buf->duration_id = 0x314;
	beacon_buf->interval = 
		cpu_to_le16(ieee->beacon_interval);
	beacon_buf->capability = 
		cpu_to_le16(ieee->iw_mode == IW_MODE_MASTER ? 1:2);
		
	beacon_buf->seq_ctrl= cpu_to_le16(ieee->seq_ctrl <<4);
	
	if(ieee->seq_ctrl == 0xFFF)
		ieee->seq_ctrl =0;
	else
		ieee->seq_ctrl++;
	
	;
	if(ieee->host_encrypt)
		crypt = ieee->crypt[ieee->tx_keyidx];
	else crypt = NULL;
	
	encrypt = ( crypt && crypt->ops);
	   
	if(ieee->hw_wep || encrypt)
			
		beacon_buf->capability |= BEACON_CAPABILITY_WEP;
	else
		beacon_buf->capability &= ~BEACON_CAPABILITY_WEP;		
	
		
	beacon_buf->frame_control = cpu_to_le16(IEEE802_11_STYPE_PROBE_RESP);
	
	tag = (u8*) beacon_buf + sizeof(struct ieee_tx_beacon);
	
	*(tag++) = 0; /* tag 0: ssid */
	*(tag++) = strlen(ssid);
	memcpy(tag,ssid,strlen(ssid));
	tag+=strlen(ssid);
	
	*(tag++) = 1; /* tag 1: rates */
	*(tag++) = 4;
	*(tag++) = 0x82;
	*(tag++) = 0x84;
	*(tag++) = 0xb;
	*(tag++) = 0x16;
	
	*(tag++) = 3; /* tag 3: channel */
	*(tag++) = 1;
	*(tag++) = ieee->master_chan;
	
	skb->len = beacon_size;
	skb->dev = ieee->dev;
	return skb;
}


struct sk_buff* ieee80211_assoc_resp(struct ieee80211_device *ieee, u8 *dest)
{
	struct sk_buff *skb;
	u8* tag;
	struct ieee80211_crypt_data* crypt;
	struct ieee80211_association_response_hdr *assoc;
	short encrypt;
	int len = sizeof(struct ieee80211_association_response_hdr)+6;
	skb=dev_alloc_skb(len+1); 
	
	assoc = (struct ieee80211_association_response_hdr *)
		skb_put(skb,sizeof(struct ieee80211_association_response_hdr));
	
	assoc->frame_control = cpu_to_le16(0x10);
	memcpy(assoc->addr1,dest,ETH_ALEN);
	memcpy(assoc->addr3,ieee->dev->dev_addr,ETH_ALEN);
	memcpy(assoc->addr2,ieee->dev->dev_addr,ETH_ALEN);
	assoc->capability = cpu_to_le16(ieee->iw_mode == IW_MODE_MASTER ? 1:2);
	assoc->seq= cpu_to_le16(ieee->seq_ctrl <<4);
	
	if(ieee->seq_ctrl == 0xFFF)
		ieee->seq_ctrl =0;
	else
		ieee->seq_ctrl++;
	
	if(ieee->host_encrypt)
		crypt = ieee->crypt[ieee->tx_keyidx];
	else crypt = NULL;
	
	encrypt = ( crypt && crypt->ops);
	   
	if(ieee->hw_wep || encrypt)
			
		assoc->capability |= BEACON_CAPABILITY_WEP;
	else
		assoc->capability &= ~BEACON_CAPABILITY_WEP;		
	
	assoc->status = 0;
	assoc->id = cpu_to_le16(ieee->assoc_id);
	if(ieee->assoc_id == 0xffff) ieee->assoc_id=0;
	else ieee->assoc_id++;
	tag = (u8*) skb_put(skb,6);
	tag[0] = 1;
	tag[1] = 4;
	tag[2] = 0x82;
	tag[3] = 0x84;
	tag[4] = 0xb;
	tag[5] = 0x16;
	
	return skb;
}

struct sk_buff* ieee80211_auth_resp(struct ieee80211_device *ieee,int status, u8 *dest)
{
	struct sk_buff *skb;
	struct ieee80211_authentication *auth;
	
	skb=dev_alloc_skb(sizeof(struct ieee80211_authentication)+1); 
	skb->len=sizeof(struct ieee80211_authentication);
	
	auth = (struct ieee80211_authentication *)skb->data;
	
	auth->status = cpu_to_le16(status);
	auth->transaction = cpu_to_le16(2);
	auth->algorithm = cpu_to_le16(WLAN_AUTH_OPEN);
	auth->header.seq_ctrl= cpu_to_le16(ieee->seq_ctrl <<4);
	
	if(ieee->seq_ctrl == 0xFFF)
		ieee->seq_ctrl =0;
	else
		ieee->seq_ctrl++;
	
	memcpy(auth->header.addr3,ieee->dev->dev_addr,ETH_ALEN);
	memcpy(auth->header.addr2,ieee->dev->dev_addr,ETH_ALEN);
	memcpy(auth->header.addr1,dest, ETH_ALEN);
	auth->header.frame_control=cpu_to_le16(0xb0);
	return skb;
	
	
}


void ieee80211_resp_to_assoc_rq(struct ieee80211_device *ieee, u8* dest)
{
	struct sk_buff *buf=ieee80211_assoc_resp(ieee, dest);
	ieee->func->tx_80211(buf, ieee->dev);
}


void ieee80211_resp_to_auth(struct ieee80211_device *ieee, int s, u8* dest)
{
	struct sk_buff *buf=ieee80211_auth_resp(ieee, s, dest);
	ieee->func->tx_80211(buf, ieee->dev);
}


void ieee80211_resp_to_probe(struct ieee80211_device *ieee, u8 *dest)
{
	
	struct sk_buff *buf=ieee80211_probe_resp(ieee, dest);
	 
	ieee->func->tx_80211(buf, ieee->dev);
}


inline struct sk_buff *ieee80211_association_req(struct ieee80211_beacon *beacon,struct ieee80211_device *ieee)
{
	struct sk_buff *skb;
	
	struct ieee80211_association_request_hdr *hdr;
	u8 *tag;
	
	int len=sizeof(struct ieee80211_association_request_hdr)+
				+ 2  + beacon->ssid_len//essid tagged val
				+ 2  + beacon->rates_len;//rates tagged val
				
	skb=dev_alloc_skb(len+1); 
	skb->len=len;
	hdr = (struct ieee80211_association_request_hdr *)skb->data;
	tag = (u8*)skb->data + sizeof(struct ieee80211_association_request_hdr);
	
	hdr->frame_control=0x0;
	hdr->duration= 37;
	memcpy(hdr->addr1,beacon->bssid,ETH_ALEN);
	memcpy(hdr->addr2,ieee->dev->dev_addr,ETH_ALEN);
	memcpy(hdr->addr3,beacon->bssid,ETH_ALEN);
	
	hdr->seq= cpu_to_le16(ieee->seq_ctrl <<4);
	
	if(ieee->seq_ctrl == 0xFFF)
		ieee->seq_ctrl =0;
	else
		ieee->seq_ctrl++;
	
	hdr->capability=0x1;
	if( beacon->capability & WLAN_CAPABILITY_PRIVACY ) hdr->capability |= WLAN_CAPABILITY_PRIVACY;
	hdr->listen_interval=0xa;
	
	tag[0]= 0; //ssid
	tag[1]= beacon->ssid_len;
	memcpy(tag+2,beacon->ssid,beacon->ssid_len);
	tag=tag +2+beacon->ssid_len;
	tag[0]=1; //rates
	tag[1]=4;
	tag[2]=0x82;
	tag[3]=0x84;
	tag[4]=0x8b;
	tag[5]=0x96;
	return skb;
}

void ieee80211_associate_step1(struct ieee80211_device *ieee)
{
	struct ieee80211_beacon *beacon = ieee->ass_beacon;
	struct sk_buff *skb;
	//int err;
	//del_timer_sync(&priv->scan_timer);
	ieee->associate_seq=1;
	ieee->seq_ctrl=0;
	IEEE80211DMESG("Stopping scan");
	ieee->func->stop_scan(ieee->dev);
	ieee->func->set_chan(ieee->dev,beacon->channel);
	ieee->ieee_stats.tx_aut++;
	skb=ieee80211_authentication_req(beacon,ieee);
	ieee->associate_state = ASSOC_AUTH ;
	IEEE80211DMESG("Sending authentication request");
	ieee->func->tx_80211(skb,ieee->dev);
	ieee->associate_timer.expires = jiffies + (HZ / 2);
	add_timer(&ieee->associate_timer);
	
}
	
void ieee80211_associate_abort_(unsigned long dev)
{
	ieee80211_associate_abort((struct ieee80211_device *) dev);
}


void ieee80211_associate_abort(struct ieee80211_device *ieee)
{
		ieee->associate_seq++;
		ieee->link_state=WLAN_LINK_NONE;
		ieee80211_r8180_flush_scan(ieee);
		
		if(ieee->associate_state == ASSOC_AUTH){
			IEEE80211DMESG("Authentication failed"); 
			ieee->ieee_stats.aut_noresp++;
		}else{
			IEEE80211DMESG("Association failed"); 
			ieee->ieee_stats.ass_noresp++;
		}
		ieee->func->start_scan(ieee->dev);
		return;	
	
}

void ieee80211_associate_step2(struct ieee80211_device *ieee)
{
	struct sk_buff* skb;
	struct ieee80211_beacon *beacon = ieee->ass_beacon;
	del_timer_sync(&ieee->associate_timer);
	IEEE80211DMESG("Sending association request");
	ieee->ieee_stats.tx_ass++;
	skb=ieee80211_association_req(beacon,ieee);
	ieee->func->tx_80211(skb,ieee->dev);
	ieee->associate_timer.expires = jiffies + (HZ / 2);
	add_timer(&ieee->associate_timer);
	
}

void ieee80211_probe_hidden(struct ieee80211_device *ieee)
{
	struct sk_buff *skb;
	//IEEE80211DMESG("Sending Probe Request.");
	skb = ieee80211_probe_req(ieee);
	ieee->func->tx_80211(skb, ieee->dev);
	ieee->ieee_stats.tx_probe_rq++;
}

void ieee80211_associate_complete(struct ieee80211_device *ieee)
{
	struct ieee80211_beacon *beacon = ieee->ass_beacon;
	
	del_timer_sync(&ieee->associate_timer);
	ieee->seq_ctrl=0;
	ieee->link_state=WLAN_LINK_ASSOCIATED;
	IEEE80211DMESG("Successfully associated");
	
	ieee->func->associate(ieee->dev,beacon); 
	ieee->func->data_poll_hard_resume(ieee->dev);
}



void ieee80211_r8180_set_master_essid(struct ieee80211_device *ieee,char* essid, int len)
{
	if(ieee->master_essid) kfree(ieee->master_essid);
	ieee->master_essid = kmalloc(len+1,GFP_KERNEL);
	strncpy(ieee->master_essid, essid, len);
	ieee->master_essid[len] = '\0';
}


inline short is_broadcast(u8 *adr)
{
	static u8 ff[] = { 0xff,0xff,0xff,0xff,0xff,0xff};
	return (0 == memcmp(ff,adr,ETH_ALEN));
}


inline short is_multicast(u8 *adr)
{
	//FIXME: STUB
	return 0;
}



short is_in_htable(struct ieee80211_device *ieee,u8 *adr,struct mac_htable_t *htable[])
{
	struct mac_htable_t *list;
	for (list = htable[adr[ETH_ALEN - 1] % MAC_HTABLE_ENTRY]; list!=NULL; list = list->next){
		if(0==memcmp(list->adr,adr,ETH_ALEN)) return 1; 
	} 
	return 0;
	
}


short add_htable(struct ieee80211_device *ieee,u8 *adr,struct mac_htable_t *htable[]){
	
	struct mac_htable_t *list;
	list = htable[adr[ETH_ALEN - 1] % MAC_HTABLE_ENTRY];
//	IEEE80211DMESG("inserting "MACSTR" to %x table",MAC2STR(adr),htable);
	if(list == NULL){
//		IEEE80211DMESG("list was null");
		list = kmalloc(sizeof(struct mac_htable_t),GFP_ATOMIC);
		list->next = NULL;
		list->time = jiffies;
		memcpy(list->adr,adr,ETH_ALEN);
		htable[adr[ETH_ALEN - 1] % MAC_HTABLE_ENTRY] = list;
		return 0;
	}
	
	while(list!=NULL){
		if(0==memcmp(list->adr,adr,ETH_ALEN)){
		 	list->time = jiffies;
			return -1;
		}
		if(list->next == NULL) break;
		list = list->next;
	}
	
	list->next = kmalloc(sizeof(struct mac_htable_t),GFP_ATOMIC);
	list->next->next = NULL;
	list->next->time = jiffies;
	memcpy(list->next->adr,adr,ETH_ALEN);
	return 0;
}

inline short is_associated(struct ieee80211_device *ieee,u8 *adr)
{
	return is_in_htable(ieee,adr,ieee->assoc_htable);
}


inline short is_bridged(struct ieee80211_device *ieee,u8 *adr)
{
	return is_in_htable(ieee,adr,ieee->bridge_htable);
}


inline short add_associate(struct ieee80211_device *ieee,u8 *adr)
{
	return add_htable(ieee,adr,ieee->assoc_htable);
}


inline short add_bridged(struct ieee80211_device *ieee,u8 *adr)
{
	return add_htable(ieee,adr,ieee->bridge_htable);
}




void init_htables(struct ieee80211_device *ieee)
{
	int i;
	
	for (i=0; i<MAC_HTABLE_ENTRY; i++){
		ieee->assoc_htable[i] = NULL;
	}
	
	for (i=0; i<MAC_HTABLE_ENTRY; i++){
		ieee->bridge_htable[i] = NULL;
	} 
}


struct ieee80211_device *ieee80211_r8180_alloc(struct net_device *dev,
					 void *priv)
{
	struct ieee80211_device *ieee = kmalloc(
		sizeof(struct ieee80211_device), GFP_KERNEL);
	if (ieee == NULL)
		return NULL;
	memset(ieee, 0, sizeof(*ieee));
	ieee->dev = dev;
	ieee->priv = priv;

#ifndef CONFIG_IEEE80211_NOWEP
	/* Default to enabling full open WEP with host based encrypt/decrypt */
	ieee->open_wep = 1;
	ieee->host_encrypt = 1;
	ieee->host_decrypt = 1;
	ieee->queue_stop=1;
	ieee->tx_pending.txb=NULL;
	INIT_LIST_HEAD(&ieee->crypt_deinit_list);
	init_timer(&ieee->crypt_deinit_timer);
	ieee->crypt_deinit_timer.data = (unsigned long)ieee;
	ieee->crypt_deinit_timer.function = ieee80211_r8180_crypt_deinit_handler;
		
#else
	ieee->open_wep = 1;
	ieee->host_encrypt = 0;
	ieee->host_decrypt = 0;
#endif

	
	ieee->ieee_stats.aut_noresp=0;
	ieee->ieee_stats.ass_noresp=0;
	ieee->ieee_stats.tx_aut=0;
	ieee->ieee_stats.tx_ass=0;
	ieee->ieee_stats.rx_ass_ok=0;
	ieee->ieee_stats.rx_aut_ok=0;
	ieee->ieee_stats.rx_aut_err=0;
	ieee->ieee_stats.rx_ass_err=0;
	ieee->ieee_stats.rx_assoc_rq=0;
	ieee->ieee_stats.rx_auth_rq=0;
	ieee->ieee_stats.swtxstop=0;
	ieee->ieee_stats.swtxawake=0;
	ieee->ieee_stats.reassoc=0;
	memset(ieee->ap,0, ETH_ALEN);
	/* Default fragmentation threshold is maximum payload size */
	ieee->fts = 2342;
	ieee->master_essid = NULL;
	spin_lock_init(&ieee->lock);
	spin_lock_init(&ieee->irq_lock);
	ieee->assoc_id =0;
	INIT_LIST_HEAD(&ieee->beacons);
	init_timer(&ieee->associate_timer);
	ieee->associate_timer.data = (unsigned long)ieee;
	ieee->associate_timer.function = ieee80211_associate_abort_;
/*	init_waitqueue_head(&ieee->assoc_queue);
	ieee->workqueue = create_workqueue(DRV_NAME);
	INIT_WORK(&ieee->associate_tasklet,(void(*)(void*)) ieee80211_associate_tasklet,ieee);
*/	
	init_htables(ieee);
	
	return ieee;
}

void ieee80211_r8180_free(struct ieee80211_device *ieee)
{
#ifndef CONFIG_IEEE80211_NOWEP
	int i;
#endif
	//unsigned long flags;

	if (!ieee)
		return;
	
		
	 
#ifndef CONFIG_IEEE80211_NOWEP

	// PR: FIXME: added this "if" because hostap had it, is it really needed?
	if (timer_pending(&ieee->crypt_deinit_timer))
		del_timer(&ieee->crypt_deinit_timer);
	ieee80211_r8180_crypt_deinit_entries(ieee, 1);

	for (i = 0; i < WEP_KEYS; i++) {
		struct ieee80211_crypt_data *crypt = ieee->crypt[i];
		if (crypt) {
			if (crypt->ops)
				crypt->ops->deinit(crypt->priv);
			kfree(crypt);
			ieee->crypt[i] = NULL;
		}
	}
#endif
	ieee80211_r8180_flush_scan(ieee);
	
	if (timer_pending(&ieee->associate_timer))
		del_timer(&ieee->associate_timer);
	
	kfree(ieee);
}


void ieee80211_r8180_flush_scan(struct ieee80211_device *ieee)
{
	struct list_head *b,*delprev,*prev,*next;
	
	delprev = NULL;	
	
	list_for_each(b,&ieee->beacons){
			
		if (delprev){
			kfree(list_entry(delprev,struct ieee80211_beacon,list));
			delprev=NULL;
		}
			
		//beacon=list_entry(b,struct ieee80211_beacon,list);
		prev = b->prev;
		next = b->next;
		next->prev = prev;
		prev->next = next;
		delprev=b;	
	}
	
	if (delprev){
		kfree(list_entry(delprev,struct ieee80211_beacon,list));
		delprev=NULL;
	}	
}


void ieee80211_new_net(struct ieee80211_device *ieee, struct ieee80211_beacon *beacon)
{
	u8 zero[] = {0,0,0,0,0,0};
	short apset,ssidset,ssidbroad,apmatch,ssidmatch;
	
	if(ieee->iw_mode == IW_MODE_INFRA){
		
		/* if the user specified the AP MAC, we need also the essid
		 * This could be obtained by beacons or, if the network does not
		 * broadcast it, it can be put manually.
		 */
		apset = (memcmp(ieee->ap, zero,ETH_ALEN)!=0 );
		ssidset = ieee->ssid[0] != '\0';
		ssidbroad =  !(beacon->ssid_len == 0 || beacon->ssid[0]== '\0');
		apmatch = (memcmp(ieee->ap, beacon->bssid,ETH_ALEN)==0);
		ssidmatch = (0==strncmp(ieee->ssid, beacon->ssid,beacon->ssid_len));
		
		
		
		if(	/* if the user set the AP check if match.
		         * if the network does not broadcast essid we check the user supplyed ANY essid
			 * if the network does broadcast and the user does not set essid it is OK
			 * if the network does broadcast and the user did set essid chech if essid match
			 */
			( apset && apmatch && 
				((ssidset && ssidbroad && ssidmatch) || (ssidbroad && !ssidset) || (!ssidbroad && ssidset)) ) ||  
			/* if the ap is not set, check that the user set the bssid
			 * and the network does bradcast and that those two bssid matches
			 */ 
			(!apset && ssidset && ssidbroad && ssidmatch) 
			){
			
			
			IEEE80211DMESG( "Associating with %s",ieee->ssid);
			ieee->ass_beacon = beacon;
			
			/* if the essid is hidden replace it with the
			 * essid provided by the user.
			 */
			if(!ssidbroad){
				strcpy(ieee->ass_beacon->ssid, ieee->ssid);
				ieee->ass_beacon->ssid_len = strlen(ieee->ssid);
			}
			
			/* if the user provided only the AP mac but not the essid then
			 * copy it from the broadcasted to the nic current essid
			 */
			 
			if(apset && !ssidset)
				strncpy(ieee->ssid, beacon->ssid, beacon->ssid_len);
			
			
			ieee->link_state=WLAN_LINK_ASSOCIATING;	
			ieee80211_associate_step1(ieee);
		}
	}else if (ieee->iw_mode == IW_MODE_ADHOC){
		if(beacon->ssid_len== strlen(ieee->master_essid) && 
			0==memcmp(ieee->master_essid, beacon->ssid,beacon->ssid_len)){
			
				ieee->beacon_interval = beacon->interval;
				memcpy(ieee->beacon_cell_ssid, beacon->bssid, ETH_ALEN);
				/* at the very last we check if the user has 
				 * forced manually the channel while we was
				 * scanning
				 */
				if(ieee->link_state == WLAN_LINK_ASSOCIATED)
					return;
				ieee->link_state = WLAN_LINK_ASSOCIATED;		
				ieee->master_chan = beacon->channel;

		}
	}
	//kfree(beacon);
}

static int __init ieee80211_r8180_init(void)
{
	printk(KERN_INFO MODULE_NAME ": loading with WEP "
#ifdef CONFIG_IEEE80211_NOWEP
	       "disabled"
#else
	       "enabled"
#endif
	       ".\n");

	return 0;
}

static void __exit ieee80211_r8180_deinit(void)
{
	printk(KERN_INFO MODULE_NAME ": unloading.\n");
}

EXPORT_SYMBOL(ieee80211_r8180_alloc);
EXPORT_SYMBOL(ieee80211_r8180_free);
EXPORT_SYMBOL(ieee80211_r8180_set_master_essid);
EXPORT_SYMBOL(ieee80211_r8180_flush_scan);
EXPORT_SYMBOL(ieee80211_probe_hidden);
module_init(ieee80211_r8180_init);
module_exit(ieee80211_r8180_deinit);

