1. describe

The driver of NIC is very simple , It's still hardware related , Mainly responsible for sending and receiving network packets , It sends the data packets from the upper layer protocol in a specific media access control mode , And deliver the received packets to the upper layer protocol .

Network card devices are different from character devices and block devices , Network devices don't correspond to /dev A file in a directory , But it will be stored in the /sys/class/net Under the table of contents

As shown in the figure below , We go through ls /sys/class/net/ command , You can see that there are two network cards :

 

 

2.Linux The system defines the network device driver 4 A hierarchical , this 4 The three levels are divided into :

1) Network protocol interface layer :

A unified protocol for sending and receiving data packets , This layer is mainly responsible for calling dev_queue_xmit() Function to send data , netif_rx() Function to receive data

2) Network device interface layer :

adopt net_device Structure to describe the information of a specific network device , Realize the unification of different hardware

3) Device driver function layer :

It is used to drive the network device hardware to complete various functions , It passes through hard_start_xmit() Function to start the send operation , And trigger the receiving operation through the interrupt on the network device ,

4) Network device and media layer :

The physical entity responsible for sending and receiving packets , The functions of the device driver function layer are physically driven here

The hierarchy is shown in the figure below :

 

3. Network card driver initialization

And our NIC driver , Just write the network device interface layer , fill net_device The content of the data structure and will net_device Register into the kernel , Set hardware related operations , Enable interrupt processing, etc

3.1 among net_device Important members of the structure , The arrangement is as follows :

 Copy code

struct net_device
{
       char               name[IFNAMSIZ];              // Network card device name
       unsigned long              mem_end;             // The end of memory address of the device
       unsigned long              mem_start;            // The starting memory address of the device
       unsigned long              base_addr;            // The memory of the device I/O Base address
       unsigned int          irq;                       // The interrupt number of the device
       unsigned char        if_port;                  // The type of port used by a multiport device
unsigned char        dma;                     // Of the device DMA passageway
       unsigned long              state;                    // Status information for network devices and network adapters
             
      struct net_device_stats* (*get_stats)(struct net_device *dev); // Get traffic statistics
// function ifconfig The member function is called , And return a net_device_stats Structure gets information
      struct net_device_stats  stats;      // Used to store statistics net_device_stats Structure
 
       unsigned long              features;        // Interface features ,     
       unsigned int          flags; //flags Refers to the network interface flag , With IFF_(Interface Flags) start
// When flags =IFF_UP(  When the device is activated and can start sending packets ,  The kernel sets the flag )、 IFF_AUTOMEDIA( Set up devices to switch between multiple media )、
IFF_BROADCAST(  Allow broadcasting )、IFF_DEBUG(  Debug mode ,  Can be used to control printk The level of detail of the call ) 、 IFF_LOOPBACK(  Loop )、
IFF_MULTICAST(  Allow multicast ) 、 IFF_NOARP(  Interface cannot execute ARP, Point to point interfaces don't need to run  ARP)  and IFF_POINTOPOINT(  The interface is connected to a point-to-point link )  etc. .
 
       unsigned        mtu;        // Maximum transmission unit , It's also called maximum packet
       unsigned short  type;    // The hardware type of the interface
       unsigned short   hard_header_len;     // Hardware frame header length , It is generally given as ETH_HLEN, namely 14
 
unsigned char   dev_addr[MAX_ADDR_LEN];      // Where the equipment is stored MAC Address
       unsigned long              last_rx;    // The time stamp of the received packet , call netif_rx() After the fu jiffies that will do
       unsigned long              trans_start;     // The time stamp for sending the packet , When it's going to be sent, put jiffies that will do
       unsigned char        dev_addr[MAX_ADDR_LEN];                //MAC Address
 
       int                 (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev);
                                   // Packet sending function , sk_buff It's the structure used to send and receive packets
void  (*tx_timeout) (struct net_device *dev); // Contract timeout handler
... ...
}

 Copy code

 

The statistics mentioned above net_device_stats Structure , The key members are as follows :

