www.pudn.com > Linux_serial_network_src.rar > ed_device.c


/*
 * ed_device.c embeded software project(1) device file.
 *
 * Copyright (C) 2003 Li Suke,Software School of Peking University.
 * 
 * The data flow of the devices is:
 *
 *          neted
 *     _______|___________
 *     |                  |
 *     |                  |
 *   ed_rec            ed_tx
 *   (recieve)          (transmit)
 *  
 * neted: pseodu network device
 * ed_rec: character device
 * ed_tx:  character device
 * You can modify and distribute this source code freely.   
 * 
 */

#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif

#include 
#include 

#include 
#include 
#ifdef LINUX_24
#include  
#else
#include 
#endif

#include 
#include   
#include   

#include 
#include   
#include  
#include           
#include 
#include 
#ifdef LINUX_20
#include 
#endif

#include 

#include "ed_device.h"
#include "ed_ioctl.h"

MODULE_AUTHOR("Li Suke");

/* We must define the htons() function here, for the kernel has no
 * this API if you do not make source code dep
 */ 
#define htons(x) ((x>>8) | (x<<8))

char ed_names[16];
struct ed_device ed[2];
#ifdef LINUX_24
struct net_device ednet_dev;
#else
struct device ednet_dev;
#endif

#ifdef LINUX_24
static int timeout = ED_TIMEOUT;
#endif

#ifdef LINUX_24
void ednet_rx(struct net_device *dev,int len,unsigned char *buf);
#else
void ednet_rx(struct device *dev,int len,unsigned char *buf);
#endif

#ifdef LINUX_24
void ednet_tx_timeout (struct net_device *dev);
#else
void ednet_tx_timeout (struct device *dev);
#endif
      
/* Initialize the ed_rec and ed_tx device,the two devices
   are allocate the initial buffer to store the incoming and
   outgoing data. If the TCP/IP handshake need change the
   MTU,we must reallocte the buffer using the new MTU value.
 */
static int device_init(){
    
    int i;
    int err;
    err = -ENOBUFS;
    strcpy(ed[ED_REC_DEVICE].name, ED_REC_DEVICE_NAME);
    strcpy(ed[ED_TX_DEVICE].name, ED_TX_DEVICE_NAME);
    for (i = 0 ;i < 2; i++ )
    {   
    	ed[i].buffer_size = BUFFER_SIZE;     	
        ed[i].buffer = kmalloc(ed[i].buffer_size + 4 , GFP_KERNEL);
        ed[i].magic = ED_MAGIC;
        ed[i].mtu = ED_MTU;
        ed[i].busy = 0;
        #ifdef LINUX_24
        init_waitqueue_head(&ed[i].rwait);
        #endif
        
	if (ed[i].buffer == NULL)
	    goto err_exit;	    	
	spin_lock_init(&ed[i].lock);     
    }  
    err = 0;
    return err;
    
err_exit:
    printk("There is no enongh memory for buffer allocation. \n");
    return err;		
}
static int ed_realloc(int new_mtu){
    int err;
    int i;
    err = -ENOBUFS;
    char *local_buffer[2];
    int size;
    for (i=0;i<2;i++){
        local_buffer[i] = kmalloc(new_mtu + 4,GFP_KERNEL);
        #ifdef LINUX_20
        if(new_mtu >= ed[i].buffer_size)
            size = new_mtu;
        else 
            size = ed[i].buffer_size;
        #else
        size = min(new_mtu,ed[i].buffer_size);
        #endif

        memcpy(local_buffer[i],ed[i].buffer,size);
        kfree(ed[i].buffer);

        ed[i].buffer = kmalloc(new_mtu + 4,GFP_KERNEL);
        if( ed[i].buffer < 0){
            printk("Can not realloc the buffer from kernel when change mtu.\n");
            return err;
        }
           
    }
    return 0;
	
}
/* Open the two character devices,and let the ed_device's private pointer 
 * point to the file struct */

