/*************************************************************************
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");
阅读(344) | 评论(0) | 转发(0) |