您的位置:首页 > 编程语言

PHY芯片 AR8033驱动代码分析

2017-06-05 11:29 537 查看
 


PHY芯片 AR8033驱动代码分析


网址:http://blog.csdn.net/Qidi_Huang/article/details/51398729

【思路描述】

    想要在Linux上使用AR8033需要做2部分工作,一是编写设备驱动并将设备驱动注册到内核,二是创建设备通信要使用的mdio总线并将设备注册到总线上。关于
AR8033 芯片的介绍可以看我的这篇博文《PHY芯片 AR8033学习笔记》

【代码分析】

a)  驱动注册流程:

    文件mdio_gpio.c 是 mdio_gpio 模块的代码所在。在模块加载函数 mdio_gpio_init() 中通过语句 ret = platform_driver_register(&mdio_gpio_driver) 将mdio 驱动注册为“平台设备驱动”,其中mdio_gpio_driver 是一个 platform_driver 结构体,初始化代码如下:

[cpp] view
plain copy

 print?

static struct platform_driver mdio_gpio_driver= {  

       .probe= mdio_gpio_probe,    // 关联设备的probe函数  

       .remove= __devexit_p(mdio_gpio_remove),    // 关联设备的remove函数  

       .driver            = {  

              .name      = "mdio-gpio",    // 驱动名  

              .owner    = THIS_MODULE,  

       },  

};  

    而在 platform_driver_register() 这个函数中,则进一步将驱动总线类型设定为 platform_bus_type,以及关联驱动的probe()函数、remove()函数和shutdown()函数。其代码细节如下:

[cpp] view
plain copy

 print?

int platform_driver_register(structplatform_driver *drv)  

{  

       drv->driver.bus= &platform_bus_type;  

       if(drv->probe)  

              drv->driver.probe = platform_drv_probe;    // 注意,这是drv->driver.probe  

       if(drv->remove)  

              drv->driver.remove= platform_drv_remove;    // 关联drv->driver.remove  

       if(drv->shutdown)  

              drv->driver.shutdown= platform_drv_shutdown;    // 关联drv->driver.shutdown  

       return driver_register(&drv->driver);  

}  

    可以看到,对于所有驱动而言初始化到这一步时都会指向probe()、remove()、shutdown()这3个函数,这3个函数分别用来返回“平台设备驱动”的probe()、remove()、shutdown()函数。代码如下:

[cpp] view
plain copy

 print?

static int platform_drv_probe(struct device*_dev)  

{  

       struct platform_driver *drv = to_platform_driver(_dev->driver);  

       struct platform_device *dev = to_platform_device(_dev);  

       return drv->probe(dev);  

}  

    而在platform_driver_register()函数结尾的 return 语句中,再调用driver_register(&drv->driver) 进一步对驱动进行注册。通过语句driver_find(drv->name, drv->bus)查找总线上是否已经注册过该驱动,若没有则使用语句bus_add_driver(drv) 将驱动添加到总线中。至此,驱动注册流程结束。代码如下:

[cpp] view
plain copy

 print?

int driver_register(struct device_driver*drv)  

{  

       int ret;  

       struct device_driver *other;  

       BUG_ON(!drv->bus->p);    // 打开调试  

   // 做一些检测  

       if((drv->bus->probe && drv->probe) ||  

           (drv->bus->remove && drv->remove) ||  

           (drv->bus->shutdown && drv->shutdown))  

              printk(KERN_WARNING "Driver '%s' needs updating - please use "  

                     "bus_typemethods\n", drv->name);  

   

       other = driver_find(drv->name, drv->bus);    // 在总线中查找是否已经注册该驱动  

       if(other) {  

              printk(KERN_ERR "Error: Driver '%s' is already registered, "  

                     "aborting...\n",drv->name);  

              return -EBUSY;  

       }  

       ret = bus_add_driver(drv);   // 将驱动添加到总线  

       if(ret)  

              return ret;  

       ret= driver_add_groups(drv, drv->groups);  

       if(ret)  

              bus_remove_driver(drv);  

       return ret;  

}  

 

