注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

TCP/IP驱动二设备注册的初步流程  

2013-04-18 20:39:25|  分类: TCPIP驱动 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

TCP/IP驱动二设备注册的初步流程

 

公司的活儿挺多,瞎忙,这一耽误就是一周,到现在又过去两周多了,今天有时间赶紧接着写.前文说过了基本的数据结构,这个东西是网络驱动的一些基础,TCPIP做为一种平台驱动,其主要的东西大家一看就会有非常熟悉的感觉.

原来在介绍MTD子系统时,曾经提过整个系统的流程,但说明还是按照内核的主线走的,这次咱们改一下,按照驱动的加载流程来看.

首先当然是设备和驱动的挂载,mach-mini2440.c中的设备数组中增加网络设备:

static struct platform_device *mini2440_devices[] __initdata = {

         &s3c_device_usb,

         &s3c_device_rtc,

         &s3c_device_lcd,

         &s3c_device_wdt,

         &s3c_device_i2c0,

         &s3c_device_iis,

         &mini2440_device_eth,

         &s3c24xx_uda134x,

         &s3c_device_nand,

         &s3c_device_sdi,

         &s3c_device_usbgadget,

};

然后通过:

MACHINE_START(MINI2440, "FriendlyARM Mini2440 development board")

         .phys_io   = S3C2410_PA_UART,

         .io_pg_offst     = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

         .boot_params = S3C2410_SDRAM_PA + 0x100,

 

         .init_irq    = s3c24xx_init_irq,

         .map_io             = mini2440_map_io,

         .init_machine  = mini2440_machine_init,

         .timer                = &s3c24xx_timer,

MACHINE_END

调用:

static void __init mini2440_machine_init(void)

{

#if defined (LCD_WIDTH)

         s3c24xx_fb_set_platdata(&mini2440_fb_info);

#endif

         s3c_i2c0_set_platdata(NULL);

 

         s3c2410_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND);

 

         s3c_device_nand.dev.platform_data = &friendly_arm_nand_info;

         s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg;

         platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));

         s3c_pm_init();

}

再调用:

platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));

注册当前网络设备.再深入的,大家可以看本博客中“ARM开发”系列中的相关知识以及MTD子系统中相关的设备注册过程。这样设备基本就在内核被启动时挂载到了内核中的相关的链表之中。

回过头来,再看驱动,毕竟主要是以驱动为主。

平台驱动中仍然是老套路,先初始化:

1. 注册平台驱动。

    主要完成的任务是:将驱动添加到总线上,完成驱动和设备的match,并执行驱动的probe函数。代码清单如下:

DM9000.C中最后:

module_init(dm9000_init);

 向上展开:

static struct platform_driver dm9000_driver = {   

    .driver = {   

        .name    = "dm9000", /*用这个名字完成驱动和设备的match*/  

        .owner   = THIS_MODULE,   

    },   

    .probe   = dm9000_probe,   

    .remove  = __devexit_p(dm9000_drv_remove),   

    .suspend = dm9000_drv_suspend,   

    .resume  = dm9000_drv_resume,   

};   

  

static int __init   

dm9000_init(void)   

{   

    printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);   

  

    return platform_driver_register(&dm9000_driver);   

    2. probe函数。

平台驱动就不得不谈下这个重要步骤。

