Chinaunix首页 | 论坛 | 博客
  • 博客访问: 665524
  • 博文数量: 156
  • 博客积分: 4833
  • 博客等级: 上校
  • 技术积分: 1554
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-21 19:36
文章分类

全部博文(156)

文章存档

2016年(2)

2013年(1)

2012年(13)

2011年(30)

2010年(46)

2009年(29)

2008年(23)

2007年(12)

分类: LINUX

2010-04-26 11:25:19

/*************************************************************************

Fake eth char convert
**************************************************************************/

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include



/*  MAC地址  */

static char vir_mac[]={0x00, 0xAA, 0xBB, 0xCC, 0xBB, 0xAA};
char *vir_mac_p = NULL;
int loop = 0;                       /* when loop=1, enable loopback mode,not write Yet :) */

//VIRMAC
module_param(vir_mac_p, charp, 0444);
MODULE_PARM_DESC(vir_mac_p, "MAC");

module_param(loop, int, 0);
MODULE_PARM_DESC(loop, "loopback mode");



#define ETH_BUFFER_LEN 1520
#define FAKE_IRQ       18
#define ETH_VIR_MAJOR  160


struct s_buff
{
  unsigned char buffer[ETH_BUFFER_LEN];
  unsigned int  length;
};

//buffer for Eth
static struct s_buff tx_buff;
static struct s_buff rx_buff;


struct vireth_priv
{

  struct net_device * netdev;      /* net device parameter */
  struct net_device_stats stats;   /* net statistics */
  struct sk_buff *skb;             /* free send skb*/
  struct proc_dir_entry *entry;   
  wait_queue_head_t inq;           /* send waitQ, for eth char read */
  struct semaphore sem;
 
};

static struct vireth_priv * dev_vireth = NULL;





#define  VIRETH_NAME        "vir_eth"        /* 网络设备的名字 */


static int vireth_open(struct net_device *dev);
static int vireth_release(struct net_device *dev);
static void vireth_init(struct net_device *dev);
static int vireth_xmit(struct sk_buff *skb, struct net_device *dev);
static int vireth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static int transmit_int(int irq, void *dev_id);
static int receive_int(int irq, void *dev_id);


/************************* MII Proc interface **************************/
static int MII_read_proc(char *page, char **start, off_t off,
         int count, int *eof, void *data)
{
  #if 0
     int length;
    
     if (down_interruptible(&dev_vireth->sem))
     {
        return -ERESTARTSYS;
     }
     //
     while( 0 == tx_buff.length )
     {
         up(&dev_vireth->sem);
         //sleep
         if( wait_event_interruptible(dev_vireth->inq,(0 != tx_buff.length)) )
         {
           return -ERESTARTSYS;
         }
      
         if (down_interruptible(&dev_vireth->sem))
         {
           return -ERESTARTSYS;
         }
     }  
   
     memcpy(page,tx_buff.buffer,tx_buff.length );
     length = tx_buff.length;
     tx_buff.length = 0;
    
     up(&dev_vireth->sem);
     *eof = 1;
    
     transmit_int(FAKE_IRQ,dev_vireth);
     //printk("#### %d##\n",length);
   
      return length;
   #endif
   ////////////
   *eof = 1;
   return 0;
}

static int MII_write_proc(struct file *file, const char *buffer,
         unsigned long count, void *data)
{
  #if 0
 
    if(count > ETH_BUFFER_LEN)
    {
      printk("Error input len\n");
      return -1;
    }
   
    memcpy(rx_buff.buffer, buffer, count);
    rx_buff.length = count;
    receive_int(FAKE_IRQ,dev_vireth);
   
    return count;
 
  #endif
  ///////////////
  return 0;
}  


/************************* fake interrupt**************************/

static int receive_int(int irq, void *dev_id)
{
  struct sk_buff *skb;
  //struct vireth_priv *priv = netdev_priv(dev);

  /*
   * The packet has been retrieved from the transmission
   * medium. Build an skb around it, so upper layers can handle it
   */
  skb = dev_alloc_skb(rx_buff.length + 4);
  if (!skb)
  {
    if (printk_ratelimit())
    { printk(KERN_NOTICE "snull rx: low on mem - packet dropped\n");}
     dev_vireth->stats.rx_dropped++;
       goto out;
  }
  skb_reserve(skb, 2); /* align IP on 16B boundary */ 
 
  memcpy(skb_put(skb, rx_buff.length), rx_buff.buffer, rx_buff.length);
 
  /* Write metadata, and then pass to the receive level */
  skb->protocol = eth_type_trans(skb, dev_vireth->netdev);
  //skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
  dev_vireth->stats.rx_packets++;
  dev_vireth->stats.rx_bytes += rx_buff.length;
  netif_rx(skb);

 out:
  return 0;
}