static int device_open(struct inode *inode,struct file *file)
{
    int Device_Major;
    struct ed_device *edp;
    Device_Major = inode->i_rdev >> 8;
    
    #ifdef _DEBUG 
    printk("Get the Device Major Number is %d\n",Device_Major);
    #endif
    if (Device_Major == MAJOR_NUM_REC )
    {
        file->private_data = &ed[ED_REC_DEVICE];
        ed[ED_REC_DEVICE].file = file;
    }    
    else    
    if (Device_Major == MAJOR_NUM_TX){
        file->private_data = &ed[ED_TX_DEVICE];
        ed[ED_TX_DEVICE].file = file;
    }    
    else
        return -NODEV;
    edp = (struct ed_device *)file->private_data;
                    
    if(edp->busy != 0){
       printk("The device is open!\n");	
       return -EBUSY;
    }
    
    edp->busy++;
 
    return 0;
       
}

/* release the devices */
int device_release(struct inode *inode,struct file *file)
{
	
    struct ed_device *edp;
    edp = (struct ed_device *)file->private_data;
    edp->busy = 0;
    
    return 0;	
}

/* read data from ed_tx device */
ssize_t device_read(struct file *file,char *buffer,size_t length, loff_t *offset)
{
    #ifdef _DEBUG
    int i;
    #endif
    struct ed_device *edp;
    edp = (struct ed_device *)file->private_data;
#ifdef LINUX_24
    DECLARE_WAITQUEUE(wait,current);
    add_wait_queue(&edp->rwait,&wait);
    for(;;){
        
        set_current_state(TASK_INTERRUPTIBLE);
        if ( file->f_flags & O_NONBLOCK)
            break;
        if ( edp->tx_len > 0)
            break;
        
        if ( signal_pending(current))
            break;
        schedule();
        
    }
    set_current_state(TASK_RUNNING);
    remove_wait_queue(&edp->rwait,&wait);
#endif
    
    spin_lock(&edp->lock);
    
    if(edp->tx_len == 0) {
         spin_unlock(&edp->lock);
         return 0;

        
    }else
    {
   
        copy_to_user(buffer,edp->buffer,edp->tx_len);
        memset(edp->buffer,0,edp->buffer_size); 
        
        #ifdef _DEBUG
        printk("\n read data from ed_tx \n");
        for(i=0;itx_len;i++)
            printk(" %02x",edp->buffer[i]&0xff);
        printk("\n");
        #endif
        
        length = edp->tx_len;
        edp->tx_len = 0;
    }
    spin_unlock(&edp->lock);
    return length;
    
}
/* This function is called by ednet device to write the network data 
 * into the ed_tx character device.
 */
ssize_t kernel_write(const char *buffer,size_t length,int buffer_size)
{
          
    if(length > buffer_size )
        length = buffer_size;
    memset(ed[ED_TX_DEVICE].buffer,0,buffer_size);
    memcpy(ed[ED_TX_DEVICE].buffer,buffer,buffer_size);
    ed[ED_TX_DEVICE].tx_len = length;
    #ifdef LINUX_24
    wake_up_interruptible(&ed[ED_TX_DEVICE].rwait);	
    #endif
    
    return length;
}

/* Device write is called by server program, to put the user space
 * network data into ed_rec device.
 */
ssize_t device_write(struct file *file,const char *buffer, size_t length,loff_t *offset)
{
    #ifdef _DEBUG
    int i;
    #endif
    struct ed_device *edp;
    edp = (struct ed_device *)file->private_data;
    
    spin_lock(&ed[ED_REC_DEVICE].lock);
    if(length > edp->buffer_size)
        length =  edp->buffer_size;
        
    copy_from_user( ed[ED_REC_DEVICE].buffer,buffer, length);
    ednet_rx(&ednet_dev,length,ed[ED_REC_DEVICE].buffer);
    
    #ifdef _DEBUG
    printk("\nNetwork Device Recieve buffer:\n");
    for(i =0;i< length;i++)
       printk(" %02x",ed[ED_REC_DEVICE].buffer[i]&0xff);
    printk("\n");
    #endif
    spin_unlock(&ed[ED_REC_DEVICE].lock);     
    return length;   
}        
int device_ioctl(struct inode *inode,
                 struct file *file,
                 unsigned int ioctl_num,
                 unsigned long ioctl_param){
    struct ed_device *edp;
    edp = (struct ed_device *)file->private_data;
    switch(ioctl_num)
    {
        case IOCTL_SET_BUSY:
           edp->busy = ioctl_param;
           break;
      
       
    }
    return 0;

}

