• 驱动11.网卡驱动程序


    1 网络传输的简介

      

      (1)接收过程,如上图,网络上的数据包到达网卡后,网卡产生中断,然后设备驱动层收到中断后,开始进行网络包的接收,接收完之后调用一个netif_rx函数交给网络协议层(层次结构上图一),然后就是一层一层的网上传到用户空间了。

      (2)发送过程,从用户空间过来的数据包,经过层层穿越之后,到达网络协议层,然后调用一个dev_queue_xmit()函数之后就不管了,剩下的交给驱动层经过处理后,使用函数hard_start_xmit()函数发送,然后硬件上网卡开始发送数据包了。

    2 dm9000网卡驱动源代码分析

     2.1 首先,看入口函数

    1 static int __init dm9000_init(void)
    2 {
    3     printk(KERN_INFO "%s Ethernet Driver
    ", CARDNAME);
    4 
    5     return platform_driver_register(&dm9000_driver);    /* search board and register */
    6 }

    这是一个平台总线的结构,注册dm9000_driver。

     1 static struct platform_driver dm9000_driver = {
     2     .driver    = {
     3         .name    = "dm9000",
     4         .owner     = THIS_MODULE,
     5     },
     6     .probe   = dm9000_probe,
     7     .remove  = dm9000_drv_remove,
     8     .suspend = dm9000_drv_suspend,
     9     .resume  = dm9000_drv_resume,
    10 };

    如果平台存在与其同名的平台设备,将调用probe函数

    2.2 probe函数

      1 /*
      2  * Search DM9000 board, allocate space and register it
      3  */
      4 static int
      5 dm9000_probe(struct platform_device *pdev)
      6 {
      7     struct dm9000_plat_data *pdata = pdev->dev.platform_data;
      8     struct board_info *db;    /* Point a board information structure */
      9     struct net_device *ndev;
     10     unsigned long base;
     11     int ret = 0;
     12     int iosize;
     13     int i;
     14     u32 id_val;
     15 
     16 #if defined(CONFIG_ARCH_S3C2410xxx)
     17     unsigned int oldval_bwscon;        /* 用来保存BWSCON寄存器的值 */
     18     unsigned int oldval_bankcon4;    /* 用来保存S3C2410_BANKCON4寄存器的值 */
     19 #endif
     20 
     21     /* Init network device */
     22     ndev = alloc_etherdev(sizeof (struct board_info));
     23     if (!ndev) {
     24         printk("%s: could not allocate device.
    ", CARDNAME);
     25         return -ENOMEM;
     26     }
     27 
     28     SET_MODULE_OWNER(ndev);
     29     SET_NETDEV_DEV(ndev, &pdev->dev);
     30 
     31     PRINTK2("dm9000_probe()");
     32 
     33 #if defined(CONFIG_ARCH_S3C2410xxx)
     34     /* 设置Bank4: 总线宽度为16, 使能nWAIT。by www.100ask.net */
     35     oldval_bwscon = *((volatile unsigned int *)S3C2410_BWSCON);
     36     *((volatile unsigned int *)S3C2410_BWSCON) = (oldval_bwscon & ~(3<<16)) 
     37         | S3C2410_BWSCON_DW4_16 | S3C2410_BWSCON_WS4 | S3C2410_BWSCON_ST4;
     38 
     39     /* 设置BANK4的时间参数, by www.100ask.net */
     40     oldval_bankcon4 = *((volatile unsigned int *)S3C2410_BANKCON4);
     41     *((volatile unsigned int *)S3C2410_BANKCON4) = 0x1f7c;
     42 #endif
     43 
     44     /* setup board info structure */
     45     db = (struct board_info *) ndev->priv;
     46     memset(db, 0, sizeof (*db));
     47 
     48     spin_lock_init(&db->lock);
     49 
     50     if (pdev->num_resources < 2) {
     51         ret = -ENODEV;
     52         goto out;
     53     } else if (pdev->num_resources == 2) {
     54         base = pdev->resource[0].start;
     55 
     56         if (!request_mem_region(base, 4, ndev->name)) {
     57             ret = -EBUSY;
     58             goto out;
     59         }
     60 
     61         ndev->base_addr = base;
     62         ndev->irq = pdev->resource[1].start;
     63         db->io_addr = (void __iomem *)base;
     64         db->io_data = (void __iomem *)(base + 4);
     65 
     66     } else {
     67         db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
     68         db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
     69         db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
     70 
     71         if (db->addr_res == NULL || db->data_res == NULL ||
     72             db->irq_res == NULL) {
     73             printk(KERN_ERR PFX "insufficient resources
    ");
     74             ret = -ENOENT;
     75             goto out;
     76         }
     77 
     78         i = res_size(db->addr_res);
     79         db->addr_req = request_mem_region(db->addr_res->start, i,
     80                           pdev->name);
     81 
     82         if (db->addr_req == NULL) {
     83             printk(KERN_ERR PFX "cannot claim address reg area
    ");
     84             ret = -EIO;
     85             goto out;
     86         }
     87 
     88         db->io_addr = ioremap(db->addr_res->start, i);
     89 
     90         if (db->io_addr == NULL) {
     91             printk(KERN_ERR "failed to ioremap address reg
    ");
     92             ret = -EINVAL;
     93             goto out;
     94         }
     95 
     96         iosize = res_size(db->data_res);
     97         db->data_req = request_mem_region(db->data_res->start, iosize,
     98                           pdev->name);
     99 
    100         if (db->data_req == NULL) {
    101             printk(KERN_ERR PFX "cannot claim data reg area
    ");
    102             ret = -EIO;
    103             goto out;
    104         }
    105 
    106         db->io_data = ioremap(db->data_res->start, iosize);
    107 
    108         if (db->io_data == NULL) {
    109             printk(KERN_ERR "failed to ioremap data reg
    ");
    110             ret = -EINVAL;
    111             goto out;
    112         }
    113 
    114         /* fill in parameters for net-dev structure */
    115 
    116         ndev->base_addr = (unsigned long)db->io_addr;
    117         ndev->irq    = db->irq_res->start;
    118 
    119         /* ensure at least we have a default set of IO routines */
    120         dm9000_set_io(db, iosize);
    121     }
    122 
    123     /* check to see if anything is being over-ridden */
    124     if (pdata != NULL) {
    125         /* check to see if the driver wants to over-ride the
    126          * default IO width */
    127 
    128         if (pdata->flags & DM9000_PLATF_8BITONLY)
    129             dm9000_set_io(db, 1);
    130 
    131         if (pdata->flags & DM9000_PLATF_16BITONLY)
    132             dm9000_set_io(db, 2);
    133 
    134         if (pdata->flags & DM9000_PLATF_32BITONLY)
    135             dm9000_set_io(db, 4);
    136 
    137         /* check to see if there are any IO routine
    138          * over-rides */
    139 
    140         if (pdata->inblk != NULL)
    141             db->inblk = pdata->inblk;
    142 
    143         if (pdata->outblk != NULL)
    144             db->outblk = pdata->outblk;
    145 
    146         if (pdata->dumpblk != NULL)
    147             db->dumpblk = pdata->dumpblk;
    148     }
    149 
    150     dm9000_reset(db);
    151 
    152     /* try two times, DM9000 sometimes gets the first read wrong */
    153     for (i = 0; i < 2; i++) {
    154         id_val  = ior(db, DM9000_VIDL);
    155         id_val |= (u32)ior(db, DM9000_VIDH) << 8;
    156         id_val |= (u32)ior(db, DM9000_PIDL) << 16;
    157         id_val |= (u32)ior(db, DM9000_PIDH) << 24;
    158 
    159         if (id_val == DM9000_ID)
    160             break;
    161         printk("%s: read wrong id 0x%08x
    ", CARDNAME, id_val);
    162     }
    163 
    164     if (id_val != DM9000_ID) {
    165         printk("%s: wrong id: 0x%08x
    ", CARDNAME, id_val);
    166         goto release;
    167     }
    168 
    169     /* from this point we assume that we have found a DM9000 */
    170 
    171     /* driver system function */
    172     ether_setup(ndev);
    173 
    174     ndev->open         = &dm9000_open;
    175     ndev->hard_start_xmit    = &dm9000_start_xmit;
    176     ndev->tx_timeout         = &dm9000_timeout;
    177     ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
    178     ndev->stop         = &dm9000_stop;
    179     ndev->get_stats         = &dm9000_get_stats;
    180     ndev->set_multicast_list = &dm9000_hash_table;
    181 #ifdef CONFIG_NET_POLL_CONTROLLER
    182     ndev->poll_controller     = &dm9000_poll_controller;
    183 #endif
    184 
    185 #ifdef DM9000_PROGRAM_EEPROM
    186     program_eeprom(db);
    187 #endif
    188     db->msg_enable       = NETIF_MSG_LINK;
    189     db->mii.phy_id_mask  = 0x1f;
    190     db->mii.reg_num_mask = 0x1f;
    191     db->mii.force_media  = 0;
    192     db->mii.full_duplex  = 0;
    193     db->mii.dev         = ndev;
    194     db->mii.mdio_read    = dm9000_phy_read;
    195     db->mii.mdio_write   = dm9000_phy_write;
    196 
    197     /* Read SROM content */
    198     for (i = 0; i < 64; i++)
    199         ((u16 *) db->srom)[i] = read_srom_word(db, i);
    200 
    201     /* Set Node Address */
    202     for (i = 0; i < 6; i++)
    203         ndev->dev_addr[i] = db->srom[i];
    204 
    205     if (!is_valid_ether_addr(ndev->dev_addr)) {
    206         /* try reading from mac */
    207 
    208         for (i = 0; i < 6; i++)
    209             ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
    210     }
    211 
    212     if (!is_valid_ether_addr(ndev->dev_addr)) {
    213         printk("%s: Invalid ethernet MAC address.  Please "
    214                "set using ifconfig
    ", ndev->name);
    215 #if defined(CONFIG_ARCH_S3C2410)
    216         printk("Now use the default MAC address: 08:90:90:90:90:90
    ");
    217         ndev->dev_addr[0] = 0x08;
    218         ndev->dev_addr[1] = 0x90;
    219         ndev->dev_addr[2] = 0x90;
    220         ndev->dev_addr[3] = 0x90;
    221         ndev->dev_addr[4] = 0x90;
    222         ndev->dev_addr[5] = 0x90;
    223 #endif
    224     }
    225 
    226     platform_set_drvdata(pdev, ndev);
    227     ret = register_netdev(ndev);
    228 
    229     if (ret == 0) {
    230         printk("%s: dm9000 at %p,%p IRQ %d MAC: ",
    231                ndev->name,  db->io_addr, db->io_data, ndev->irq);
    232         for (i = 0; i < 5; i++)
    233             printk("%02x:", ndev->dev_addr[i]);
    234         printk("%02x
    ", ndev->dev_addr[5]);
    235     }
    236     return 0;
    237 
    238  release:
    239  out:
    240     printk("%s: not found (%d).
    ", CARDNAME, ret);
    241 #if defined(CONFIG_ARCH_S3C2410xxx)
    242     /* 恢复寄存器原来的值 */
    243     *((volatile unsigned int *)S3C2410_BWSCON) = oldval_bwscon;
    244     *((volatile unsigned int *)S3C2410_BANKCON4) = oldval_bankcon4;
    245 #endif
    246     dm9000_release_board(pdev, db);
    247     free_netdev(ndev);
    248 
    249     return ret;
    250 }
    probe函数

    主要完后以下工作:

    ①分配一个net_device结构体(alloc_etherdev)

    ②定义一个单板相关信息board_info结构体

    ③设置net_device和board_info结构体

    ④使用board_info信息来初始化单板

    ⑤初始化网卡

    ⑥设置操作函数open,hard_start_xmit(发包函数),tx_timeout(超时函数),watchdog_timeo(看门狗函数),stop,get_stats,set_multicast_list

    设置MAC地址

    最后,注册net_device结构体

    2.3 发包函数

     1 /*
     2  *  Hardware start transmission.
     3  *  Send a packet to media from the upper layer.
     4  */
     5 static int
     6 dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
     7 {
     8     board_info_t *db = (board_info_t *) dev->priv;
     9 
    10     PRINTK3("dm9000_start_xmit
    ");
    11 
    12     if (db->tx_pkt_cnt > 1)
    13         return 1;
    14 
    15     netif_stop_queue(dev);
    16 
    17     /* Disable all interrupts */
    18     iow(db, DM9000_IMR, IMR_PAR);
    19 
    20     /* Move data to DM9000 TX RAM */
    21     writeb(DM9000_MWCMD, db->io_addr);
    22 
    23     (db->outblk)(db->io_data, skb->data, skb->len);
    24     db->stats.tx_bytes += skb->len;
    25 
    26     /* TX control: First packet immediately send, second packet queue */
    27     if (db->tx_pkt_cnt == 0) {
    28 
    29         /* First Packet */
    30         db->tx_pkt_cnt++;
    31 
    32         /* Set TX length to DM9000 */
    33         iow(db, DM9000_TXPLL, skb->len & 0xff);
    34         iow(db, DM9000_TXPLH, (skb->len >> 8) & 0xff);
    35 
    36         /* Issue TX polling command */
    37         iow(db, DM9000_TCR, TCR_TXREQ);    /* Cleared after TX complete */
    38 
    39         dev->trans_start = jiffies;    /* save the time stamp */
    40 
    41     } else {
    42         /* Second packet */
    43         db->tx_pkt_cnt++;
    44         db->queue_pkt_len = skb->len;
    45     }
    46 
    47     /* free this SKB */
    48     dev_kfree_skb(skb);
    49 
    50     /* Re-enable resource check */
    51     if (db->tx_pkt_cnt == 1)
    52         netif_wake_queue(dev);
    53 
    54     /* Re-enable interrupt */
    55     iow(db, DM9000_IMR, IMR_PAR | IMR_PTM | IMR_PRM);
    56 
    57     return 0;
    58 }
    发包函数

    主要完后以下工作:

    1.停止该网卡的队列,把skb数据写入网卡netif_stop_queue(dev);
    2.构造一个虚假的skb_buff上报
    3.释放skb(dev_kfree_skb)

    4.数据全部发送出去后唤醒网卡队列(netif_wake_queue)

    5.更新统计信息

    3 写代码

      1 /*
      2  * 参考 drivers
    etcs89x0.c
      3  */
      4 
      5 #include <linux/module.h>
      6 #include <linux/errno.h>
      7 #include <linux/netdevice.h>
      8 #include <linux/etherdevice.h>
      9 #include <linux/kernel.h>
     10 #include <linux/types.h>
     11 #include <linux/fcntl.h>
     12 #include <linux/interrupt.h>
     13 #include <linux/ioport.h>
     14 #include <linux/in.h>
     15 #include <linux/skbuff.h>
     16 #include <linux/slab.h>
     17 #include <linux/spinlock.h>
     18 #include <linux/string.h>
     19 #include <linux/init.h>
     20 #include <linux/bitops.h>
     21 #include <linux/delay.h>
     22 #include <linux/ip.h>
     23 
     24 #include <asm/system.h>
     25 #include <asm/io.h>
     26 #include <asm/irq.h>
     27 
     28 static struct net_device *vnet_dev;
     29 
     30 static void emulator_rx_packet(struct sk_buff *skb, struct net_device *dev)
     31 {
     32     /* 参考LDD3 */
     33     unsigned char *type;
     34     struct iphdr *ih;
     35     __be32 *saddr, *daddr, tmp;
     36     unsigned char    tmp_dev_addr[ETH_ALEN];
     37     struct ethhdr *ethhdr;
     38     
     39     struct sk_buff *rx_skb;
     40         
     41     // 从硬件读出/保存数据
     42     /* 对调"源/目的"的mac地址 */
     43     ethhdr = (struct ethhdr *)skb->data;
     44     memcpy(tmp_dev_addr, ethhdr->h_dest, ETH_ALEN);
     45     memcpy(ethhdr->h_dest, ethhdr->h_source, ETH_ALEN);
     46     memcpy(ethhdr->h_source, tmp_dev_addr, ETH_ALEN);
     47 
     48     /* 对调"源/目的"的ip地址 */    
     49     ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr));
     50     saddr = &ih->saddr;
     51     daddr = &ih->daddr;
     52 
     53     tmp = *saddr;
     54     *saddr = *daddr;
     55     *daddr = tmp;
     56     
     57     //((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */
     58     //((u8 *)daddr)[2] ^= 1;
     59     type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr);
     60     //printk("tx package type = %02x
    ", *type);
     61     // 修改类型, 原来0x8表示ping
     62     *type = 0; /* 0表示reply */
     63     
     64     ih->check = 0;           /* and rebuild the checksum (ip needs it) */
     65     ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);
     66     
     67     // 构造一个sk_buff
     68     rx_skb = dev_alloc_skb(skb->len + 2);
     69     skb_reserve(rx_skb, 2); /* align IP on 16B boundary */    
     70     memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);
     71 
     72     /* Write metadata, and then pass to the receive level */
     73     rx_skb->dev = dev;
     74     rx_skb->protocol = eth_type_trans(rx_skb, dev);
     75     rx_skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
     76     dev->stats.rx_packets++;
     77     dev->stats.rx_bytes += skb->len;
     78 
     79     // 提交sk_buff
     80     netif_rx(rx_skb);
     81 }
     82 
     83 static int virt_net_send_packet(struct sk_buff *skb, struct net_device *dev)
     84 {
     85     static int cnt = 0;
     86     printk("virt_net_send_packet cnt = %d
    ", ++cnt);
     87 
     88     /* 对于真实的网卡, 把skb里的数据通过网卡发送出去 */
     89     netif_stop_queue(dev); /* 停止该网卡的队列 */
     90     /* ...... */           /* 把skb的数据写入网卡 */
     91 
     92     /* 构造一个假的sk_buff,上报 */
     93     emulator_rx_packet(skb, dev);
     94 
     95     dev_kfree_skb (skb);   /* 释放skb */
     96     netif_wake_queue(dev); /* 数据全部发送出去后,唤醒网卡的队列 */
     97 
     98     /* 更新统计信息 */
     99     dev->stats.tx_packets++;
    100     dev->stats.tx_bytes += skb->len;
    101     
    102     return 0;
    103 }
    104 
    105 
    106 static int virt_net_init(void)
    107 {
    108     /* 1. 分配一个net_device结构体 */
    109     vnet_dev = alloc_netdev(0, "vnet%d", ether_setup);;  /* alloc_etherdev */
    110 
    111     /* 2. 设置 */
    112     vnet_dev->hard_start_xmit = virt_net_send_packet;
    113 
    114     /* 设置MAC地址 */
    115     vnet_dev->dev_addr[0] = 0x08;
    116     vnet_dev->dev_addr[1] = 0x89;
    117     vnet_dev->dev_addr[2] = 0x89;
    118     vnet_dev->dev_addr[3] = 0x89;
    119     vnet_dev->dev_addr[4] = 0x89;
    120     vnet_dev->dev_addr[5] = 0x11;
    121 
    122     /* 设置下面两项才能ping通 */
    123     vnet_dev->flags           |= IFF_NOARP;
    124     vnet_dev->features        |= NETIF_F_NO_CSUM;    
    125 
    126     /* 3. 注册 */
    127     //register_netdevice(vnet_dev);
    128     register_netdev(vnet_dev);
    129     
    130     return 0;
    131 }
    132 
    133 static void virt_net_exit(void)
    134 {
    135     unregister_netdev(vnet_dev);
    136     free_netdev(vnet_dev);
    137 }
    138 
    139 module_init(virt_net_init);
    140 module_exit(virt_net_exit);
    141 
    142 MODULE_AUTHOR("thisway.diy@163.com,17653039@qq.com");
    143 MODULE_LICENSE("GPL");
    virtnet.c
  • 相关阅读:
    android-为应用单元测试
    android手机拨号器实现
    android模拟器使用
    android开发环境搭建
    C语言之关键字
    linux shell脚本基础-----3
    linux shell脚本基础-----2
    linux shell脚本基础-----1
    Android学习计划
    MySql 绿色版配置
  • 原文地址:https://www.cnblogs.com/Lwd-linux/p/6357564.html
Copyright © 2020-2023  润新知