//send finished
  static int transmit_int(int irq, void *dev_id)
{
     dev_kfree_skb(dev_vireth->skb);
     netif_wake_queue(dev_vireth->netdev);      
     return 0;
}



/*************************Eth  interface **************************/
static int vireth_open(struct net_device *dev)
{  
   /* 启动发送队列 */
  netif_start_queue(dev);
  return 0;
}


static int vireth_release(struct net_device *dev)
{    /* 停止发送队列 */
  netif_stop_queue(dev);  /* can't transmit any more */
  return 0;
}


//copy from skb to buffer
static int vireth_write_packet(struct sk_buff *skb, struct net_device *dev)
{
  //lock char read
   if(down_interruptible(&dev_vireth->sem))
   {
     return 1;
   }
   //still have pack int TX buffer
   if(tx_buff.length !=0 )
   {
     up(&dev_vireth->sem);
     return 1;
   }
   memcpy(tx_buff.buffer, skb->data, skb->len); 
   tx_buff.length = skb->len;

   up(&dev_vireth->sem);
   wake_up_interruptible(&dev_vireth->inq);
    
   return 0;
}


static int vireth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
  int rc = 0;

  switch (cmd)
  {
    default:
      rc = -ENOSYS;
      break;
  }

  return rc;
}

//call by up layer
static int vireth_xmit(struct sk_buff *skb, struct net_device *dev)
{
  struct vireth_priv * priv = (struct vireth_priv *)netdev_priv(dev);
  int rc;

  netif_stop_queue(dev);

  if (vireth_write_packet(skb, dev))
  {
    priv->stats.tx_dropped++;
    rc = 1;
  }
  else
  {
    priv->stats.tx_packets++;
    priv->stats.tx_bytes += skb->len;
        priv->skb = skb;
    dev->trans_start = jiffies;         /* save the timestamp */

    rc = 0;
  }

  return rc;
}


static struct net_device_stats *vireth_get_stats(struct net_device *dev)
{
   struct vireth_priv * priv = (struct vireth_priv *)netdev_priv(dev);

   return &(priv->stats);
}

/* fake multicast ability */
static void set_multicast_list(struct net_device *dev)
{
  return;
}

static int set_mac_address(struct net_device *dev, void* addr)
{
  struct sockaddr *address = addr;

  if (!is_valid_ether_addr(address->sa_data))
  {
    return -EADDRNOTAVAIL;
  }

  memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
  printk("%s: Setting MAC address to %pM\n", dev->name,  dev->dev_addr);

  return 0;
}



static const struct net_device_ops vireth_netdev_ops = {
  .ndo_open               = vireth_open,
  .ndo_stop               = vireth_release,
  .ndo_do_ioctl           = vireth_ioctl,
  .ndo_get_stats          = vireth_get_stats,
  .ndo_start_xmit         = vireth_xmit,
  .ndo_set_multicast_list = set_multicast_list,
  .ndo_set_mac_address  = set_mac_address,
};

/************************char dev ************************/

static struct cdev vireth_cdev;

static int vireth_char_open(struct inode * inode, struct file * filp)
{
  return 0;
}


static int vireth_char_release(struct inode * inode, struct file * filp)
{
  return 0;
}

static int vireth_char_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
{
    switch(cmd)
    {
       default:
           return -EINVAL;
    }
   
    return 0;
}

//eth out put packet
static ssize_t vireth_char_read(struct file * file, char __user * buf, size_t count, loff_t *ppos)
{
    int length;
    unsigned long ret;
    
     if (down_interruptible(&dev_vireth->sem))
     {
        return -ERESTARTSYS;
     }
     //
     while( 0 == tx_buff.length )
     {
         up(&dev_vireth->sem);
         //sleep
         if( wait_event_interruptible(dev_vireth->inq,(0 != tx_buff.length)) )
         {
           return -ERESTARTSYS;
         }
      
         if (down_interruptible(&dev_vireth->sem))
         {
           return -ERESTARTSYS;
         }
     }  
   
     ret = copy_to_user(buf,tx_buff.buffer,tx_buff.length );
     length = tx_buff.length;
     tx_buff.length = 0;
    
     up(&dev_vireth->sem);   
    
     transmit_int(FAKE_IRQ,dev_vireth);
     //printk("#### %d##\n",length);
   
     return length;
 
}