/* 
 * All the ednet_* functions are for the ednet pseudo network device ednet.
 * ednet_open and ednet_release are the two functions which open and release
 * the device.
 */
 
#ifdef LINUX_20
int ednet_open(struct device *dev)
#else
int ednet_open(struct net_device *dev)
#endif
{
    MOD_INC_USE_COUNT;
    
    /* Assign the hardware pseudo network hardware address,
     * the MAC address's first octet is 00,for the MAC is
     * used for local net,not for the Internet.
     */
    memcpy(dev->dev_addr, "\0ED000", ETH_ALEN);
   
#ifndef LINUX_24                
    dev->start = 1;
    dev->tbusy = 0;             
#endif
             
#ifdef LINUX_24             
    netif_start_queue(dev);
#endif
    return 0;
}
#ifdef LINUX_24
int ednet_release(struct net_device *dev)
#else
int ednet_release(struct device *dev)
#endif
{
    
#ifdef LINUX_24    
    netif_stop_queue(dev); 
#endif

#ifndef LINUX_24                
    dev->start = 0;
    dev->tbusy = 1;             
#endif                          
    MOD_DEC_USE_COUNT;
    
    return 0;
}

/*
 * Used by ifconfig,the io base addr and IRQ can be modified
 * when the net device is not running.
 */
#ifdef LINUX_24
int ednet_config(struct net_device *dev, struct ifmap *map)
#else
int ednet_config(struct device *dev, struct ifmap *map)
#endif
{
    if (dev->flags & IFF_UP) 
        return -EBUSY;

    /* change the io_base addr */
    if (map->base_addr != dev->base_addr) {
        printk(KERN_WARNING "ednet: Can't change I/O address\n");
        return -EOPNOTSUPP;
    }

    /* can change the irq */
    if (map->irq != dev->irq) {
        dev->irq = map->irq;
        
    }

    
    return 0;
}

/*
 * ednet_rx,recieves a network packet and put the packet into TCP/IP up
 * layer,netif_rx() is the kernel API to do such thing. The recieving
 * procedure must alloc the sk_buff structure to store the data,
 * and the sk_buff will be freed in the up layer.
 */
#ifdef LINUX_24
void ednet_rx(struct net_device *dev, int len, unsigned char *buf)
#else
void ednet_rx(struct device *dev ,int len, unsigned char *buf)
#endif
{
    struct sk_buff *skb;
    struct ednet_priv *priv = (struct ednet_priv *) dev->priv;

    skb = dev_alloc_skb(len+2);
    if (!skb) {
        printk("ednet_rx can not allocate more memory to store the packet. drop the packet\n");
        priv->stats.rx_dropped++;
        return;
    }
    skb_reserve(skb, 2);
    memcpy(skb_put(skb, len), buf, len);

    skb->dev = dev;
    skb->protocol = eth_type_trans(skb, dev);
    /* We need not check the checksum */
    skb->ip_summed = CHECKSUM_UNNECESSARY; 
    priv->stats.rx_packets++;
#ifndef LINUX_20                        
    priv->stats.rx_bytes += len;
#endif                                  
    netif_rx(skb);
    return;
}
    
        

/*
 * pseudo network hareware transmit,it just put the data into the 
 * ed_tx device.
 */
#ifdef LINUX_24
void ednet_hw_tx(char *buf, int len, struct net_device *dev)
#else
void ednet_hw_tx(char *buf, int len, struct device *dev)
#endif
{
   
    struct ednet_priv *priv;
   
    /* check the ip packet length,it must more then 34 octets */
    if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) {
        printk("Bad packet! It's size is less then 34!\n");
        
        return;
    }
    /* now push the data into ed_tx device */  
    ed[ED_TX_DEVICE].kernel_write(buf,len,ed[ED_TX_DEVICE].buffer_size); 
    
    /* record the transmitted packet status */
    priv = (struct ednet_priv *) dev->priv;
    priv->stats.tx_packets++;
    priv->stats.rx_bytes += len;
    
    /* remember to free the sk_buffer allocated in upper layer. */
    dev_kfree_skb(priv->skb);
 
   
}