主要是探测设备获得并保存资源信息,根据这些信息申请内存和中断,最后调用register_netdev注册这个网络设备。

   1) 首先定义了几个局部变量:

         struct dm9000_plat_data *pdata = pdev->dev.platform_data;

         struct board_info *db; /* Point a board information structure */

         struct net_device *ndev;

   2) 初始化一个网络设备。关键系统函数:alloc_etherdev()

   3) 获得资源信息并将其保存在board_info变量db中。关键系统函数:netdev_priv(),  platform_get_resource()

   4) 根据资源信息分配内存,申请中断等等, 并将申请后的资源信息也保存到db中,并且填充ndev中的参数。 关键系统函数:request_mem_region(),  ioremap() 自定义函数:dm9000_set_io()

   5) 完成了第4步以后,回顾一下dbndev中都有了什么:

       struct board_info *db:

                 addr_res -- 地址资源

                 data_res -- 数据资源

                 irq_res    -- 中断资源

                 addr_req -- 分配的地址内存资源

                 io_addr   -- 寄存器I/O基地址

                 data_req -- 分配的数据内存资源

                 io_data   -- 数据I/O基地址

                 dumpblk  -- IO模式

                 outblk     -- IO模式

                 inblk        -- IO模式

                 lock         -- 自旋锁(已经被初始化)

                 addr_lock -- 互斥锁(已经被初始化)

        struct net_device *ndev

                 base_addr  -- 设备IO地址

                 irq              -- 设备IRQ

     6) 设备复位。硬件操作函数dm9000_reset()

     7) 读一下生产商和制造商的ID,应该是0x9000 0A46 关键函数:ior()

     8) 读一下芯片类型。

     9) 借助ether_setup()函数来部分初始化ndev。因为对以太网设备来讲,很多操作与属性是固定的,内核可以帮助完成。

    10) 手动初始化ndevopsdbmii部分。

    11) (如果有的话)从EEPROM中读取节点地址。这里可以看到mini2440这个板子上没有为DM9000外挂EEPROM,所以读取出来的全部是0xff。见函数dm9000_read_eeprom 关于外挂EEPROM,可以参考datasheet上的7.EEPROM Format一节。

    12)  很显然ndev是我们在probe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢? 这就需要把它保存起来。内核提供了这个方法,使用函数platform_set_drvdata()可以将ndev保存成平台总线设备的私有数据。以后再要使用它时只需调用platform_get_drvdata()就可以了。

13) 使用register_netdev()注册ndev

static int __devinit   

dm9000_probe(struct platform_device *pdev)   

{   

    struct dm9000_plat_data *pdata = pdev->dev.platform_data;   

    struct board_info *db;  /* Point a board information structure */  

    struct net_device *ndev;   

    const unsigned char *mac_src;   

    int ret = 0;   

    int iosize;   

    int i;   

    u32 id_val;   

  

    /* Init network device */  

        /*使用alloc_etherdev()来生成一个net_device结构体,并对其公有成员赋值*/  

    ndev = alloc_etherdev(sizeof(struct board_info));   

    if (!ndev) {   

        dev_err(&pdev->dev, "could not allocate device.\n");   

        return -ENOMEM;   

    }   

  

    SET_NETDEV_DEV(ndev, &pdev->dev);   

  

    dev_dbg(&pdev->dev, "dm9000_probe()\n");   

  

    /* setup board info structure */  

    db = netdev_priv(ndev);   

    memset(db, 0, sizeof(*db));   

  

    db->dev = &pdev->dev;   

    db->ndev = ndev;   

  

    spin_lock_init(&db->lock);   

    mutex_init(&db->addr_lock);   

  

    INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);   

  

    db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);   

    db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);   

    db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);   

  

    if (db->addr_res == NULL || db->data_res == NULL ||   

        db->irq_res == NULL) {   

        dev_err(db->dev, "insufficient resources\n");   

        ret = -ENOENT;   

        goto out;   

    }   

  

    iosize = res_size(db->addr_res);   

    db->addr_req = request_mem_region(db->addr_res->start, iosize,   

                      pdev->name);   

  

    if (db->addr_req == NULL) {   

        dev_err(db->dev, "cannot claim address reg area\n");   

        ret = -EIO;   

        goto out;   

    }   

  

    db->io_addr = ioremap(db->addr_res->start, iosize);   

  

    if (db->io_addr == NULL) {   

        dev_err(db->dev, "failed to ioremap address reg\n");   

        ret = -EINVAL;   

        goto out;   

    }   

  

    iosize = res_size(db->data_res);   

    db->data_req = request_mem_region(db->data_res->start, iosize,   

                      pdev->name);   

  

    if (db->data_req == NULL) {   

        dev_err(db->dev, "cannot claim data reg area\n");   

        ret = -EIO;   

        goto out;   

    }   

  

    db->io_data = ioremap(db->data_res->start, iosize);   

  

    if (db->io_data == NULL) {   

        dev_err(db->dev, "failed to ioremap data reg\n");   

        ret = -EINVAL;   

        goto out;   

    }   

  

    /* fill in parameters for net-dev structure */  

    ndev->base_addr = (unsigned long)db->io_addr;   

    ndev->irq    = db->irq_res->start;   

  

    /* ensure at least we have a default set of IO routines */  

    dm9000_set_io(db, iosize);   

  

    /* check to see if anything is being over-ridden */  

    if (pdata != NULL) {   

        /* check to see if the driver wants to over-ride the  

         * default IO width */  

  

        if (pdata->flags & DM9000_PLATF_8BITONLY)   

            dm9000_set_io(db, 1);   

  

        if (pdata->flags & DM9000_PLATF_16BITONLY)   

            dm9000_set_io(db, 2);   

  

        if (pdata->flags & DM9000_PLATF_32BITONLY)   

            dm9000_set_io(db, 4);   

  

        /* check to see if there are any IO routine  

         * over-rides */  

  

        if (pdata->inblk != NULL)   

            db->inblk = pdata->inblk;   

  

        if (pdata->outblk != NULL)   

            db->outblk = pdata->outblk;   

  

        if (pdata->dumpblk != NULL)   

            db->dumpblk = pdata->dumpblk;   

  

        db->flags = pdata->flags;   

    }  

 

