/* $Id: vnetusba.c,v 1.5 2003/12/20 16:31:18 pb Exp $ */
/***************************************************************************************
	Copyright 2000-2001 ATMEL Corporation.
	
	This file is part of atmel wireless lan drivers.

    Atmel wireless lan drivers is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Atmel wireless lan drivers 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 Atmel wireless lan drivers; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

**************************************************************************************/
//jal: there might be a makefile compiling all c source in one call ...
#ifndef MODULE
# define MODULE
#endif

#include <linux/module.h>
#include <linux/wireless.h>
#include <linux/kmod.h>		/* for call_usermodehelper only */
#include <linux/if_arp.h>
#include <net/iw_handler.h>
#include "vnetusba.h"
#include <linux/string.h>
#include "vnetioctl.h"
#include "config.h"

#ifdef CONFIG_IPAQ_HANDHELD
#include <asm/mach-types.h>
#include <asm/arch/ipaq.h>
#include <asm/arch-pxa/h5400-asic.h>
#endif

struct usb_device_id __devinitdata vnet_usb_id[] =
#if defined(RFMD)
    RFMD_DEVICES
#elif defined(R505)    
    R505_DEVICES
#elif defined(R505_2958)    
    R505_2958_DEVICES
#endif
;

/* the number of entries in array above */
int const vnet_usb_id_len =
    sizeof(vnet_usb_id) / sizeof(struct usb_device_id);

/* forward for vnet_driver */
static void usb_vnet_disconnect(struct usb_device *dev, void *ptr);
static void *usb_vnet_probe(struct usb_device *dev, UINT interface,
			    const struct usb_device_id *id_table);

struct usb_driver vnet_driver = {
#if defined(RFMD)
	name:"usbvnetr",
#elif defined(R505)             
	name:"usbvnet5",
#elif defined(R505_2958)             
	name:"usbvnet5_2958",
#endif
	probe:usb_vnet_probe,
	disconnect:usb_vnet_disconnect,
	id_table:vnet_usb_id,
};

/* == PROC ssid2str ==
   converts a SSID into a zero terminated string in the given buffer */
char *ssid2str(char *buf, char *ssid, int len)
{
	assert(len <= ESSID_SIZE);
	if (len > ESSID_SIZE)
		len = ESSID_SIZE;
	memcpy(buf, ssid, len);
	buf[len] = '\0';

	return buf;
}

/* == PROC buf2str ==
   hexdump a memory area into a given buffer, zero terminated */
char *buf2str(char *str, void *buf, int len)
{
	unsigned char *ptr = buf;
	char *retval = str;
	while (len--) {
		*str++ = BIN2HEX(*ptr >> 4);
		*str++ = BIN2HEX(*ptr & 0xf);
		ptr++;
	}
	*str = '\0';
	return retval;
}


void mod_use(UCHAR a, PVNet_ADAPTER Adapter)
{
	if (a) {
		Adapter->CallbacksPending++;
		MOD_INC_USE_COUNT;
	} else {
		Adapter->CallbacksPending--;
		MOD_DEC_USE_COUNT;
	}
}

void ChangeState(PVNet_ADAPTER Adapter, UCHAR State)
{
		if ((Adapter->StationState == STATION_STATE_READY) 
        && (Adapter->StationState != State)){
			if (!(Adapter->flags & TX_STOPPED)) 
				netif_stop_queue(Adapter->net);
				Adapter->flags |= TX_STOPPED;
			} else if ((Adapter->StationState != State) 
		    && (State == STATION_STATE_READY)) {
			if (Adapter->flags & TX_STOPPED) 
				netif_wake_queue(Adapter->net);
				Adapter->flags &= ~TX_STOPPED;
		}
		dbgcond(DBG_STATE, "state change %d -> %d\n",
			Adapter->StationState, State);
	Adapter->StationState = State;

#ifdef CONFIG_IPAQ_HANDHELD
	if (machine_is_h5400()) {
		if (State == STATION_STATE_READY) {
			ipaq_led_off (RED_LED);
		} else {
			ipaq_led_blink (RED_LED, 1, 2);
		}
	}
#endif
}

static int usb_vnet_open(struct net_device *dev)
{
	PVNet_ADAPTER Adapter = (PVNet_ADAPTER) dev->priv;

	if ((Adapter->StationState == STATION_STATE_INITIALIZING) ||
	    (Adapter->StationState == STATION_STATE_FW_DOWNLOAD))
		return -1;

	Adapter->flags |= VNET_RUNNING;

	MOD_INC_USE_COUNT;

	if (Adapter->flags & STOPPED_RX)
		UsbRxInit(Adapter);

	if (Adapter->flags & TX_STOPPED) {
		netif_start_queue(Adapter->net);
		Adapter->flags &= ~TX_STOPPED;
	}
	return 0;
}

static int usb_vnet_close(struct net_device *dev)
{
	PVNet_ADAPTER Adapter = (PVNet_ADAPTER) dev->priv;

	Adapter->flags &= ~VNET_RUNNING;
#if defined (R505) || defined (RFMD) || defined (R505_2958)
    if (MOD_IN_USE > 2) {
        MOD_DEC_USE_COUNT;
    }
#else
      MOD_DEC_USE_COUNT;
#endif

#ifdef CONFIG_IPAQ_HANDHELD
	if (machine_is_h5400()) {
		ipaq_led_off (RED_LED);
		ipaq_led_off (RED_LED_2);
	}
#endif

	info("Device %s closed", dev->name);
	return 0;
}

static struct net_device_stats *usb_vnet_net_stats(struct net_device *dev)
{
	if (!dev->priv)
		return NULL;
	return ((PVNet_ADAPTER) dev->priv)->netstats;
}

static struct iw_statistics *wireless_stats(struct net_device *dev)
{
	PVNet_ADAPTER Adapter = (PVNet_ADAPTER) dev->priv;