/*
 * Transmit the packet,called by the kernel when there is an
 * application wants to transmit a packet.
 */
#ifdef LINUX_24
int ednet_tx(struct sk_buff *skb, struct net_device *dev)
#else
int ednet_tx(struct sk_buff *skb, struct device *dev)
#endif
{
    int len;
    char *data;
    struct ednet_priv *priv = (struct ednet_priv *) dev->priv;
    
    if( ed[ED_TX_DEVICE].busy ==1){
     
        return -EBUSY;
    }
#ifndef LINUX_24
    if (dev->tbusy || skb == NULL) {
  
        ednet_tx_timeout (dev);
        if (skb == NULL)
            return 0;
    }
#endif

    len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
    data = skb->data;
    /* stamp the time stamp */
    dev->trans_start = jiffies;

    /* remember the skb and free it in ednet_hw_tx */
    priv->skb = skb;
    
    /* pseudo transmit the packet,hehe */
    ednet_hw_tx(data, len, dev);

    return 0; 
}

/*
 * Deal with a transmit timeout.
 */
#ifdef LINUX_24
void ednet_tx_timeout (struct net_device *dev)
#else
void ednet_tx_timeout (struct device *dev)
#endif
{
    struct ednet_priv *priv = (struct ednet_priv *) dev->priv;
    priv->stats.tx_errors++;
#ifdef LINUX_24
    netif_wake_queue(dev);
#endif   
    return;
}



/*
 * When we need some ioctls.
 */
#ifdef LINUX_24
int ednet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
#else
int ednet_ioctl(struct device *dev, struct ifreq *rq, int cmd)
#endif
{
 
    return 0;
}

/*
 * ifconfig to get the packet transmitting status.
 */
#ifdef LINUX_24
struct net_device_stats *ednet_stats(struct net_device *dev)
#else
struct enet_statistics *ednet_stats(struct device *dev)
#endif
{
    struct ednet_priv *priv = (struct ednet_priv *) dev->priv;
    return &priv->stats;
}

/*
 * TCP/IP handshake will call this function, if it need.
 */
#ifdef LINUX_24
int ednet_change_mtu(struct net_device *dev, int new_mtu)
#else
int ednet_change_mtu(struct device *dev, int new_mtu)
#endif
{
    int err;
    unsigned long flags;
    spinlock_t *lock = &((struct ednet_priv *) dev->priv)->lock;
    
    /* en, the mtu CANNOT LESS THEN 68 OR MORE THEN 1500. */
    if (new_mtu < 68)
        return -EINVAL;
   
    
    spin_lock_irqsave(lock, flags);
    dev->mtu = new_mtu;
    /* realloc the new buffer */
    
    err = ed_realloc(new_mtu);
    spin_unlock_irqrestore(lock, flags);
    return err; 
}
#ifdef LINUX_24
int ednet_header(struct sk_buff *skb,
                 struct net_device *dev,
                 unsigned short type,
                 void *daddr,
                 void *saddr,
                 unsigned int len)
#else
int ednet_header(struct sk_buff *skb,
                 struct device *dev,
                 unsigned short type,
                 void *daddr,
                 void *saddr,
                 unsigned int len)
#endif
{
    struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
    eth->h_proto = htons(type);
    memcpy(eth->h_source,saddr? saddr : dev->dev_addr,dev->addr_len);
    memcpy(eth->h_dest,   daddr? daddr : dev->dev_addr, dev->addr_len);
    return (dev->hard_header_len);

}

int ednet_rebuild_header(struct sk_buff *skb)
{
    struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
    
#ifdef LINUX_24
    struct net_device *dev = skb->dev;
#else 
    struct device *dev = skb->dev;
#endif

    memcpy(eth->h_source, dev->dev_addr ,dev->addr_len);
    memcpy(eth->h_dest,   dev->dev_addr , dev->addr_len);
    return 0;

}