 Copy code

struct net_device_stats
{
       unsigned long       rx_packets;           /* Number of packets received */
       unsigned long       tx_packets;           /* Number of packets sent     */
       unsigned long       rx_bytes;               /* The number of bytes received , Can pass sk_buff Member of a structure len To get */
       unsigned long       tx_bytes;               /* Number of bytes sent , Can pass sk_buff Member of a structure len To get */
       unsigned long       rx_errors;              /* The number of error packets received */
       unsigned long       tx_errors;              /* The number of error packets sent */
       ... ...
}

 Copy code

 

 

3.2 therefore init function , The steps to initialize the NIC are as follows :

 

  • 1) Use alloc_netdev() To allocate a net_device Structure  
  • 2) Set the registers related to network card hardware
  • 3) Set up net_device Member of a structure
  • 4) Use register_netdev() To register net_device Structure

 

4. The network card drives the contract process

In kernel , When the upper layer sends a packet , Call the network device layer net_device Members of data structures hard_start_xmit() Send packets out .

hard_start_xmit() We need to build our own functions , The prototype of this function is as follows :

int    (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev);

 

In this function, we need to involve sk_buff Structure , It means (socket buffer) Socket buffer , It's used to transfer data between different layers of the network .

4.1 sk_buff A structure is a two-way list , The key members are as follows :

 Copy code

struct sk_buff {
       /* These two members must be first. */
       struct sk_buff        *next;      // Point to next sk_buff Structure
       struct sk_buff        *prev;     // Point to the previous sk_buff Structure
... ...
       unsigned int          len,    // The total length of the packet , Including linear data and nonlinear data
                            data_len,        // Nonlinear data length
                            mac_len;         //mac Baotou length
__u32       priority;          // The sk_buff The priority of the structure    
__be16      protocol;           // It stores the protocol type of the upper layer , Can pass eth_type_trans() To get
       ... ...
      sk_buff_data_t              transport_header;   // The offset value of the transport layer header
      sk_buff_data_t              network_header;    // The offset value of the network layer header
      sk_buff_data_t              mac_header;          //MAC The offset value of the data link layer header
 sk_buff_data_t              tail;                    // Point to the end of the packet in the buffer
      sk_buff_data_t              end;                     // Point to the end of the buffer
      unsigned char         *head,                   // Point to the beginning of the protocol header in the buffer
                                  *data;                   // Point to the beginning of the packet in the buffer
       ... ...
}

 Copy code

 

among sk_buff The space of the structure , As shown in the figure below :

 

among sk_buff-> data The packet format is shown in the figure below :

 

 

4.2 therefore ,hard_start_xmit() The processing steps of this function are as follows :

  • 1) Before sending the packet out , Need to use netif_stop_queue() To stop the packets from the upper layer ,
  • 2) Set register , Through the network device hardware , To send data
  • 2) When the packet goes out , Call again dev_kfree_skb() Function to release sk_buff, The prototype of this function is as follows :
void dev_kfree_skb(struct sk_buff *skb);

 

  • 3) When the packet is sent successfully , Will enter TX Interrupt function , Then update the statistics , call netif_wake_queue() Wake up , Start up the upper layer and continue .
  • 4) If the packet is sent out overtime , I can't get in TX Interrupt function , Will call net_device Structure of the (*tx_timeout) Timeout member function , Update statistics in this function , call netif_wake_queue() Wake up

 

among netif_wake_queue() and netif_stop_queue() The function prototype is as follows :

void netif_wake_queue(struct net_device *dev);  // Wake up the blocked upper layer , Start to continue sending packets to the network device driver layer
void netif_stop_queue(struct net_device *dev); // Prevent the upper layer from sending packets to the network device driver layer 

 

 

 5. The network card drives the packet receiving process

The receiving data packet is mainly processed by interrupt function , To determine the type of interrupt , If it is equal to ISQ_RECEIVER_EVENT, Denoted as receive interrupt , Then go to the receive data function , adopt netif_rx() Put the data up to the top

For example, as shown in the figure below , Refer to the network card driver in the kernel :/drivers/net/cs89x0.c

 

As shown in the figure above , By getting status Mark to determine what the interruption is , If it's a receive interrupt , To get into net_rx()