	Adapter->w_stats->qual.qual = (40 - Adapter->LinkQuality) * 10 / 4;
	Adapter->w_stats->qual.level =
	    ((Adapter->Rssi > 40) ? 40 : Adapter->Rssi) * 10 / 4;
	Adapter->w_stats->qual.updated = 1;

	return (Adapter->w_stats);
}

static int translate_scan_results(struct iwreq *wrq, PVNet_ADAPTER Adapter)
{
	char *current_ev = wrq->u.data.pointer;
	char *end_buf = wrq->u.data.pointer + IW_SCAN_MAX_DATA;
	char *current_val;
	int i;

	for (i = 0; i < Adapter->SiteS.BSSInList; i++) {
		BSS_INFO *bss;
		struct iw_event iwe;

		bss = &Adapter->SiteS.BssInfo[i];

		/* First entry *MUST* be the AP MAC address */
		memset(&iwe, 0, sizeof(iwe));
		iwe.cmd = SIOCGIWAP;
		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
		memcpy(iwe.u.ap_addr.sa_data, bss->BSSID, ETH_ALEN);

		/* FIX:
		 * I do not know how this is possible, but iwe_stream_add_event
		 * seems to re-order memcpy execution so that len is set only
		 * after copying.. Pre-setting len here "fixes" this, but real
		 * problems should be solved (after which these iwe.len
		 * settings could be removed from this function). */
		iwe.len = IW_EV_ADDR_LEN;
		current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
						  IW_EV_ADDR_LEN);

		/* Other entries will be displayed in the order we give them */

		memset(&iwe, 0, sizeof(iwe));
		iwe.cmd = SIOCGIWESSID;
		iwe.u.data.length = le16_to_cpu(bss->SSIDsize);
		if (iwe.u.data.length > 32)
			iwe.u.data.length = 32;
		iwe.u.data.flags = 1;
		iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
		current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
						  bss->SSID);		

		memset(&iwe, 0, sizeof(iwe));
		iwe.cmd = SIOCGIWFREQ;
		iwe.u.freq.m = bss->Channel;
		iwe.u.freq.e = 0;
		iwe.len = IW_EV_FREQ_LEN;
		current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
						  IW_EV_FREQ_LEN);

		memset(&iwe, 0, sizeof(iwe));
		iwe.cmd = SIOCGIWENCODE;
		if (bss->UsingWEP)
			iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
		else
			iwe.u.data.flags = IW_ENCODE_DISABLED;
		iwe.u.data.length = 0;
		iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
		current_ev = iwe_stream_add_point(
			current_ev, end_buf, &iwe,
			bss->SSID /* memcpy 0 bytes */);

		memset(&iwe, 0, sizeof(iwe));
		iwe.cmd = IWEVQUAL;
		iwe.u.qual.level = bss->RSSI;
		iwe.len = IW_EV_QUAL_LEN;
		current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
						  IW_EV_QUAL_LEN);
	}

	wrq->u.data.length = current_ev - wrq->u.data.pointer;

	return 0;
}

static int usb_vnet_ioctl(struct net_device *dev, struct ifreq *ifr,
			  int cmd)
{
	PVNet_ADAPTER Adapter = (PVNet_ADAPTER) dev->priv;
	struct iwreq *wrq = (struct iwreq *) ifr;
	UCHAR old_EncL = Adapter->WepInfo.EncryptionLevel;
	DEVICE_CONFIGURATION DevConfig;
	VERSION_INFO VersionInfo;
	unsigned flags;
	int AP_Selected, i=0;

	dbgcond(DBG_IOCTL, "cmd x%x StationState %d\n",
	cmd, Adapter->StationState);
      if (!capable (CAP_NET_ADMIN))
        {
          return -EPERM;
        }

	if ((Adapter->StationState == STATION_STATE_INITIALIZING) ||
	    (Adapter->StationState == STATION_STATE_FW_DOWNLOAD)) {
		printk("Device not ready yet\n");
		return -EIO;
	}

	if (Adapter->flags & IOCTL_SLEEPING) {
		printk(KERN_INFO "ioctl already pending\n");
		return -EINPROGRESS;
	}
	spin_lock_irqsave(Adapter->lock, flags);
	UpdateAppInfo(Adapter, &DevConfig);

	switch (cmd) {

	case SIOCGIWNAME:
#ifdef RFMD
		strcpy(wrq->u.name, "ATMEL RFMD503A");
#elif defined(R505)                
		strcpy(wrq->u.name, "ATMEL RFMD505");
#elif defined(R505_2958)                
		strcpy(wrq->u.name, "ATMEL 2958505");
#endif
		break;
	case SIOCDEVPRIVATE:
		switch (wrq->u.data.flags) {
		case GET_SUPPORT_INFO:
			// Give our configuration to the user
			wrq->u.data.length = sizeof(DEVICE_CONFIGURATION);
			copy_to_user(wrq->u.data.pointer, &DevConfig,
				     wrq->u.data.length);
			break;
		case SET_SUPPORT_INFO:
			// Set config we got from the user
			copy_from_user(&DevConfig, wrq->u.data.pointer,
				       wrq->u.data.length);
			if (!ValidateChannel(Adapter, DevConfig.Channel)) {
				dbgcond(DBG_IOCTL,
					"Trying to set invalid channel %d",
					DevConfig.Channel);
				return -EINVAL;
			}
			ChangeConfiguration(Adapter, &DevConfig);
			break;
		case GET_WEP_INFO:
			{
				WEP_INFO TmpWep;
				wrq->u.data.length = sizeof(WEP_INFO);
				memcpy(&TmpWep, &Adapter->WepInfo,
				       wrq->u.data.length);
				if (Adapter->PrivacyInvoked == 0)
					TmpWep.EncryptionLevel = 0;
				copy_to_user(wrq->u.data.pointer, &TmpWep,
					     wrq->u.data.length);
			}
			break;
		case SET_WEP_INFO:
			copy_from_user(&Adapter->WepInfo,
				       wrq->u.data.pointer,
				       wrq->u.data.length);
			if (Adapter->WepInfo.EncryptionLevel == 0) {
				Adapter->PrivacyInvoked = 0;
				Adapter->WepInfo.EncryptionLevel =
				    old_EncL;
			} else
				Adapter->PrivacyInvoked = 1;

			dbgcond(DBG_IOCTL, "AuthType %d WepKey1[0]=%02x\n",
				Adapter->WepInfo.AuthenticationType,
				Adapter->WepInfo.WepKey1[0]);

			Adapter->flags &= ~WepIsSet;

			if (Adapter->StationState ==
			    STATION_STATE_SCANNING)
				break;

			spin_unlock_irqrestore(Adapter->lock, flags);
			if (Adapter->flags & GetCommandPedding) {
				DECLARE_WAITQUEUE(wait, current);
				add_wait_queue(Adapter->ctrl_wait, &wait);
				set_current_state(TASK_INTERRUPTIBLE);
				Adapter->flags |= IOCTL_SLEEPING;
				schedule();
				set_current_state(TASK_RUNNING);
				remove_wait_queue(Adapter->ctrl_wait,
						  &wait);
				if (Adapter->flags & WAKE_UP_WITH_ERROR) {
					Adapter->flags &= ~IOCTL_SLEEPING;
					return -1;	/* jal: real error code ??? */
				}
			}
			dbgcond(DBG_IOCTL, "Setting Wep (%lu)\n",
				Adapter->Stats.RxDataPacketsOk);
			DropPendingTxPackets(Adapter);
			dbgcond(DBG_IOCTL, "Tx Packets Dropped\n");
			SetWEPvalue(Adapter);
			dbgcond(DBG_IOCTL, "out\n");
			return 0;
		case GET_VERSION_INFO:
			VersionInfo.DriverMajorVersion =
			    USB_VNET_MAJOR_VERSION;
			VersionInfo.DriverMinorVersion =
			    USB_VNET_MINOR_VERSION;
			VersionInfo.DriverSubVersion =
			    USB_VNET_SUB_VERSION;
			VersionInfo.DriverBuild = USB_VNET_BUILD;
			VersionInfo.FwMajorVersion =
			    (USHORT) Adapter->FwVersion[0];
			VersionInfo.FwMinorVersion =
			    (USHORT) Adapter->FwVersion[1];
			VersionInfo.FwSubVersion = (USHORT) Adapter->FwVersion[2];
			VersionInfo.FwBuild =
			    (USHORT) Adapter->FwVersion[3];
			wrq->u.data.length = sizeof(VERSION_INFO);
			copy_to_user(wrq->u.data.pointer, &VersionInfo,
				     wrq->u.data.length);
			dbgcond(DBG_IOCTL, "net queue %s\n",
				netif_queue_stopped(Adapter->
						    net) ? "stopped" :
				"running");
			dbgcond(DBG_IOCTL,
				"status: tx_urb %d, rx_urb %d\n",
				Adapter->tx_urb->status,
				Adapter->rx_urb->status);
			break;
		case CLEAR_PACKETS:
			memset((PUCHAR) & Adapter->Stats, 0,
			       sizeof(STATISTICS));
			break;
		case GET_STATISTICS:
			wrq->u.data.length = sizeof(STATISTICS);
			copy_to_user(wrq->u.data.pointer, &Adapter->Stats,
				     wrq->u.data.length);
			break;
		case GET_MAC_ADDRESS:
			wrq->u.data.length = 6;
			copy_to_user(wrq->u.data.pointer,
				     Adapter->StationAddress,
				     wrq->u.data.length);
			break;
		case SITE_SURVEY:
			spin_unlock_irqrestore(Adapter->lock, flags);
                        if (Adapter->RadioIsOn){
                            if (Adapter->flags & GetCommandPedding) {
                                    DECLARE_WAITQUEUE(wait, current);
                                    add_wait_queue(Adapter->ctrl_wait, &wait);
                                    set_current_state(TASK_INTERRUPTIBLE);
                                    Adapter->flags |= IOCTL_SLEEPING;
                                    schedule();
                                    set_current_state(TASK_RUNNING);
                                    remove_wait_queue(Adapter->ctrl_wait,
                                                      &wait);
                                    if (Adapter->flags & WAKE_UP_WITH_ERROR) {
                                            Adapter->flags &= ~IOCTL_SLEEPING;
                                            return -1;
                                    }
                            }
                            Adapter->TempState = Adapter->StationState;
                            ChangeState(Adapter, STATION_STATE_SCANNING);
                            Adapter->flags |= SITE_SURVEY_REQUEST;
                            DropPendingTxPackets(Adapter);
                            UsbScan(Adapter);
                        }
                        break;
		case SITE_SURVEY_STATE:
			if (Adapter->StationState != STATION_STATE_SCANNING){
				*wrq->u.data.pointer = 2;	//SITE_SURVEY_COMPLETED
      }
			break;
		case GET_AP_INFO:
      if(!Adapter->RadioIsOn){
        Adapter->SiteS.BSSInList = 0;
      }
			wrq->u.data.length = Adapter->SiteS.BSSInList * sizeof(BSS_INFO);
			if (Adapter->SiteS.BSSInList == 0)
				return 0;
			copy_to_user(wrq->u.data.pointer,
				     Adapter->SiteS.BssInfo,
				     wrq->u.data.length);
			break;
		case STOP_SITE_SURVEY:
                            if(Adapter->RadioIsOn){
                                //Let's see What about Ad-hoc
                                if(Adapter->OperatingMode !=AD_HOC_MODE){
                                    if(Adapter->InternationalRoaming){
                                        for(i=0; i<14; i++){
                                            if ( Adapter->ChannelVector[i] != 0 )
                                                break;
                                        }
                                        if(i<14){
                                            if(Adapter->ErrorCode == 0xa0){
                                                    Adapter->StationState = STATION_STATE_MGMT_ERROR;
                                                return -1;
                                            } else if(Adapter->ErrorCode == 0xa1){
                                                return -1;
                                            }else{
                                                    Adapter->StationState = STATION_STATE_MGMT_ERROR;
                                                    UsbJoin(Adapter);
                                            }
                                        }else {
                                                Adapter->StationState = STATION_STATE_SCANNING;   
                                                UsbScan(Adapter);
                                        }
                                    }else {
                                            if(Adapter->ErrorCode == 0xa0){
                                                    Adapter->StationState = STATION_STATE_MGMT_ERROR;
                                                return -1;
                                            } else if(Adapter->ErrorCode == 0xa1){
                                                return -1;
                                            }else{
                                                    Adapter->StationState = STATION_STATE_MGMT_ERROR;
                                                    UsbJoin(Adapter);
                                            }
                                    }
                                } else {
                                           Adapter->StationState = STATION_STATE_READY;
                               }

                            }
			break;
		case SELECT_AP_BY_INDEX:
                            if(Adapter->RadioIsOn){
                                AP_Selected = *wrq->u.data.pointer - 1;
                                spin_unlock_irqrestore(Adapter->lock, flags);
                                if (Adapter->flags & GetCommandPedding) {
                                        DECLARE_WAITQUEUE(wait, current);
                                        add_wait_queue(Adapter->ctrl_wait, &wait);
                                        set_current_state(TASK_INTERRUPTIBLE);
                                        Adapter->flags |= IOCTL_SLEEPING;
                                        schedule();
                                        set_current_state(TASK_RUNNING);
                                        remove_wait_queue(Adapter->ctrl_wait,
                                                          &wait);
                                        if (Adapter->flags & WAKE_UP_WITH_ERROR) {
                                                Adapter->flags &= ~IOCTL_SLEEPING;
                                                return -1;
                                        }
                                }

                                Adapter->SSID_size =
                                    Adapter->SiteS.BssInfo[AP_Selected].SSIDsize;
                                Adapter->DesiredSSIDsize = Adapter->SSID_size;
                                memcpy(Adapter->SelectedSSID,
                                       Adapter->SiteS.BssInfo[AP_Selected].SSID,
                                       Adapter->SSID_size);
                                memcpy(Adapter->DesiredSSID,
                                       Adapter->SiteS.BssInfo[AP_Selected].SSID,
                                       Adapter->SSID_size);
                                memcpy((PUCHAR) Adapter->CurrentBSSID,
                                       (PUCHAR) Adapter->SiteS.
                                       BssInfo[AP_Selected].BSSID, 6);
                                //Copy the Channel of the corresponding Channel
                                Adapter->Channel = Adapter->SiteS.BssInfo[AP_Selected].Channel;
                                Adapter->OperatingMode = Adapter->SiteS.BssInfo[AP_Selected].BSStype;
                                if (Adapter->SiteS.BssInfo[AP_Selected].UsingWEP == 1 && Adapter->PrivacyInvoked == 0){
                                     Adapter->ErrorCode=0xa0;
                                     ChangeState(Adapter, STATION_STATE_MGMT_ERROR);
                                     return 0;
                                } else if (Adapter->SiteS.BssInfo[AP_Selected].UsingWEP == 0 && Adapter->PrivacyInvoked == 1){
                                         Adapter->ErrorCode=0xa1;
                                         ChangeState(Adapter, STATION_STATE_MGMT_ERROR);
                                         return 0;
                                }else {
                                Adapter->StationState = STATION_STATE_JOINING;
                                UsbJoin(Adapter);
                                }
                                return 0;
                            }
                        break;
                }
		break;
	case SIOCGIWESSID:	// Return Current ESSID to wireless tools
		wrq->u.data.flags = 1;
		if (Adapter->SSID_size == 0) {
			/* we don't camp on a beacon yet -> report the desired SSID */
			wrq->u.data.length = Adapter->DesiredSSIDsize;
			copy_to_user(wrq->u.data.pointer,
				     Adapter->DesiredSSID,
				     wrq->u.data.length);
		} else {
			/* we have found a BSS -> report its SSID (in case we had "any") */
			wrq->u.data.length = Adapter->SSID_size;
			copy_to_user(wrq->u.data.pointer,
				     Adapter->SelectedSSID,
				     wrq->u.data.length);
		}
		break;
	case SIOCGIWMODE:
		switch (Adapter->OperatingMode) {
		case AD_HOC_MODE:
			wrq->u.mode = IW_MODE_ADHOC;
			break;
		case INFRASTRUCTURE_MODE:
			wrq->u.mode = IW_MODE_INFRA;
			break;
			// For compatibility with wireless tools
			// AdHoc in wt is 1 and for infrastructure return 0 (Auto in wt)
		}
		break;
	case SIOCGIWNWID:	//return -1. This is if ioctl failed and as a result
		spin_unlock_irqrestore(Adapter->lock, flags);
		return -EINVAL;	//wt(wireless tools) won't print a network ID    
	case SIOCGIWNICKN:	//same as above, for the nickname
		wrq->u.essid.length = Adapter->SSID_size;
		copy_to_user(wrq->u.essid.pointer, Adapter->SelectedSSID,
			     Adapter->SSID_size);
		spin_unlock_irqrestore(Adapter->lock, flags);
		return 0;
	case SIOCGIWFRAG:	//fragmentation threshold
		wrq->u.frag.value = Adapter->FragmentationThreshold;
		wrq->u.frag.fixed = 1;
		break;
	case SIOCGIWRTS:	//RTS threshold
		wrq->u.rts.value = Adapter->RtsThreshold;
		wrq->u.rts.fixed = 1;
		break;
	case SIOCGIWFREQ:	//Return m,e
		wrq->u.freq.m = Adapter->Channel;
		wrq->u.freq.e = 0;
		break;
	case SIOCGIWAP:	//Access Point Address
		memcpy(wrq->u.ap_addr.sa_data, Adapter->CurrentBSSID, 6);
                wrq->u.ap_addr.sa_family = 1;
		break;
	case SIOCGIWAPLIST:	//Access Point Address
                 copy_to_user(wrq->u.data.pointer, Adapter->SiteS.BssInfo, wrq->u.data.length);
		break;
	case SIOCGIWPOWER:	//Power Management
		spin_unlock_irqrestore(Adapter->lock, flags);
		return -EINVAL;
	case SIOCGIWENCODE:
		if (Adapter->PrivacyInvoked == 0) {
			wrq->u.data.flags |= IW_ENCODE_DISABLED;
			wrq->u.data.length = 0;
		} else {
			wrq->u.data.flags &= ~IW_ENCODE_DISABLED;
			wrq->u.data.flags |=
			    (Adapter->WepInfo.WepKeyToUse + 1);
			if (Adapter->WepInfo.AuthenticationType ==
			    C80211_MGMT_AAN_OPENSYSTEM)
				wrq->u.data.flags |= IW_ENCODE_OPEN;
			if (Adapter->WepInfo.ExcludeUnencrypted ==
			    WEP_MODE_MANDATORY)
				wrq->u.data.flags |= IW_ENCODE_RESTRICTED;
			wrq->u.data.length =
			    (Adapter->WepInfo.EncryptionLevel ==
			     WEP_64BIT) ? 5 : 13;
		}
		copy_to_user(wrq->u.data.pointer,
			     (PUCHAR) &Adapter->WepInfo + (5 +
							    (Adapter->
							     WepInfo.
							     WepKeyToUse *
							     13)),
			     wrq->u.data.length);
		break;
	case SIOCGIWRATE:	//Bitrate Used
		wrq->u.bitrate.fixed = 0;
		switch (Adapter->TxRate) {
		case 0:
			wrq->u.bitrate.value = 1e6;
			break;
		case 1:
			wrq->u.bitrate.value = 2e6;
			break;
		case 2:
			wrq->u.bitrate.value = 5.5e6;
			break;
		case 3:
			wrq->u.bitrate.value = 11e6;
			break;
		default:
			wrq->u.bitrate.value = 2e9;
			break;
		}
		break;
	case SIOCGIWSENS:	//AP Density. Currently not supported
		spin_unlock_irqrestore(Adapter->lock, flags);
		return -EINVAL;
	case SIOCGIWRANGE:
		spin_unlock_irqrestore(Adapter->lock, flags);
		return -EINVAL;
	case SIOCGIWPRIV:
	  if(wrq->u.data.pointer)
	    {
	      struct iw_priv_args priv[] =
		{ /* cmd, set_args, get_args, name */
		  {SIOCIWPRIVSPREAM, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"},
		  {SIOCIWPRIVGPREAM, 0, IW_PRIV_TYPE_CHAR | 6, "get_preamble"},
		};
	      wrq->u.data.length = sizeof(priv) / sizeof(priv[0]);
	      if (copy_to_user(wrq->u.data.pointer, priv, sizeof(priv))) {
		spin_unlock_irqrestore(Adapter->lock, flags);
		return -EFAULT;
	      }
	    }
	  break;
	case SIOCIWPRIVSPREAM:
		DevConfig.PreambleType = *( (int *) wrq->u.name ) & 1;
		ChangeConfiguration(Adapter, &DevConfig);
		break;
	case SIOCIWPRIVGPREAM:
		if (wrq->u.data.pointer) {
			wrq->u.data.flags = 1;
			if (DevConfig.PreambleType) {
				wrq->u.data.length = 6;
				if (copy_to_user(wrq->u.data.pointer, "short", 6)) {
					spin_unlock_irqrestore(Adapter->lock, flags);
					return -EFAULT;
				}
			} else {
				wrq->u.data.length = 5;
				if (copy_to_user(wrq->u.data.pointer, "long", 5)) {
					spin_unlock_irqrestore(Adapter->lock, flags);
					return -EFAULT;
				}
			}
		}
		break;
	case SIOCSIWRATE:	//Bitrate Used
		if (wrq->u.bitrate.fixed == 0) {
			spin_unlock_irqrestore(Adapter->lock, flags);
			DevConfig.TxRate = 4;
			ChangeConfiguration(Adapter, &DevConfig);
			return 0;
		}
		if ((wrq->u.bitrate.value / 1000000) > 2) {
			if ((wrq->u.bitrate.value / 1000000) > 6)
				DevConfig.TxRate = 3;
			else
				DevConfig.TxRate = 2;
		} else
			DevConfig.TxRate =
			    (wrq->u.bitrate.value / 1000000) - 1;
		ChangeConfiguration(Adapter, &DevConfig);
		break;
	case SIOCSIWESSID:	//ESSID change
		// iwconfig will report the length including the \0
		// at the end of the string so we need to decrease
		// the length by one and then set it
		memcpy(DevConfig.SSID, wrq->u.data.pointer,
		       wrq->u.data.length - 1);
		DevConfig.SSIDlength = wrq->u.data.length - 1;
		ChangeConfiguration(Adapter, &DevConfig);
		break;
	case SIOCSIWRTS:
		DevConfig.RtsCtsThreshold = wrq->u.rts.value;
		ChangeConfiguration(Adapter, &DevConfig);
		break;
	case SIOCSIWFRAG:
		DevConfig.FragmentationThreshold = wrq->u.frag.value;
		ChangeConfiguration(Adapter, &DevConfig);
		break;
	case SIOCSIWMODE:
		/* RS Addition
		   I will not try to understand this. The file appint.h defines ad_hoc_mode as 1 and
		   infrastructure mode as 2. But in order to set them they need to be respectively
		   0 and 1
		 */
		switch (wrq->u.mode) {
		case IW_MODE_ADHOC:
			DevConfig.OperatingMode = 0;
			break;
		case IW_MODE_INFRA:
			DevConfig.OperatingMode = 1;
			break;
		default:
			spin_unlock_irqrestore(Adapter->lock, flags);
			return -EINVAL;
		}
		dbgcond(DBG_IOCTL, "u.mode = %X\n", wrq->u.mode);
		ChangeConfiguration(Adapter, &DevConfig);
		break;
	case SIOCSIWFREQ:
		if (wrq->u.freq.m < 1 || wrq->u.freq.m > 14) {
			spin_unlock_irqrestore(Adapter->lock, flags);
			return -EINVAL;
		}
		DevConfig.Channel = wrq->u.freq.m;
		ChangeConfiguration(Adapter, &DevConfig);
		break;
	case SIOCSIWENCODE:
		dbgcond(DBG_IOCTL,
			"SIOCSIWENCODE called, flags x%04X, len %d\n",
			wrq->u.data.flags, wrq->u.data.length);
		spin_unlock_irqrestore(Adapter->lock, flags);
		if ((wrq->u.data.flags & IW_ENCODE_DISABLED) || (wrq->u.data.flags & IW_ENCODE_NOKEY)) 
                {
			Adapter->PrivacyInvoked = 0;
			SetWEPvalue(Adapter);
			return 0;
		}
		Adapter->PrivacyInvoked = 1;
		if (wrq->u.data.flags & IW_ENCODE_OPEN)
			Adapter->WepInfo.AuthenticationType =
			    C80211_MGMT_AAN_OPENSYSTEM;
		if (wrq->u.data.flags & IW_ENCODE_RESTRICTED)
			Adapter->WepInfo.ExcludeUnencrypted = WEP_MODE_MANDATORY;
		if ((wrq->u.data.length != 5)
		    && (wrq->u.data.length != 13))
			return -EINVAL;
		if (wrq->u.data.length == 5)
			Adapter->WepInfo.EncryptionLevel = WEP_64BIT;
		else
			Adapter->WepInfo.EncryptionLevel = WEP_128BIT;

		if (wrq->u.data.flags & IW_ENCODE_INDEX) {
			Adapter->WepInfo.WepKeyToUse = (wrq->u.data.flags & IW_ENCODE_INDEX) - 1;
			dbgcond(DBG_IOCTL, "WepKeyToUse : %d\n",
				 Adapter->WepInfo.WepKeyToUse);
			if (Adapter->WepInfo.WepKeyToUse > 3)
				return -EINVAL;
		}
		copy_from_user((PUCHAR) &Adapter->WepInfo +
			       (5 + (13 * Adapter->WepInfo.WepKeyToUse)),
			       wrq->u.data.pointer, wrq->u.data.length);

		Adapter->flags &= ~WepIsSet;
		if (Adapter->flags & GetCommandPedding) {
			DECLARE_WAITQUEUE(wait, current);
			add_wait_queue(Adapter->ctrl_wait, &wait);
			set_current_state(TASK_INTERRUPTIBLE);
			Adapter->flags |= IOCTL_SLEEPING;
			schedule();
			set_current_state(TASK_RUNNING);
			remove_wait_queue(Adapter->ctrl_wait, &wait);
			if (Adapter->flags & WAKE_UP_WITH_ERROR) {
				Adapter->flags &= ~IOCTL_SLEEPING;
				return -1;
			}
		}
		dbgcond(DBG_IOCTL, "Setting Wep (%ld)\n",
			Adapter->Stats.RxDataPacketsOk);
		DropPendingTxPackets(Adapter);
		dbgcond(DBG_IOCTL, "Tx Packets Dropped\n");
		SetWEPvalue(Adapter);
		dbgcond(DBG_IOCTL, "out\n");
		return 0;
	case SIOCSIWSCAN:
		spin_unlock_irqrestore(Adapter->lock, flags);
		if (Adapter->RadioIsOn){
			if (Adapter->flags & GetCommandPedding) {
				DECLARE_WAITQUEUE(wait, current);
				add_wait_queue(Adapter->ctrl_wait, &wait);
				set_current_state(TASK_INTERRUPTIBLE);
				Adapter->flags |= IOCTL_SLEEPING;
				schedule();
				set_current_state(TASK_RUNNING);
				remove_wait_queue(Adapter->ctrl_wait,
						  &wait);
				if (Adapter->flags & WAKE_UP_WITH_ERROR) {
					Adapter->flags &= ~IOCTL_SLEEPING;
					return -1;
				}
			}
			Adapter->TempState = Adapter->StationState;
			ChangeState(Adapter, STATION_STATE_SCANNING);
			Adapter->flags |= SITE_SURVEY_REQUEST;
			DropPendingTxPackets(Adapter);
			UsbScan(Adapter);
			return 0;
		}
		return -ENETDOWN;
	case SIOCGIWSCAN:
		if (Adapter->StationState == STATION_STATE_SCANNING)
			return -EAGAIN;
		return translate_scan_results(wrq, Adapter);
	default:
		spin_unlock_irqrestore(Adapter->lock, flags);
		dbgcond(DBG_IOCTL, "unknown ioctl x%x\n", cmd);
		return -EOPNOTSUPP;
	}
	spin_unlock_irqrestore(Adapter->lock, flags);
	return 0;
}

static int usb_vnet_set_mac(struct net_device *dev, void *addr)
{
	/*
	   PVNet_ADAPTER Adapter = (PVNet_ADAPTER)dev->priv;
	   struct sockaddr *sa = addr;        
	   UCHAR tmpMAC[6];
	   memcpy(Adapter->tmpMAC, sa->sa_data);      
	   SetMacAddress(Adapter);    */
	return 0;
}

#ifdef CONFIG_IPAQ_HANDHELD

static struct timer_list led_timer;

static void
ipaq_clear_led (unsigned long time)
{
	ipaq_led_off (RED_LED_2);
}

static void
ipaq_blink_led (void)
{
	ipaq_led_on (RED_LED_2);

	mod_timer (&led_timer, jiffies + (HZ / 25));
}

static void
ipaq_init_led (void)
{
	led_timer.function = ipaq_clear_led;

	init_timer (&led_timer);
}

#endif

static int usb_vnet_tx(struct sk_buff *skb, struct net_device *dev)
{
	PVNet_ADAPTER Adapter = (PVNet_ADAPTER) dev->priv;
	USHORT len;
	ULONG flags;
	PTX_LIST NewTxL;

	if (!Adapter) {
		Adapter->Stats.TxDataPacketsError++;
		Adapter->netstats->tx_dropped++;
		netif_stop_queue(dev);
		Adapter->flags |= TX_STOPPED;

		return NET_XMIT_DROP;
	}

	if (!netif_device_present(Adapter->net)
	    || !(Adapter->flags & VNET_RUNNING)) {
		Adapter->Stats.TxDataPacketsError++;
		Adapter->netstats->tx_dropped++;
		netif_stop_queue(dev);
		Adapter->flags |= TX_STOPPED;
		return NET_XMIT_DROP;
	}

	if (Adapter->StationState != STATION_STATE_READY)
		return -EAGAIN;

#ifdef CONFIG_IPAQ_HANDHELD
	if (machine_is_h5400())
		ipaq_blink_led();
#endif

	len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;

	spin_lock_irqsave(Adapter->lock, flags);

	if (!(NewTxL = AllocPacket(Adapter, len))) {
		spin_unlock_irqrestore(Adapter->lock, flags);
		return NET_XMIT_DROP;
	}

	NewTxL->skb = skb;

	// add the packet in the buffer (the first 12 bytes are going to be used in the
	// wireless header.
	memcpy(&NewTxL->tx_buff->WirelessPacket[18], (PUCHAR) skb->data,
	       len);

	// add the wireless header.....
	Ethernet2Wireless(Adapter, NewTxL);

	/* we are inside the Adapter->lock spinlock ... */
	DeQueueTxPacket(Adapter, NewTxL);

	// remember time of transmit
	Adapter->net->trans_start = jiffies;
	spin_unlock_irqrestore(Adapter->lock, flags);

	// packet used. Wait until callback to free sk_buff....
	return NET_XMIT_SUCCESS;
}

static void *usb_vnet_probe(struct usb_device *dev, UINT interface,
			    const struct usb_device_id *id_table)
{
	PVNet_ADAPTER Adapter;
	int i;

	/* jal: do we really need to probe as we pass an id_table
	   in vnet_driver struct ??? */
	printk(KERN_INFO "usb vendor/product=%x/%x\n",
	       dev->descriptor.idVendor, dev->descriptor.idProduct);
	for (i = 0; i < vnet_usb_id_len; i++)
		if (dev->descriptor.idVendor == vnet_usb_id[i].idVendor &&
		    dev->descriptor.idProduct == vnet_usb_id[i].idProduct)
			break;

	if (i == vnet_usb_id_len) {
		err("Device Descriptor not matching");
		return NULL;
	}

	Adapter =
	    (PVNet_ADAPTER) kmalloc(sizeof(VNet_ADAPTER), GFP_KERNEL);

	if (!Adapter) {
		err("Not enough memory");
		return NULL;
	}

	MOD_INC_USE_COUNT;
	memset((PUCHAR) Adapter, 0, sizeof(VNet_ADAPTER));

	Adapter->dbg_mask = 
#ifdef USBDBG
		0xffffffff & ~(DBG_BEACON | DBG_RXDATA | 
				DBG_DFU_CB | DBG_TXDATA | DBG_TX_CB | DBG_IOCTL | DBG_FW_DL | DBG_STATE);	/* switch on all debugs but ... */
#else
	0;
#endif

	ChangeState(Adapter, STATION_STATE_INITIALIZING);
#ifdef RFMD
	Adapter->BoardType = RFMD_Board;
#elif defined(R505)        
	Adapter->BoardType = R505_Board;
#elif defined(R505_2958)        
	Adapter->BoardType = R505_2958_Board;
#endif
	Adapter->config = dev->config;
	Adapter->usb = dev;

	if (InitDevice(Adapter) != 0) {
		err("device initialization failed");
		ChangeState(Adapter, STATION_STATE_EXITING);
		MOD_DEC_USE_COUNT;
		del_timer(Adapter->CtrlTimer);
		kfree(Adapter);
		return NULL;
	}

#ifdef CONFIG_IPAQ_HANDHELD
	if (machine_is_h5400 ())
		ipaq_init_led ();
#endif

	usb_inc_dev_use(dev);

	Adapter->BulkInAddr =
	    (Adapter->usb->actconfig->interface->altsetting->endpoint->
	     bEndpointAddress) & ~0x80;
	//    Start Polling In Bulk In Endpoint
	if (UsbRxInit(Adapter) != 0) {
		MOD_DEC_USE_COUNT;
		kfree(Adapter);
		usb_dec_dev_use(dev);
		return NULL;
	}
	dbgcond(DBG_INIT, "RxInit OK\n");
	Adapter->flags |= VNET_RUNNING;


	if (!(Adapter->net = init_etherdev(NULL, 0))) {
		MOD_DEC_USE_COUNT;
		kfree(Adapter);
		usb_dec_dev_use(dev);
		return NULL;
	}

	ether_setup(Adapter->net);
	Adapter->net->open = &usb_vnet_open;
	Adapter->net->stop = &usb_vnet_close;
	Adapter->net->hard_start_xmit = &usb_vnet_tx;
	Adapter->net->do_ioctl = &usb_vnet_ioctl;
	Adapter->net->set_mac_address = usb_vnet_set_mac;
	Adapter->net->priv = Adapter;
	Adapter->net->get_stats = &usb_vnet_net_stats;
	Adapter->net->get_wireless_stats = wireless_stats;
	Adapter->net->hard_header_len = 14;
	Adapter->net->mtu = 1500;
	Adapter->net->addr_len = 6;
	SET_MODULE_OWNER(Adapter->net);
	memcpy(Adapter->net->dev_addr, Adapter->StationAddress,
	       Adapter->net->addr_len);

	if (Adapter->net->flags & IFF_MULTICAST)
		printk("Multicast is enabled\n");
	if (Adapter->net->flags & IFF_PROMISC)
		printk("Promiscuous mode is enabled\n");

	ChangeState(Adapter, STATION_STATE_SCANNING);
        SetRadio(Adapter, TRUE);

/*	if (UsbScan(Adapter) != 0) {
		err("Scan Command failed");
		MOD_DEC_USE_COUNT;
		usb_dec_dev_use(dev);
		kfree(Adapter);
		return NULL;
	}
  */
	if (!register_netdev(Adapter->net)) {
		err("register_netdev failed");
		MOD_DEC_USE_COUNT;
		usb_dec_dev_use(dev);
		kfree(Adapter);
		return NULL;
	}

	info("usb %s initialized and registered", Adapter->net->name);
	return Adapter;
}

static void usb_vnet_disconnect(struct usb_device *dev, void *ptr)
{
	PVNet_ADAPTER Adapter = (PVNet_ADAPTER) ptr;
	ULONG flags;
	char *argv[4];

	if (!Adapter)
		return;

	netif_stop_queue(Adapter->net);

	argv[0] = "/sbin/modprobe";
	argv[1] = "-r";
	argv[2] = (char *) vnet_driver.name;
	argv[3] = 0;

	dbgcond(DBG_INIT, " device %s module %s\n", Adapter->net->name,
		argv[2]);

	Adapter->flags &= ~VNET_RUNNING;
	Adapter->StationState = STATION_STATE_EXITING;

#ifdef CONFIG_IPAQ_HANDHELD
	if (machine_is_h5400())
		del_timer(&led_timer);
#endif

	if (waitqueue_active(Adapter->ctrl_wait)) {
		Adapter->flags |= WAKE_UP_WITH_ERROR;
		wake_up_interruptible(Adapter->ctrl_wait);
	}

	spin_lock_irqsave(Adapter->lock, flags);

	DropPendingTxPackets(Adapter);

	if (timer_pending(Adapter->CtrlTimer)) {
		del_timer(Adapter->CtrlTimer);
	}

	Adapter->rx_urb->transfer_flags &= ~USB_ASYNC_UNLINK;
	Adapter->tx_urb->transfer_flags &= ~USB_ASYNC_UNLINK;
	Adapter->ctrl_urb->transfer_flags &= ~USB_ASYNC_UNLINK;

	usb_unlink_urb(Adapter->rx_urb);
	usb_unlink_urb(Adapter->tx_urb);
	usb_unlink_urb(Adapter->ctrl_urb);
	spin_unlock_irqrestore(Adapter->lock, flags);
	// Since it isn't clear if the Callback function is called whenever 
	// usb_unlink_urb is used.....(patenta).

	Adapter->flags |= TX_STOPPED;
	dbgcond(DBG_INIT, "trying to unregister net device\n");
        if(Adapter->net != NULL)
	unregister_netdev(Adapter->net);
	dbgcond(DBG_INIT, "unregistered\n");

	/* jal: TODO: remove this and look at mod_use() calls, right now
	   we are unable to have more than one USB adapter handled by this driver !!! */
	while (MOD_IN_USE > 0) {
		MOD_DEC_USE_COUNT;
	}

	usb_dec_dev_use(dev);
	VnetReleaseResources(Adapter);

	/* jal: Do we really need this ? IMHO the kmod will remove the module,
	   _if_ we handle the MOD_IN_USE counter correctly (see above) !!! */
	call_usermodehelper(argv[0], argv, NULL);
	info("device disconnected");
}

static int __init usb_vnet_init(void)
{
#ifdef CONFIG_IPAQ_HANDHELD
	if (machine_is_h5400()) {
		/* turn WLAN power on */
		/* both needed? */
		SET_H5400_ASIC_GPIO (GPB, RF_POWER_ON, 1);
		SET_H5400_ASIC_GPIO (GPB, WLAN_POWER_ON, 1);
	}
#endif
	return usb_register(&vnet_driver);
}

static void __exit usb_vnet_exit(void)
{
	wait_ms(250);		//wait for call_usermodehelper to finish.
	usb_deregister(&vnet_driver);

#ifdef CONFIG_IPAQ_HANDHELD
	if (machine_is_h5400()) {
		/* turn WLAN power off */
		SET_H5400_ASIC_GPIO (GPB, RF_POWER_ON, 0);
		SET_H5400_ASIC_GPIO (GPB, WLAN_POWER_ON, 0);
	}
#endif
}

/**************************************************************/
MODULE_DESCRIPTION("ATMEL 503A usb Card");
MODULE_AUTHOR("ATMEL Drivers Group");
MODULE_DEVICE_TABLE(usb, vnet_usb_id);
MODULE_LICENSE("GPL");

module_init(usb_vnet_init);	// initialization function
module_exit(usb_vnet_exit);	// exit function
/**************************************************************/