#ifdef LINUX_24
int ednet_init(struct net_device *dev)
#else
int ednet_init(struct device *dev)
#endif
{

   
    ether_setup(dev); 
    dev->open            = ednet_open;
    dev->stop            = ednet_release;
    dev->set_config      = ednet_config;
    dev->hard_start_xmit = ednet_tx;
    dev->do_ioctl        = ednet_ioctl;
    dev->get_stats       = ednet_stats;
    dev->change_mtu      = ednet_change_mtu;  
#ifdef LINUX_24
    dev->hard_header     = ednet_header;
#endif
    dev->rebuild_header  = ednet_rebuild_header;
#ifdef LINUX_24
    dev->tx_timeout     = ednet_tx_timeout;
    dev->watchdog_timeo = timeout;
#endif
    /* We do not need the ARP protocol. */
    dev->flags           |= IFF_NOARP;
#ifndef LINUX_20                        
    dev->hard_header_cache = NULL;      
#endif 

#ifdef LINUX_24                                 
    SET_MODULE_OWNER(dev);
#endif

    dev->priv = kmalloc(sizeof(struct ednet_priv), GFP_KERNEL);
    if (dev->priv == NULL)
        return -ENOMEM;
    memset(dev->priv, 0, sizeof(struct ednet_priv));
    spin_lock_init(& ((struct ednet_priv *) dev->priv)->lock);
    return 0;
}

struct file_operations ed_ops ={
#ifdef LINUX_24
    NULL,
#endif
    NULL,
    device_read,
    device_write,
    NULL,
    NULL,
    device_ioctl,
    NULL,
    device_open,
    NULL,
    device_release,
    	
};

/* initialize the character devices */
int eddev_module_init(void)
{
    int err;
    int i;
    if((err=device_init()) != 0)
    {
        printk("Init device error:");
        return err;
    }
    err = register_chrdev(MAJOR_NUM_REC,ed[ED_REC_DEVICE].name,&ed_ops);
    if( err != 0)
        printk("Install the buffer rec device %s fail", ED_REC_DEVICE_NAME);
    for(i=0; i<2;i++)
        ed[i].kernel_write = kernel_write;  
    
    err = register_chrdev(MAJOR_NUM_TX,ed[ED_TX_DEVICE].name,&ed_ops);
    if( err != 0)
        printk("Install the buffer tx device %s fail",ED_TX_DEVICE_NAME);		
    return err;    
    

}
/* clean up the character devices */
void eddev_module_cleanup(void)
{
    int err;
    int i;
    for (i = 0 ;i < 2; i++){
        kfree(ed[i].buffer);   
    }    

    err = unregister_chrdev(MAJOR_NUM_REC,ed[ED_REC_DEVICE].name);
    if(err != 0)
        printk("UnInstall the buffer recieve device %s fail",ED_REC_DEVICE_NAME);
    err = unregister_chrdev(MAJOR_NUM_TX,ed[ED_TX_DEVICE].name);
    if(err != 0)
        printk("UnInstall the buffer recieve device %s fail",ED_TX_DEVICE_NAME);
        	
}


int ednet_module_init(void)
{

    int err;
    strcpy(ednet_dev.name, "ed0");
    ednet_dev.init = ednet_init;
    if ( (err = register_netdev(&ednet_dev)) )
            printk("ednet: error %i registering pseudo network device \"%s\"\n",
                   err, ednet_dev.name);
        
    return err;
}

void ednet_module_cleanup(void)
{
   
    kfree(ednet_dev.priv);
    unregister_netdev(&ednet_dev);
    return;
}

/* called by the kernel to setup the module*/
int init_module(void)
{
    int err;
    err = eddev_module_init();
    if(err < 0)
        return err;
    err = ednet_module_init();
    if(err < 0)
        return err;
    return err;	
	
}
/* cleanup the module */
void cleanup_module()
{
    eddev_module_cleanup();
    ednet_module_cleanup();	
	
}
#ifdef LINUX_24
MODULE_LICENSE("GPL");
#endif