ARM-Linux驱动--DM9000网卡驱动分析(二)

来源:互联网 发布:大数据的经典案例 编辑:程序博客网 时间:2024/04/28 11:57

硬件平台:FL2440(s3c2440)

内核版本:2.6.35

主机平台:Ubuntu 11.04

内核版本:2.6.39

原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/6612623

下面开始分析具体的代码,这里由于使DM9000驱动更容易理解,在不影响基本的功能的前提下,这里将尽可能的简化该驱动(如:去掉该驱动中支持电源管理的功能)

分析该驱动

1、首先看一下该驱动的平台设备驱动的结构体定义

[cpp] view plaincopy
  1. /*平台设备驱动的结构体定义 
  2. *在该结构体中可以定义有关Power Management的管理函数 
  3. *该驱动中将其省略,侧重分析dm9000的基本原理 
  4. */  
  5. static struct platform_driver dm9000_driver = {  
  6.     .driver = {  
  7.         .name    = "dm9000",/* 该名称和系统初始化中,平台设备的名称一致 */  
  8.         .owner   = THIS_MODULE,  
  9.     },  
  10.     .probe   = dm9000_probe,/* 资源探测函数 */  
  11.     .remove  = __devexit_p(dm9000_drv_remove),/* 设备移除函数 */  
  12. };  
在执行insmod后内核自动那个执行下面的函数
[cpp] view plaincopy
  1. static int __init  
  2. dm9000_init(void)  
  3. {  
  4.     printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);  
  5.   
  6.     return platform_driver_register(&dm9000_driver);  
  7. }  

调用函数platform_driver_register()函数注册驱动。

3、自动执行驱动的probe函数,进行资源的探测和申请资源。

其中BWSCON为总线宽度 等待控制寄存器


其中第[19:18]位的作用如下


下面函数中将两位设置为11,也就是WAIT使能,bank4使用UB/LB。

alloc_etherdev()函数分配一个网络设备的结构体,原型在include/linux/etherdevice.h

原型如下:

[cpp] view plaincopy
  1. extern struct net_device *alloc_etherdev_mq(int sizeof_priv, unsigned int queue_count);  
  2. #define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)  
该函数中需要将获得的资源信息存储在一个结构体中,定义如下:

[cpp] view plaincopy
  1. /* Structure/enum declaration ------------------------------- */  
  2. typedef struct board_info {  
  3.   
  4.     void __iomem    *io_addr;   /* Register I/O base address */  
  5.     void __iomem    *io_data;   /* Data I/O address */  
  6.     u16      irq;       /* IRQ */  
  7.   
  8.     u16     tx_pkt_cnt;  
  9.     u16     queue_pkt_len;  
  10.     u16     queue_start_addr;  
  11.     u16     queue_ip_summed;  
  12.     u16     dbug_cnt;  
  13.     u8      io_mode;        /* 0:word, 2:byte */  
  14.     u8      phy_addr;  
  15.     u8      imr_all;  
  16.   
  17.     unsigned int    flags;  
  18.     unsigned int    in_suspend :1;  
  19.     unsigned int    wake_supported :1;  
  20.     int     debug_level;  
  21.   
  22.     enum dm9000_type type;  
  23.   
  24.     void (*inblk)(void __iomem *port, void *data, int length);  
  25.     void (*outblk)(void __iomem *port, void *data, int length);  
  26.     void (*dumpblk)(void __iomem *port, int length);  
  27.   
  28.     struct device   *dev;        /* parent device */  
  29.   
  30.     struct resource *addr_res;   /* resources found */  
  31.     struct resource *data_res;  
  32.     struct resource *addr_req;   /* resources requested */  
  33.     struct resource *data_req;  
  34.     struct resource *irq_res;  
  35.   
  36.     int      irq_wake;  
  37.   
  38.     struct mutex     addr_lock; /* phy and eeprom access lock */  
  39.   
  40.     struct delayed_work phy_poll;  
  41.     struct net_device  *ndev;  
  42.   
  43.     spinlock_t  lock;  
  44.   
  45.     struct mii_if_info mii;  
  46.     u32     msg_enable;  
  47.     u32     wake_state;  
  48.   
  49.     int     rx_csum;  
  50.     int     can_csum;  
  51.     int     ip_summed;  
  52. } board_info_t;  

下面是probe函数,

其中有个函数db = netdev_priv(ndev)

该函数实际上是返回网卡私有成员的数据结构地址

函数如下,定义在include/linux/net_device.h中

