• 【linux驱动分析】之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现


    一、dm9000_init
    打印出驱动的版本,注冊dm9000_driver驱动,将驱动加入到总线上。运行match,假设匹配,将会运行probe函数。

    1 static int __init
    2 dm9000_init(void)
    3 {
    4     printk(KERN_INFO "%s Ethernet Driver, V%s
    ", CARDNAME, DRV_VERSION);
    5 
    6     return platform_driver_register(&dm9000_driver);
    7 }

    二、dm9000_probe函数
      1 /*
      2  * Search DM9000 board, allocate space and register it
      3  */
      4 static int __devinit
      5 dm9000_probe(struct platform_device *pdev)
      6 {
      7     /* 把mach-mini6410.c中定义的dm9000_plat_data传递过来 */
      8     struct dm9000_plat_data *pdata = pdev->dev.platform_data;
      9     struct board_info *db;    /* Point a board information structure */
     10     struct net_device *ndev;
     11     const unsigned char *mac_src;
     12     int ret = 0;
     13     int iosize;
     14     int i;
     15     u32 id_val;
     16 
     17     /* Init network device */
     18     /* 分配一个名为eth%d的网络设备。同一时候分配一个私有数据区,数据区是32字节对齐 */
     19     ndev = alloc_etherdev(sizeof(struct board_info));
     20     if (!ndev) {
     21         dev_err(&pdev->dev, "could not allocate device.
    ");
     22         return -ENOMEM;
     23     }
     24     /* 把网络设备的基类dev的父指针设为平台设备的基类dev */
     25     SET_NETDEV_DEV(ndev, &pdev->dev);
     26 
     27     dev_dbg(&pdev->dev, "dm9000_probe()
    ");
     28 
     29     /* setup board info structure */
     30     /* 设置私有数据,以下会详细分析这个函数 */
     31     db = netdev_priv(ndev);
     32     /* 给私有数据赋值 */
     33     db->dev = &pdev->dev;
     34     db->ndev = ndev;
     35     
     36     /* 初始化一个自旋锁和一个相互排斥体 */
     37     spin_lock_init(&db->lock);
     38     mutex_init(&db->addr_lock);
     39     
     40     /* 往工作队列插入一个工作,随后我们调用schedule_delayed_work就会运行传递的函数
     41      * 关于工作队列会有专门一篇文章来学习总结
     42      */
     43     INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);
     44 
     45     /* 获得资源。这个函数会在以下解说 */
     46     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
     47     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
     48     db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
     49 
     50     if (db->addr_res == NULL || db->data_res == NULL ||
     51         db->irq_res == NULL) {
     52         dev_err(db->dev, "insufficient resources
    ");
     53         ret = -ENOENT;
     54         goto out;
     55     }
     56     
     57     /* 获取中断号,这个中断号是不存在的,因为resource里仅仅有一个中断号 */
     58     db->irq_wake = platform_get_irq(pdev, 1);
     59     if (db->irq_wake >= 0) {
     60         dev_dbg(db->dev, "wakeup irq %d
    ", db->irq_wake);
     61         
     62         /* 为ndev申请中断。中断服务程序为dm9000_wol_interrupt,关于中断也会有一篇文章来学习总结 */
     63         ret = request_irq(db->irq_wake, dm9000_wol_interrupt,
     64                   IRQF_SHARED, dev_name(db->dev), ndev);
     65         if (ret) {
     66             dev_err(db->dev, "cannot get wakeup irq (%d)
    ", ret);
     67         } else {
     68 
     69             /* test to see if irq is really wakeup capable */
     70             ret = set_irq_wake(db->irq_wake, 1);
     71             if (ret) {
     72                 dev_err(db->dev, "irq %d cannot set wakeup (%d)
    ",
     73                     db->irq_wake, ret);
     74                 ret = 0;
     75             } else {
     76                 set_irq_wake(db->irq_wake, 0);
     77                 db->wake_supported = 1;
     78             }
     79         }
     80     }
     81     /* 返回dm9000内存资源的大小,以下一句是申请内存,关于内存的申请和分配也会有一篇文章 */
     82     iosize = resource_size(db->addr_res);
     83     db->addr_req = request_mem_region(db->addr_res->start, iosize,
     84                       pdev->name);
     85 
     86     if (db->addr_req == NULL) {
     87         dev_err(db->dev, "cannot claim address reg area
    ");
     88         ret = -EIO;
     89         goto out;
     90     }
     91     /* 存放地址的内存空间開始地址,地址寄存器,一共占3个地址,
     92      * 各自是0x18000000,0x18000001,0x18000002,0x18000003,
     93      * 这也是一个巧妙之处。dm9000芯片的cmd引脚接的是arm11的addr2。
     94      * 所以写“0地址”代表送地址。读写4地址表示读写数据
     95      * */
     96     db->io_addr = ioremap(db->addr_res->start, iosize);
     97 
     98     if (db->io_addr == NULL) {
     99         dev_err(db->dev, "failed to ioremap address reg
    ");
    100         ret = -EINVAL;
    101         goto out;
    102     }
    103 
    104     iosize = resource_size(db->data_res);
    105     db->data_req = request_mem_region(db->data_res->start, iosize,
    106                       pdev->name);
    107 
    108     if (db->data_req == NULL) {
    109         dev_err(db->dev, "cannot claim data reg area
    ");
    110         ret = -EIO;
    111         goto out;
    112     }
    113     /* 数据寄存器的地址,1MB的空间 */
    114     db->io_data = ioremap(db->data_res->start, iosize);
    115 
    116     if (db->io_data == NULL) {
    117         dev_err(db->dev, "failed to ioremap data reg
    ");
    118         ret = -EINVAL;
    119         goto out;
    120     }
    121 
    122     /* fill in parameters for net-dev structure */
    123     ndev->base_addr = (unsigned long)db->io_addr;
    124     ndev->irq    = db->irq_res->start;
    125 
    126     /* ensure at least we have a default set of IO routines */
    127     /* iosize是一个非常大的值,这里是先保证有个默认值位宽,32位 */
    128     dm9000_set_io(db, iosize);
    129 
    130     /* check to see if anything is being over-ridden */
    131     if (pdata != NULL) {
    132         /* check to see if the driver wants to over-ride the
    133          * default IO width */
    134 
    135         if (pdata->flags & DM9000_PLATF_8BITONLY)
    136             dm9000_set_io(db, 1);
    137         /*
    138          * 我们这里设置的是16位。他会做以下几件事:
    139          * db->dumpblk = dm9000_dumpblk_16bit;
    140          * db->outblk  = dm9000_outblk_16bit;
    141          * db->inblk   = dm9000_inblk_16bit;
    142          */
    143         if (pdata->flags & DM9000_PLATF_16BITONLY)
    144             dm9000_set_io(db, 2);
    145 
    146         if (pdata->flags & DM9000_PLATF_32BITONLY)
    147             dm9000_set_io(db, 4);
    148 
    149         /* check to see if there are any IO routine
    150          * over-rides */
    151 
    152         if (pdata->inblk != NULL)
    153             db->inblk = pdata->inblk;
    154 
    155         if (pdata->outblk != NULL)
    156             db->outblk = pdata->outblk;
    157 
    158         if (pdata->dumpblk != NULL)
    159             db->dumpblk = pdata->dumpblk;
    160 
    161         db->flags = pdata->flags;
    162     }
    163 
    164 #ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL
    165     db->flags |= DM9000_PLATF_SIMPLE_PHY;
    166 #endif
    167     /*
    168      * dm9000_reset函数是运行以下两句话,中间有延时,这里省略了
    169      * writeb(DM9000_NCR, db->io_addr); //先写NCR寄存器的地址到“0地址”(写到0地址就代表写地址)
    170      * writeb(NCR_RST, db->io_data);    //再给“4地址”写NCR_RST(0x01)。即NCR = 1;
    171      * 读写“4地址”就相当于发送数据,cmd引脚连的是addr2,dm9000地址线数据线复用
    172      * */
    173     dm9000_reset(db);
    174 
    175     /* try multiple times, DM9000 sometimes gets the read wrong */
    176     /* 以下所做的是读出dm9000的供应商ID和产品ID */
    177     for (i = 0; i < 8; i++) {
    178         /* ior是从reg读出数据。类型是u8。它的原理与上面分析reset函数的原理是一样的 */
    179         id_val  = ior(db, DM9000_VIDL);
    180         id_val |= (u32)ior(db, DM9000_VIDH) << 8;
    181         id_val |= (u32)ior(db, DM9000_PIDL) << 16;
    182         id_val |= (u32)ior(db, DM9000_PIDH) << 24;
    183 
    184         if (id_val == DM9000_ID)
    185             break;
    186         dev_err(db->dev, "read wrong id 0x%08x
    ", id_val);
    187     }
    188 
    189     if (id_val != DM9000_ID) {
    190         dev_err(db->dev, "wrong id: 0x%08x
    ", id_val);
    191         ret = -ENODEV;
    192         goto out;
    193     }
    194 
    195     /* Identify what type of DM9000 we are working on */
    196     /* 读出芯片版本号寄存器。推断dm9000的型号,默认是dm9000E */
    197     id_val = ior(db, DM9000_CHIPR);
    198     dev_dbg(db->dev, "dm9000 revision 0x%02x
    ", id_val);
    199 
    200     switch (id_val) {
    201     case CHIPR_DM9000A:
    202         db->type = TYPE_DM9000A;
    203         break;
    204     case CHIPR_DM9000B:
    205         db->type = TYPE_DM9000B;
    206         break;
    207     default:
    208         dev_dbg(db->dev, "ID %02x => defaulting to DM9000E
    ", id_val);
    209         db->type = TYPE_DM9000E;
    210     }
    211 
    212     /* dm9000a/b are capable of hardware checksum offload */
    213     if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {
    214         db->can_csum = 1;
    215         db->rx_csum = 1;
    216         ndev->features |= NETIF_F_IP_CSUM;
    217     }
    218 
    219     /* from this point we assume that we have found a DM9000 */
    220 
    221     /* driver system function */
    222     /* 这个函数是初始化ndev的一些成员 */
    223     ether_setup(ndev);
    224 
    225     /* 以下也是初始化ndev的一些成员 */
    226     ndev->netdev_ops    = &dm9000_netdev_ops;
    227     ndev->watchdog_timeo    = msecs_to_jiffies(watchdog);
    228     ndev->ethtool_ops    = &dm9000_ethtool_ops;
    229 
    230     db->msg_enable       = NETIF_MSG_LINK;
    231     db->mii.phy_id_mask  = 0x1f;
    232     db->mii.reg_num_mask = 0x1f;
    233     db->mii.force_media  = 0;
    234     db->mii.full_duplex  = 0;
    235     db->mii.dev         = ndev;
    236     db->mii.mdio_read    = dm9000_phy_read;
    237     db->mii.mdio_write   = dm9000_phy_write;
    238 
    239     mac_src = "eeprom";
    240     /* node address是在网络中的一个电脑或终端的号码或名字。
    241      * 这里从eeprom读取,因为我们没有,所以它读回来的是6个FF
    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     /* try MAC address passed by kernel command line */
    248     /* 这个函数是友善之臂加入的,它在mach-mini6410里加入了这样一句话__setup("ethmac=", dm9000_set_mac);
    249      * 内核启动时,遇到"ethmac="回去运行dm9000_set_mac函数,所以就实现了mac从内核传递过来
    250      * 这也是一个非常巧妙的设计,须要写一篇文章学习总结一下
    251      * */
    252     if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
    253         mac_src = "param data";
    254         memcpy(ndev->dev_addr, pdata->param_addr, 6);
    255     }
    256     /* 以下是读取mac的几种方法,当前这一种是从dm9000的Physical Address Register读取 */
    257     if (!is_valid_ether_addr(ndev->dev_addr)) {
    258         /* try reading from mac */
    259         mac_src = "chip";
    260         for (i = 0; i < 6; i++)
    261             ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
    262     }
    263     /* 从pdata里的dev_addr读取 */
    264     if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
    265         mac_src = "platform data";
    266         memcpy(ndev->dev_addr, pdata->dev_addr, 6);
    267     }
    268     /* 没有读到有效的mac地址。提示用ifconfig命令设置 */
    269     if (!is_valid_ether_addr(ndev->dev_addr))
    270         dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "
    271              "set using ifconfig
    ", ndev->name);
    272 
    273     /* 
    274      * 这里因为ndev是我们定义的一个局部变量。所以要ndev传递给平台设备pdev
    275      * 即pdev->dev->p->driver_data = ndev;
    276      * 要使用是通过platform_get_drvdata获得
    277      * */
    278     platform_set_drvdata(pdev, ndev);
    279     /* net_device结构体初始化好后,剩余的工作就是把该结构传递给register_netdev函数。
    280      * 当调用register_netdev后就能够用驱动程序操作设备了。所以
    281      * 必须在初始化一切事情后再注冊
    282      * */
    283     ret = register_netdev(ndev);
    284 
    285     if (ret == 0)
    286         printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)
    ",
    287                ndev->name, dm9000_type_to_char(db->type),
    288                db->io_addr, db->io_data, ndev->irq,
    289                ndev->dev_addr, mac_src);
    290     return 0;
    291 
    292 out:
    293     dev_err(db->dev, "not found (%d).
    ", ret);
    294 
    295     dm9000_release_board(pdev, db);
    296     free_netdev(ndev);
    297 
    298     return ret;
    299 }
    300 /*********** probe函数大功告成 *************/
    301 
    

    三、总结probe函数分析是留下的问题
    在上面用红色标记出来了要分析的东西
    1、分析netdev_priv
    在运行 ndev = alloc_etherdev(sizeof(struct board_info));时,先分配了一个net_device结构。又分配了一个board_info结构体。作为ndev的私有数据,然后运行了db = netdev_priv( ndev );来获得私有数据的開始地址。并在以后做初始化。
    在probe函数最后,通过platform_set_drvdata函数把ndev结构传给了平台设备,以供使用,那么我猜想这里把board_info也传过去了,以后也能够用。获得它的地址的方法是通过以下的函数。
    303 /**
    304  *    netdev_priv - access network device private data
    305  *    @dev: network device
    306  *
    307  * Get network device private data
    308  */
    309 static inline void *netdev_priv(const struct net_device *dev)
    310 {
    311     return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN);
    312 }

    2、platform_get_resource函数分析
    46     db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    47     db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
    48     db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    platform_data的关系是这种:platform device->dev->platform_data
    对于dm9000驱动来说是这样实现的:
    8     struct dm9000_plat_data *pdata = pdev->dev.platform_data;
    static struct dm9000_plat_data dm9000_setup = {
    	.flags		= DM9000_PLATF_16BITONLY | DM9000_PLATF_EXT_PHY,
    	.dev_addr       = { 0x08, 0x90, 0x00, 0xa0, 0x90, 0x90 },
    }; 

    3、以后要写文章总结的东西
    (1)、关于工作队列的要总结一下
    43     INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);

    (2)、关于内核中断的原理和操作方法
    63         ret = request_irq(db->irq_wake, dm9000_wol_interrupt,
    64                   IRQF_SHARED, dev_name(db->dev), ndev);

    (3)、关于内核内存分配和操作方法
    83     db->addr_req = request_mem_region(db->addr_res->start, iosize,
    84                       pdev->name);
    (4)、关于__setup的作用
    __setup("ethmac=", dm9000_set_mac);
  • 相关阅读:
    中国软件外包IT公司最新排名
    DJ舞曲、音乐与爱好!
    linux论坛
    IBM P 系列小型机的控件面板功能~!(转用)
    JDBC Driver 驱动 For SQL 2008 Server /2005 /2000
    年报盘点:149家公司筹码大幅集中
    公式指标—精华
    观峰雨个人空间 2010 STOCK ADVICE !
    IntelliJ IDEA提示Cannot resolve symbol
    今天天变的好冷了~
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/5104943.html
Copyright © 2020-2023  润新知