static ssize_t vireth_char_write(struct file * file, const char __user * buf, size_t count,
       loff_t *ppos)
{
    unsigned long ret;
    if(count > ETH_BUFFER_LEN)
    {
       printk("Error input len\n");
       return -1;
    }
   
    ret = copy_from_user(rx_buff.buffer, buf, count);
    rx_buff.length = count;
    receive_int(FAKE_IRQ,dev_vireth);
   
    return count;
}


static struct file_operations vireth_char_fops = {
  .llseek     = NULL,
  .read       = vireth_char_read,
  .write      = vireth_char_write,
  .ioctl      = vireth_char_ioctl,
  .open       = vireth_char_open,
  .release    = vireth_char_release,
  .fasync     = NULL,
};


static int __init vireth_char_init(void)
{
    int ret;
    int devno = MKDEV(ETH_VIR_MAJOR,0);
  
    printk("Vireth version:%s %s\n",__DATE__,__TIME__);
           
    cdev_init(&vireth_cdev, &vireth_char_fops);
    vireth_cdev.owner = THIS_MODULE;
    ret = cdev_add(&vireth_cdev, devno, 1);
    if(ret)
    {       
       return ret; 
    }
  
    return 0;
}

static void __exit vireth_char_exit(void)
{
    cdev_del(&vireth_cdev);
}

/*************************eth  init **************************/

static void vireth_init(struct net_device *dev)
{
  struct vireth_priv * priv = NULL;

  ether_setup(dev);
  dev->netdev_ops  = &vireth_netdev_ops;

  dev->watchdog_timeo = 3*HZ;  
  dev->irq = FAKE_IRQ;//no use
 
  memcpy(dev->dev_addr, vir_mac, ETH_ALEN);

  priv = (struct vireth_priv *)netdev_priv(dev);
  dev_vireth = priv;
 
  priv->netdev = dev;

}

inline char ascTochar(char s)
{
         if( ('0'<= s) && ( s<='9') )
         {
                   return (s-'0');
         }
        
         else if( ('a'<= s) && (s<='f') )
         {
                   return (s-'a'+10);
         }
         else if( ('A'<= s) && (s<='F') )
         {
                   return (s-'A'+10);
         }
         else
         {
                  return 0;
         }
        
}

static int __init vireth_init_module(void)
{
  int rc = 0;
  int i;
 
  struct net_device  *net_dev = NULL;
 
  printk("Input MAC:%s\n",vir_mac_p);
  if(vir_mac_p)
  {
          for(i=0;i<6;i++)
          {
                   vir_mac[i] = ascTochar( *(vir_mac_p+i*2) )<<4;
                   vir_mac[i] |=ascTochar( *(vir_mac_p+i*2+1) );
          }
  }
  //
  printk("active MAC: ");
  for(i=0;i<6;i++)
  {
          printk("%02X:",vir_mac[i]);
  }
  printk("\n");
 
  /* alloc net dev */
  net_dev = alloc_netdev(sizeof(struct vireth_priv), VIRETH_NAME, vireth_init);
  if (!net_dev)
  {
    //log(KERN_ERR "no enough memory!\n");
    rc = -1;
    goto ERROR0;
  }

  /* got a name for eth*/
  rc = dev_alloc_name(net_dev, VIRETH_NAME);
  if (rc < 0)
    {
    //log(KERN_INFO "failed to alloc device name %s\n", VIRETH_NAME);
    goto ERROR0;
  }

  rc = register_netdev(net_dev);
  if (rc < 0)
  {
    goto ERROR0;
  }
 

  dev_vireth->entry = create_proc_entry("VIR_ETH_MII_DATA", 0, NULL);
  if (dev_vireth->entry == NULL)
    {
      return -ENOMEM;
    }
  dev_vireth->entry->read_proc = MII_read_proc;
  dev_vireth->entry->write_proc = MII_write_proc;
  dev_vireth->entry->data = NULL;
 
  init_waitqueue_head(&dev_vireth->inq);
  init_MUTEX(&dev_vireth->sem);
 
  vireth_char_init();

  return rc;

ERROR0:
 
  free_netdev(net_dev);
  return rc;
}


static void __exit vireth_cleanup_module(void)
{
    unregister_netdev(dev_vireth->netdev);
    remove_proc_entry("VIR_ETH_MII_DATA", NULL);
    free_netdev(dev_vireth->netdev);
    vireth_char_exit(); 
}

module_init(vireth_init_module);
module_exit(vireth_cleanup_module);

MODULE_AUTHOR("roemer");
MODULE_DESCRIPTION("virtual ETH driver");
MODULE_LICENSE("GPL");
阅读(1012) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~