#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL   

    db->flags |= DM9000_PLATF_SIMPLE_PHY;  

#endif   

  

    dm9000_reset(db);   

  

    /* try multiple times, DM9000 sometimes gets the read wrong */  

    for (i = 0; i < 8; i++) {   

        id_val  = ior(db, DM9000_VIDL);   

        id_val |= (u32)ior(db, DM9000_VIDH) << 8;   

        id_val |= (u32)ior(db, DM9000_PIDL) << 16;   

        id_val |= (u32)ior(db, DM9000_PIDH) << 24;   

  

        if (id_val == DM9000_ID)   

            break;   

        dev_err(db->dev, "read wrong id 0x%08x\n", id_val);   

    }   

  

    if (id_val != DM9000_ID) {   

        dev_err(db->dev, "wrong id: 0x%08x\n", id_val);   

        ret = -ENODEV;   

        goto out;   

    }   

  

    /* Identify what type of DM9000 we are working on */  

  

    id_val = ior(db, DM9000_CHIPR);   

    dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);   

  

    switch (id_val) {   

    case CHIPR_DM9000A:   

        db->type = TYPE_DM9000A;   

        break;   

    case CHIPR_DM9000B:    

        db->type = TYPE_DM9000B;   

        break;   

    default:   

        dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);   

        db->type = TYPE_DM9000E;   

    }   

  

    /* from this point we assume that we have found a DM9000 */  

  

    /* driver system function */  

    ether_setup(ndev);   

  

    ndev->netdev_ops = &dm9000_netdev_ops;   

    ndev->watchdog_timeo = msecs_to_jiffies(watchdog);   

    ndev->ethtool_ops    = &dm9000_ethtool_ops;   

  

    db->msg_enable       = NETIF_MSG_LINK;   

    db->mii.phy_id_mask  = 0x1f;   

    db->mii.reg_num_mask = 0x1f;   

    db->mii.force_media  = 0;   

    db->mii.full_duplex  = 0;   

    db->mii.dev       = ndev;   

    db->mii.mdio_read    = dm9000_phy_read;   

    db->mii.mdio_write   = dm9000_phy_write;   

  

    mac_src = "eeprom";   

  

    /* try reading the node address from the attached EEPROM */  

    for (i = 0; i < 6; i += 2)   

        dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);   

   

    if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {   

        mac_src = "platform data";   

        memcpy(ndev->dev_addr, pdata->dev_addr, 6);   

    }   

  

    if (!is_valid_ether_addr(ndev->dev_addr)) {   

        /* try reading from mac */  

        mac_src = "chip";   

        for (i = 0; i < 6; i++)   

            ndev->dev_addr[i] = ior(db, i+DM9000_PAR);   

    }   

  

    if (!is_valid_ether_addr(ndev->dev_addr))   

        dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "  

             "set using ifconfig\n", ndev->name);   

  

    platform_set_drvdata(pdev, ndev);   

    ret = register_netdev(ndev);   

  

    if (ret == 0)   

        printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",   

               ndev->name, dm9000_type_to_char(db->type),   

               db->io_addr, db->io_data, ndev->irq,   

               ndev->dev_addr, mac_src);   

    return 0;   

out:   

    dev_err(db->dev, "not found (%d).\n", ret);   

 

    dm9000_release_board(pdev, db);   

    free_netdev(ndev);   

    return ret;   

如果这样一个流程能够正确的走下来,那么就可以认为驱动是正常的挂载成功了,下来就可以进行各种各样的操作和控制了。

具体的操作下次再说。

.

  评论这张
 
阅读(915)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017