[cpp] view plaincopy
  1. static inline void *netdev_priv(const struct net_device *dev)  
  2. {  
  3.     return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN);  
  4. }  

[cpp] view plaincopy
  1. /* 
  2.  * Search DM9000 board, allocate space and register it 
  3.  */  
  4. static int __devinit  
  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.     const unsigned char *mac_src;  
  11.     int ret = 0;  
  12.     int iosize;  
  13.     int i;  
  14.     u32 id_val;  
  15.   
  16.     unsigned char ne_def_eth_mac_addr[]={0x00,0x12,0x34,0x56,0x80,0x49};/* 设定默认的mac地址 */  
  17.     static void *bwscon;/* 保存ioremap返回的寄存器的虚拟地址,下同 */  
  18.     static void *gpfcon;  
  19.     static void *extint0;  
  20.     static void *intmsk;  
  21.     /*Added by yan*/  
  22.     #define BWSCON           (0x48000000)  
  23.     #define GPFCON           (0x56000050)  
  24.     #define EXTINT0           (0x56000088)  
  25.     #define INTMSK           (0x4A000008)  
  26.   
  27.     bwscon=ioremap_nocache(BWSCON,0x0000004);  
  28.     gpfcon=ioremap_nocache(GPFCON,0x0000004);  
  29.     extint0=ioremap_nocache(EXTINT0,0x0000004);  
  30.     intmsk=ioremap_nocache(INTMSK,0x0000004);  
  31.   
  32.     writel( readl(bwscon)|0xc0000,bwscon);/* 将BWSCON寄存器[19:18]设置为11 */  
  33.     writel( (readl(gpfcon) & ~(0x3 << 14)) | (0x2 << 14), gpfcon); /* 设置GPF寄存器 */  
  34.     writel( readl(gpfcon) | (0x1 << 7), gpfcon); // Disable pull-up,不使能上拉  
  35.     writel( (readl(extint0) & ~(0xf << 28)) | (0x4 << 28), extint0); //rising edge,设置上升沿触发中断  
  36.     writel( (readl(intmsk))  & ~0x80, intmsk);/* 设置中断屏蔽寄存器 */  
  37.           
  38.     /*End of add*/  
  39.     /* Init network device */  
  40.     /* 使用alloc_etherdev()函数分配一个网络设备的结构体,原型在include/linux/etherdevice.h */  
  41.     ndev = alloc_etherdev(sizeof(struct board_info));  
  42.     if (!ndev) {  
  43.         dev_err(&pdev->dev, "could not allocate device.\n");  
  44.         return -ENOMEM;  
  45.     }  
  46.   
  47. /*通过SET_NETDEV_DEV(netdev, &pdev->dev)宏设置net_device.device->parent为当前的pci_device->device 
  48. *(这儿net_device包含的是device结构,而不是指针)。这样,就建立起了net_device到device的联系。 
  49. */  
  50.     SET_NETDEV_DEV(ndev, &pdev->dev);  
  51.   
  52.     dev_dbg(&pdev->dev, "dm9000_probe()\n");  
  53.   
  54.     /* setup board info structure */  
  55.     /* 下面都是设置board_info结构体 */  
  56.     db = netdev_priv(ndev);/* 返回dev->priv的地址 */  
  57.   
  58.     db->dev = &pdev->dev;  
  59.     db->ndev = ndev;  
  60.   
  61.     spin_lock_init(&db->lock);  
  62.     mutex_init(&db->addr_lock);  
  63.   
  64.     INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);  
  65.   
  66.     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  67.     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);  
  68.     db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);  
  69.   
  70.     if (db->addr_res == NULL || db->data_res == NULL ||  
  71.         db->irq_res == NULL) {  
  72.         dev_err(db->dev, "insufficient resources\n");  
  73.         ret = -ENOENT;  
  74.         goto out;  
  75.     }  
  76.   
  77.     db->irq_wake = platform_get_irq(pdev, 1);  
  78.     if (db->irq_wake >= 0) {  
  79.         dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake);  
  80.   
  81.         ret = request_irq(db->irq_wake, dm9000_wol_interrupt,  
  82.                   IRQF_SHARED, dev_name(db->dev), ndev);  
  83.         if (ret) {  
  84.             dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret);  
  85.         } else {  
  86.   
  87.             /* test to see if irq is really wakeup capable */  
  88.             ret = set_irq_wake(db->irq_wake, 1);  
  89.             if (ret) {  
  90.                 dev_err(db->dev, "irq %d cannot set wakeup (%d)\n",  
  91.                     db->irq_wake, ret);  
  92.                 ret = 0;  
  93.             } else {  
  94.                 set_irq_wake(db->irq_wake, 0);  
  95.                 db->wake_supported = 1;  
  96.             }  
  97.         }  
  98.     }  
  99.   
  100.     iosize = resource_size(db->addr_res);  
  101.     db->addr_req = request_mem_region(db->addr_res->start, iosize,  
  102.                       pdev->name);  
  103.   
  104.     if (db->addr_req == NULL) {  
  105.         dev_err(db->dev, "cannot claim address reg area\n");  
  106.         ret = -EIO;  
  107.         goto out;  
  108.     }  
  109.   
  110.     db->io_addr = ioremap(db->addr_res->start, iosize);  
  111.   
  112.     if (db->io_addr == NULL) {  
  113.         dev_err(db->dev, "failed to ioremap address reg\n");  
  114.         ret = -EINVAL;  
  115.         goto out;  
  116.     }  
  117.   
  118.     iosize = resource_size(db->data_res);  
  119.     db->data_req = request_mem_region(db->data_res->start, iosize,  
  120.                       pdev->name);  
  121.   
  122.     if (db->data_req == NULL) {  
  123.         dev_err(db->dev, "cannot claim data reg area\n");  
  124.         ret = -EIO;  
  125.         goto out;  
  126.     }  
  127.   
  128.     db->io_data = ioremap(db->data_res->start, iosize);  
  129.   
  130.     if (db->io_data == NULL) {  
  131.         dev_err(db->dev, "failed to ioremap data reg\n");  
  132.         ret = -EINVAL;  
  133.         goto out;  
  134.     }  
  135.     /* 设置结构体board_info结束 */  
  136.       
  137.     /* fill in parameters for net-dev structure */  
  138.     ndev->base_addr = (unsigned long)db->io_addr;/* 设置网络设备的地址 */  
  139.     ndev->irq    = db->irq_res->start;/* 设置网络设备的中断资源地址 */  
  140.   
  141.     /* ensure at least we have a default set of IO routines */  
  142.     dm9000_set_io(db, iosize);  
  143.   
  144.     /* check to see if anything is being over-ridden */  
  145.       
  146.     /*根据pdev->dev.platform_data的信息判断IO的宽度并设置相应的宽度*/  
  147.     if (pdata != NULL) {  
  148.         /* check to see if the driver wants to over-ride the 
  149.          * default IO width */  
  150.   
  151.         if (pdata->flags & DM9000_PLATF_8BITONLY)  
  152.             dm9000_set_io(db, 1);  
  153.   
  154.         if (pdata->flags & DM9000_PLATF_16BITONLY)  
  155.             dm9000_set_io(db, 2);  
  156.   
  157.         if (pdata->flags & DM9000_PLATF_32BITONLY)  
  158.             dm9000_set_io(db, 4);  
  159.   
  160.         /* check to see if there are any IO routine 
  161.          * over-rides */  
  162.   
  163.         if (pdata->inblk != NULL)  
  164.             db->inblk = pdata->inblk;  
  165.   
  166.         if (pdata->outblk != NULL)  
  167.             db->outblk = pdata->outblk;  
  168.   
  169.         if (pdata->dumpblk != NULL)  
  170.             db->dumpblk = pdata->dumpblk;  
  171.   
  172.         db->flags = pdata->flags;  
  173.     }  
  174.   
  175. #ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL  
  176.     db->flags |= DM9000_PLATF_SIMPLE_PHY;  
  177. #endif  
  178.   
  179.     dm9000_reset(db);/* 复位 */  
  180.   
  181.     /* try multiple times, DM9000 sometimes gets the read wrong */  
  182.     for (i = 0; i < 8; i++) {  
  183.         id_val  = ior(db, DM9000_VIDL);  
  184.         id_val |= (u32)ior(db, DM9000_VIDH) << 8;  
  185.         id_val |= (u32)ior(db, DM9000_PIDL) << 16;  
  186.         id_val |= (u32)ior(db, DM9000_PIDH) << 24;  
  187.   
  188.         if (id_val == DM9000_ID)  
  189.             break;  
  190.         dev_err(db->dev, "read wrong id 0x%08x\n", id_val);  
  191.     }  
  192.   
  193.     if (id_val != DM9000_ID) {  
  194.         dev_err(db->dev, "wrong id: 0x%08x\n", id_val);  
  195.         ret = -ENODEV;  
  196.         goto out;  
  197.     }  
  198.   
  199.     /* Identify what type of DM9000 we are working on */  
  200.   
  201.     id_val = ior(db, DM9000_CHIPR);  
  202.     dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);  
  203.   
  204.     switch (id_val) {  
  205.     case CHIPR_DM9000A:  
  206.         db->type = TYPE_DM9000A;  
  207.         break;  
  208.     case CHIPR_DM9000B:  
  209.         db->type = TYPE_DM9000B;  
  210.         break;  
  211.     default:  
  212.         dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);  
  213.         db->type = TYPE_DM9000E;  
  214.     }  
  215.   
  216.     /* dm9000a/b are capable of hardware checksum offload */  
  217.     if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {  
  218.         db->can_csum = 1;  
  219.         db->rx_csum = 1;  
  220.         ndev->features |= NETIF_F_IP_CSUM;  
  221.     }  
  222.   
  223.     /* from this point we assume that we have found a DM9000 */  
  224.   
  225.     /* driver system function */  
  226.     ether_setup(ndev);  
  227.   
  228.     ndev->netdev_ops = &dm9000_netdev_ops;  
  229.     ndev->watchdog_timeo = msecs_to_jiffies(watchdog);  
  230.     ndev->ethtool_ops    = &dm9000_ethtool_ops;  
  231.   
  232.     db->msg_enable       = NETIF_MSG_LINK;  
  233.     db->mii.phy_id_mask  = 0x1f;  
  234.     db->mii.reg_num_mask = 0x1f;  
  235.     db->mii.force_media  = 0;  
  236.     db->mii.full_duplex  = 0;  
  237.     db->mii.dev       = ndev;  
  238.     db->mii.mdio_read    = dm9000_phy_read;  
  239.     db->mii.mdio_write   = dm9000_phy_write;  
  240.   
  241.     mac_src = "eeprom";  
  242.   
  243.     /* try reading the node address from the attached EEPROM */  
  244.     for (i = 0; i < 6; i += 2)  
  245.         dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);  
  246.   
  247.     if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {  
  248.         mac_src = "platform data";  
  249.         memcpy(ndev->dev_addr, pdata->dev_addr, 6);  
  250.     }  
  251.   
  252.     if (!is_valid_ether_addr(ndev->dev_addr)) {  
  253.         /* try reading from mac */  
  254.           
  255.         mac_src = "chip";  
  256.         for (i = 0; i < 6; i++)  
  257.             ndev->dev_addr[i] = ne_def_eth_mac_addr[i];  
  258.     }  
  259.   
  260.     if (!is_valid_ether_addr(ndev->dev_addr))  
  261.         dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "  
  262.              "set using ifconfig\n", ndev->name);  
  263.   
  264.     /* 设置pdev->dev->driver_data为ndev,保存成平台设备总线上的数据,以后使用只需platform_get_drvdata()即可*/  
  265.     platform_set_drvdata(pdev, ndev);  
  266.       
  267.     /* 注册该网络设备 */  
  268.     ret = register_netdev(ndev);  
  269.   
  270.     if (ret == 0)  
  271.         printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",  
  272.                ndev->name, dm9000_type_to_char(db->type),  
  273.                db->io_addr, db->io_data, ndev->irq,  
  274.                ndev->dev_addr, mac_src);  
  275.     return 0;  
  276.       
  277. /* 异常处理 */  
  278. out:  
  279.     dev_err(db->dev, "not found (%d).\n", ret);  
  280.   
  281.     dm9000_release_board(pdev, db);  
  282.     free_netdev(ndev);  
  283.   
  284.     return ret;  
  285. }  
这样,最后完成了网络设备的数据保存到总线上,将网络设备注册到内核。

4、设备的移除函数

[cpp] view plaincopy
  1. /* 该函数是将设备从内核中移除,释放资源,在移除设备驱动时执行 */  
  2. static int __devexit  
  3. dm9000_drv_remove(struct platform_device *pdev)  
  4. {  
  5.     struct net_device *ndev = platform_get_drvdata(pdev);/* 从总线获取probe函数保存到总线的设备信息 */  
  6.   
  7.     platform_set_drvdata(pdev, NULL);/* 释放pdev资源 */  
  8.   
  9.     unregister_netdev(ndev);/* 解除网络设备 */  
  10.     dm9000_release_board(pdev, (board_info_t *) netdev_priv(ndev));/* 释放该设备申请的IO资源 */  
  11.     free_netdev(ndev);      /* free device structure */  
  12.   
  13.     dev_dbg(&pdev->dev, "released and freed device\n");  
  14.     return 0;  
  15. }  
0 0