b)  设备注册流程:

    驱动注册流程的末尾会调用驱动的 probe() 函数,即 mdio_gpio_probe() 函数。在该函数中,通过语句new_bus = mdio_gpio_bus_init(&pdev->dev, pdata,pdev->id) 初始化一个 mdio 总线设备,在使用语句 mdiobus_register(new_bus)对该设备总线进行注册。代码如下:

[cpp] view
plain copy

 print?

static int __devinit mdio_gpio_probe(structplatform_device *pdev)  

{  

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

       struct mii_bus *new_bus;  

       intret;  

       if(!pdata)  

              return-ENODEV;  

       new_bus = mdio_gpio_bus_init(&pdev->dev, pdata,pdev->id);  

       if(!new_bus)  

              return -ENODEV;  

       ret = mdiobus_register(new_bus);  

       if(ret)  

              mdio_gpio_bus_deinit(&pdev->dev);  

       return ret;  

}  

    进入函数 mdiobus_register() 查看代码,内容可以分为2部分。一是对 mdio 总线设备进行真正的注册,二是注册成功后,在总线上根据 phy_mask搜索 PHY 设备。代码如下:

[cpp] view
plain copy

 print?

int mdiobus_register(struct mii_bus *bus)  

{  

       int i, err;  

       if(NULL == bus || NULL == bus->name ||  

                     NULL== bus->read ||  

                     NULL== bus->write)  

              return-EINVAL;  

       BUG_ON(bus->state!= MDIOBUS_ALLOCATED &&  

              bus->state != MDIOBUS_UNREGISTERED);  

       bus->dev.parent= bus->parent;  

       bus->dev.class= &mdio_bus_class;  

       bus->dev.groups= NULL;  

       dev_set_name(&bus->dev,"%s", bus->id);  

       err = device_register(&bus->dev);    // 注册 mdio总线设备  

       if(err) {  

              printk(KERN_ERR "mii_bus %s failed to register\n", bus->id);  

              return -EINVAL;  

       }  

       mutex_init(&bus->mdio_lock);  

       if(bus->reset)  

              bus->reset(bus);  

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

              if((bus->phy_mask & (1 << i)) == 0) {  

                     structphy_device *phydev;  

                     phydev = mdiobus_scan(bus, i);    // 在总线上搜索 phy 设备  

                     if(IS_ERR(phydev)) {  

                            err = PTR_ERR(phydev);  

                            goto error;  

                     }  

              }  

       }  

       bus->state= MDIOBUS_REGISTERED;  

…   // 以下省略  

}  

    进入函数 mdiobus_scan() 查看代码,可以看到在该函数中使用 phydev = get_phy_device(bus, addr) 语句从总线设备上获取到 phy 设备,然后通过语句phy_device_register(phydev) 对phy 设备进行注册。至此,设备注册流程结束。代码如下:

[cpp] view
plain copy

 print?

struct phy_device *mdiobus_scan(structmii_bus *bus, int addr)  

{  

       struct phy_device *phydev;  

       struct mdio_board_entry *be;  

       int err;  

   

       phydev = get_phy_device(bus, addr);    // 从总线设备上获取 phy 设备  

       if(IS_ERR(phydev) || phydev == NULL)  

              returnphydev;  

       mutex_lock(&__mdio_board_lock);  

       list_for_each_entry(be,&__mdio_board_list, list)  

              mdiobus_setup_phydev_from_boardinfo(bus,phydev,  

                                              &be->board_info);  

       mutex_unlock(&__mdio_board_lock);  

       err = phy_device_register(phydev);    // 将 phy 设备注册到内核  

       if(err) {  

              phy_device_free(phydev);  

              return NULL;  

       }  

       return phydev;  

}  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: