#include <linux/module.h>
#include <linux/wireless.h>
#include "vnetusba.h"
#include <linux/string.h>
#include "vnetioctl.h"
#include "config.h"
/*
 * RS started to use RCS as I am changing quite some stuff now
 *
 * $Id: vnetusba.c,v 1.2 2002/08/05 21:41:28 root Exp $
 *----------------------------------------------------------------------
 * $Log: vnetusba.c,v $
 * Revision 1.2  2002/08/05 21:41:28  root
 * made WEP work
 *
 * Revision 1.1  2002/08/05 19:44:36  root
 * Initial revision
 *
 *
 *----------------------------------------------------------------------
 */
struct usb_driver vnet_driver = {
#ifdef PIDVID
  name:		"vnetusbp",
#elif defined(RFMD)
  name: 		"vnetusbr",
#elif defined(i3863)
  name: 		"vnetusb3",
#else
  name: 		"vnetusba",
#endif
  probe:		usb_vnet_probe,
  disconnect:	usb_vnet_disconnect,
  id_table:	vnet_usb_id,
};

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;
  }	
  Adapter->StationState = State;
}

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

  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;
  MOD_DEC_USE_COUNT;
  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 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;
	
  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");
#else
      strcpy(wrq->u.name, "ATMEL USB503A");
#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);
	  /* Rs Addition */
	  dbgusbnl ("Operating mode         %x", DevConfig.OperatingMode);
	  dbgusbnl ("Channel                %x", DevConfig.Channel);
	  dbgusbnl ("SSID                   %s", DevConfig.SSID);
	  dbgusbnl ("SSIDLength             %x", DevConfig.SSIDlength);
	  dbgusbnl ("TxRate                 %x", DevConfig.TxRate);
	  dbgusbnl ("PowerMgmtMode          %x", DevConfig.PowerMgmtMode);
	  dbgusbnl ("PreambleType           %x", DevConfig.PreambleType);
	  dbgusbnl ("FragmentationThreshold %d", DevConfig.FragmentationThreshold);
	  dbgusbnl ("RtsCtsTrheshold        %d", DevConfig.RtsCtsThreshold);
	  dbgusbnl ("BSSID                  %s", DevConfig.BSSID);
	  dbgusbnl ("StationState           %x", DevConfig.StationState);
	  dbgusbnl ("Rssi                   %x", DevConfig.Rssi);
	  dbgusbnl ("LinkQuality            %x", DevConfig.LinkQuality);
	  dbgusbnl ("MgmtErrorCode          %x", DevConfig.MgmtErrorCode);
	  /* RS */

	  break;
	case SET_SUPPORT_INFO:
	  /* RS addition */
	  dbgusbnl ("wants to set support info");
	  /* RS */
	  // Set config we got from the user
	  copy_from_user(&DevConfig, wrq->u.data.pointer, wrq->u.data.length);
	  if(!ValidateChannel(Adapter, DevConfig.Channel)){
	    dbgusbnl("Trying to set invalid channel")
	      return -EINVAL;
	  }
	  ChangeConfiguration(Adapter, &DevConfig);
	  /* Rs Addition */
	  dbgusbnl ("Operating mode         %x", DevConfig.OperatingMode);
	  dbgusbnl ("Channel                %x", DevConfig.Channel);
	  dbgusbnl ("SSID                   %s", DevConfig.SSID);
	  dbgusbnl ("SSIDLength             %x", DevConfig.SSIDlength);
	  dbgusbnl ("TxRate                 %x", DevConfig.TxRate);
	  dbgusbnl ("PowerMgmtMode          %x", DevConfig.PowerMgmtMode);
	  dbgusbnl ("PreambleType           %x", DevConfig.PreambleType);
	  dbgusbnl ("FragmentationThreshold %d", DevConfig.FragmentationThreshold);
	  dbgusbnl ("RtsCtsTrheshold        %d", DevConfig.RtsCtsThreshold);
	  dbgusbnl ("BSSID                  %s", DevConfig.BSSID);
	  dbgusbnl ("StationState           %x", DevConfig.StationState);
	  dbgusbnl ("Rssi                   %x", DevConfig.Rssi);
	  dbgusbnl ("LinkQuality            %x", DevConfig.LinkQuality);
	  dbgusbnl ("MgmtErrorCode          %x", DevConfig.MgmtErrorCode);
	  /* RS */
	  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;
	  dbgusbnl("%d %02x", 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;
	    }
	  }
	  dbgusbnl("Setting Wep (%d))", Adapter->Stats.RxDataPacketsOk)
	    DropPendingTxPackets(Adapter);
	  dbgusbnl("Tx Packets Dropped")
	    SetWEPvalue(Adapter);
	  dbgusbnl("out")
	    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)0;
	  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);
	  dbgusbnl("Q status, %d", netif_queue_stopped(Adapter->net))
	    dbgusbnl("BO status, %d", Adapter->tx_urb->status)
	    dbgusbnl("BI status, %d", 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->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;
	    }
	  }
	  ChangeState(Adapter, STATION_STATE_SCANNING);
	  Adapter->flags |= SITE_SURVEY_REQUEST;
	  DropPendingTxPackets(Adapter);
	  UsbScan(Adapter);
	  return;
	case SITE_SURVEY_STATE:
	  if(Adapter->StationState != STATION_STATE_SCANNING)
	    *wrq->u.data.pointer = 2;//SITE_SURVEY_COMPLETED
	  break;
	case GET_AP_INFO:
	  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:
	  Adapter->StationState = STATION_STATE_JOINING;
	  UsbJoin(Adapter);
	  break;
	case SELECT_AP_BY_INDEX:
	  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);
	  Adapter->StationState = STATION_STATE_JOINING;
	  UsbJoin(Adapter);
	  return 0;
	}
      break;
    case SIOCGIWESSID: // Return Current ESSID to wireless tools
      wrq->u.data.flags=1;
      wrq->u.data.length=Adapter->SSID_size;
      copy_to_user(wrq->u.data.pointer, Adapter->SelectedSSID, wrq->u.data.length);
      break;
    case SIOCGIWMODE:
      /* RS addition
	 iwconfig would always report the wrong settins I changed it so that
	 the function returns the correct IW define
      */
      dbgusbnl ("OperatingMode = %X", Adapter->OperatingMode);
      switch(Adapter->OperatingMode)
	{
	case AD_HOC_MODE:
	  wrq->u.mode=Adapter->OperatingMode;
	  break;
	case INFRASTRUCTURE_MODE:
	  wrq->u.mode=IW_MODE_INFRA;
	  break;
	}
      /* RS end */
      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);
      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 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/1e6)>2){
	if((wrq->u.bitrate.value/1e6)>6)
	  DevConfig.TxRate = 3;
	else
	  DevConfig.TxRate = 2;
      }else
	DevConfig.TxRate = (wrq->u.bitrate.value/1e6) - 1;
      ChangeConfiguration(Adapter, &DevConfig);
      break;			
    case SIOCSIWESSID://ESSID change
      /* RS addition. Somehow the wireless tools report the wrong length when setting aabbcc iw will
	 report a length of 7 this is wrong. It should be a length of 6. So substracting 1 will
	 correct it
      */
      dbgusbnl ("iw calling SIOCSIWESSID %s %d", wrq->u.data.pointer, wrq->u.data.length);
      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;
      }
      dbgusbnl ("u.mode = %X", wrq->u.mode);
      ChangeConfiguration(Adapter, &DevConfig);
      break;
    case SIOCSIWFREQ:
      dbgusbnl("trying to set channel %X %X %X", wrq->u.freq.m, wrq->u.freq.e,wrq->u.freq.i);
      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:
      spin_unlock_irqrestore(Adapter->lock, flags);
      if(wrq->u.data.flags & IW_ENCODE_DISABLED){
	Adapter->PrivacyInvoked = 0;
	return 0;
      }
      if (wrq->u.data.flags == IW_ENCODE_ENABLED) {
	Adapter->PrivacyInvoked = 1;
	return 0;
      }
      dbgusbnl ("data.flags %04X", wrq->u.data.flags);
      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;
      dbgusbnl ("WENCODE length %d", wrq->u.data.length);
      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;
      
      Adapter->WepInfo.WepKeyToUse = (wrq->u.data.flags & IW_ENCODE_INDEX)-1;
      dbgusbnl ("WepKeyToUse : %d", 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;
	}
      }
      dbgusbnl("Setting Wep (%d))", Adapter->Stats.RxDataPacketsOk);
      DropPendingTxPackets(Adapter);
      dbgusbnl("Tx Packets Dropped");
      SetWEPvalue(Adapter);
      dbgusbnl("out");
      
      return 0;
    }
  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;
}
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;
	
  len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
  spin_lock_irqsave(Adapter->lock, flags);
  EnQueuePacket(Adapter, &NewTxL, len, 1);
  NewTxL->skb 	= skb;
  // add the packet in the buffer (the first 12 bytes are going to be used in the
  // wireless header.
  memcpy((PUCHAR)&NewTxL->tx_buff->WirelessPacket[18],(PUCHAR)skb->data, len);
  // add the wireless header.....
  Ethernet2Wireless(Adapter, NewTxL);
  DeQueueTxPacket(Adapter);
  // 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;
  UCHAR			BoardType=0, i;
	
  if( (dev->descriptor.idVendor != VNET_VIDS) &&
      (dev->descriptor.idProduct != VNET_PIDS) ){
    err("Device Descriptors 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));
  ChangeState(Adapter, STATION_STATE_INITIALIZING);
#ifdef RFMD	
  Adapter->BoardType = RFMD_Board;
#elif defined(i3863)
  Adapter->BoardType = I3863_Board;
#else
  Adapter->BoardType = Intersil_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;
  }
	
  usb_inc_dev_use(dev);
  //	Start Polling In Bulk In Endpoint
  if(UsbRxInit(Adapter)!=0){
    MOD_DEC_USE_COUNT;
    kfree(Adapter);
    usb_dec_dev_use(dev);		
    return NULL;
  }
  dbgusbnl("RxInit OK")
    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);

  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 register", Adapter->net->name);
  return Adapter;
}

static void usb_vnet_disconnect(struct usb_device *dev, void *ptr)
{
  PVNet_ADAPTER	Adapter = (PVNet_ADAPTER)ptr;
  DECLARE_WAITQUEUE(wait, current);
  UCHAR			DeviceMode = Adapter->DevMode;
  ULONG			flags;
  UCHAR			BitUp=0;
  char			*argv[4];
  int				ret=0;
	
  argv[0]="/sbin/modprobe";
  argv[1]="-r";
#ifdef PIDVID
  argv[2]="vnetusbp";
#elif RFMD
  argv[2]="vnetusbr";
#elif defined(i3863)
  argv[2]="vnetusb3";
#else
  argv[2]="vnetusba";
#endif
  argv[3]=0;
	
  printk("argv[2] : %s\n", argv[2]);
  if(!Adapter) return;

  info("usb device %s disconnecting\n", Adapter->net->name);
	
  Adapter->flags &= ~VNET_RUNNING;
  Adapter->StationState = STATION_STATE_EXITING;
	
  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;

  ret = usb_unlink_urb(Adapter->rx_urb);
  ret = usb_unlink_urb(Adapter->tx_urb);
  ret = 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).
  netif_stop_queue(Adapter->net);
  Adapter->flags |= TX_STOPPED;
  printk("Trying to unregister net device\n");
  unregister_netdevice( Adapter->net );

  printk("unregistered\n");
  /*
    Adapter->net->priv = NULL;	
    Adapter->net->name[0] = '\0';
    Adapter->net = NULL;
  */
  while(MOD_IN_USE > 0){
    MOD_DEC_USE_COUNT;
  }
	
  usb_dec_dev_use( dev );
  VnetReleaseResources(Adapter);

  call_usermodehelper(argv[0], argv, NULL);
  info("device disconnected");
}

static int __init usb_vnet_init(void)
{
  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 );
}

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

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