4.1 among net_rx() The processing steps of the packet receiving function are as follows :

  • 1) Use dev_alloc_skb() To construct a new sk_buff
  • 2) Use skb_reserve(rx_skb, 2); take sk_buff The packets in the buffer are shifted one after another 2 byte , To make room for sk_buff The head space in the buffer
  • 3) Read data received on network device hardware
  • 4) Use memcpy() Copy data to a new sk_buff Inside data The address the member points to , have access to skb_put() To dynamically expand sk_buff The data area in the structure
  • 5) Use eth_type_trans() To get the upper layer protocol , Assign return value to sk_buff Of protocol Among the members
  • 6) Then update the statistics , Finally using netif_rx( ) to sk_fuffer To the upper protocol

among skb_put() The function prototype is as follows :

static inline unsigned char *skb_put(struct sk_buff *skb, unsigned int len);
//len: Expand the data area down len byte 

 

Use skb_put() After the function , among sk_buff The buffer changes are shown in the figure below :

 

 

6. Write virtual NIC driver

This section will start to write a simple virtual network card driver , That is to say, no hardware related operations are required , So there's no interrupt function , We go through linux Of ping Command to implement the contract , Then forge a received in the contract function ping Package function , Realization can ping Through any ip Address

stay init In the initial function :

  • 1) Use alloc_netdev() To allocate a net_device Structure
  • 2) Set up net_device Member of a structure
  • 3) Use register_netdev() To register net_device Structure

In this function :

  • 1) Use netif_stop_queue() To prevent the upper layer from sending packets to the network device driver layer
  • 2) Call the packet receiving function , And put in the sent sk_buff buffer , There's a fake inside ping Package function
  • 3) Use dev_kfree_skb() Function to release the sent sk_buff Buffer zone
  • 4) Update sent Statistics
  • 5) Use netif_wake_queue() To wake up the blocked upper layer ,

In the packet receiving function :

First modify the sent sk_buff The data in the packet , Make it a receiving sk_buff, The data packet structure is shown in the figure below :

 

  • 1) You need to swap the ethhdr Structure ” Source / Purpose ”MAC Address
  • 2) You need to swap the iphdr Structure ” Source / Purpose ” IP Address
  • 3) Use ip_fast_csum() To recapture iphdr Check code of structure
  • 4) Set the data type of the data package in the figure above , Before it was sent ping package 0x08, Need to be changed to 0x00, To receive ping package
  • 5) Use dev_alloc_skb() To construct a new sk_buff
  • 6) Use skb_reserve(rx_skb, 2); take sk_buff The packets in the buffer are shifted one after another 2 byte , To make room for sk_buff The head space in the buffer
  • 7) Use memcpy() Change the previously modified sk_buff->data Copy to new sk_buff Inside data The address the member points to :
memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);
// skb_put(): To dynamically expand sk_buff The data area in the structure , Avoid spillovers 

  • 8) Set up the new sk_buff Other members
  • 9) Use eth_type_trans() To get the upper layer protocol , Assign return value to sk_buff Of protocol Among the members
  • 10) Then update the receiving Statistics , Finally using netif_rx( ) to sk_fuffer To the upper protocol

 

7. The specific driver code is as follows :

 Copy code

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/ip.h> 
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
static struct net_device    *virt_net;
static void virt_rs_packet(struct sk_buff *skb, struct net_device *dev)
{
       unsigned char *type;
       struct iphdr *ih;
       __be32 *saddr, *daddr, tmp;
       unsigned char tmp_dev_addr[ETH_ALEN];
       struct ethhdr *ethhdr;
       struct sk_buff *rx_skb;
    /*1)  Swap ethhdr Structure  " Source / Purpose "MAC Address */
       ethhdr = (struct ethhdr *)skb->data;
       memcpy(tmp_dev_addr, ethhdr->h_dest, ETH_ALEN);
       memcpy(ethhdr->h_dest, ethhdr->h_source, ETH_ALEN);
       memcpy(ethhdr->h_source, tmp_dev_addr, ETH_ALEN);
     /*2) Swap  iphdr Structure " Source / Purpose " IP Address */
       ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr));
       saddr = &ih->saddr;
       daddr = &ih->daddr;
       tmp = *saddr;
       *saddr = *daddr;
       *daddr = tmp;
   
    /*3) Use ip_fast_csum() To recapture iphdr Check code of structure */
        ih->check = 0;               
        ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);
    /*4) Set data type */
       type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr);
       *type = 0;      // Before it was sent ping package 0x08, Need to be changed to 0x00, To receive ping package
    /*5) Use dev_alloc_skb() To construct a new sk_buff   */
       rx_skb = dev_alloc_skb(skb->len + 2);
   
    /*6) Use skb_reserve() To make room for 2 Byte head space   */
       skb_reserve(rx_skb, 2);
   
    /*7) Use memcpy() Change the previously modified sk_buff->data Copy to new sk_buff in */
    memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len); // skb_put(): To dynamically expand sk_buff The data area in the structure , Avoid spillovers
    /*8) Set up the new sk_buff  Other members */
    rx_skb->dev = dev;
    rx_skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
    /*9) Use eth_type_trans() To get the upper layer protocol  */
    rx_skb->protocol = eth_type_trans(rx_skb, dev);
 
    /*10)  Update reception Statistics , And use netif_rx( ) Come on   Pass on sk_fuffer Receive package  */
       dev->stats.rx_packets++;                     
       dev->stats.rx_bytes += skb->len;
       dev->last_rx= jiffies;                       // Receiving time stamp
       netif_rx(rx_skb);
}
static int virt_send_packet(struct sk_buff *skb, struct net_device *dev)
{
  /*1) Use netif_stop_queue() To prevent the upper layer from sending packets to the network device driver layer */
    netif_stop_queue(dev);
 
   // Set the hardware to send packets during this period
  /*2) Call the packet receiving function , There's a fake inside ping Package function */
    virt_rs_packet(skb,dev);
 
 /*3) Use dev_kfree_skb() Function to release the sent sk_buff Buffer zone */
     dev_kfree_skb(skb);
 /*4) Update sent Statistics */
    dev->stats.tx_packets++;            // Successfully sent a packet
    dev->stats.tx_bytes+=skb->len;  // Successfully sent len Long byte
    dev->trans_start = jiffies;            // Send timestamp
 /*5) Use netif_wake_queue() To wake up the blocked upper layer */
    netif_wake_queue(dev); 
    return 0;
}
 
static int virt_net_init(void)
{
    /*1) Use alloc_netdev() To allocate a net_device Structure */
    virt_net= alloc_netdev(sizeof(struct net_device), "virt_eth0", ether_setup);
    /*2) Set up net_device Member of a structure  */
    virt_net->hard_start_xmit      = virt_send_packet;
    virt_net->dev_addr[0] = 0x08;  
    virt_net->dev_addr[1] = 0x89;
    virt_net->dev_addr[2] = 0x89;
    virt_net->dev_addr[3] = 0x89;
    virt_net->dev_addr[4] = 0x89;
    virt_net->dev_addr[5] = 0x89;
    virt_net->flags           |= IFF_NOARP;
    virt_net->features        |= NETIF_F_NO_CSUM;
   /*3) Use register_netdev() To register net_device Structure  */
    register_netdev(virt_net);
    return 0;
}
static void virt_net_exit(void)
{
    unregister_netdev(virt_net);
    free_netdev(virt_net);   
}
module_init(virt_net_init);
module_exit(virt_net_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("by:zhang");

 Copy code

8. test run

Mount driver , As shown in the figure below , You can see net Class has this network card device

 

Start the test , First, set the network card device's ip, Then go to ping Let's talk about the others ip, As shown in the figure below :

 

The image above ping, Why it's successful , Because we're in the contract function , I got a fake bag , adopt netif_rx() To upload the package to the upper layer

Use ifconfig, You can see that the statistical information of this network card device has been sent and received 6 A package , And the total data sent and received

 

 

 

Next section will begin to learn network card chip DM9000C

How to write migration DM9000C Network card driver : http://www.cnblogs.com/lifexy/p/7777961.html