歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> PHY芯片 AR8033驅動代碼分析

PHY芯片 AR8033驅動代碼分析

日期:2017/3/3 12:50:41   编辑:Linux技術
【思路描述】
想要在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 結構體,初始化代碼如下:
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()函數。其代碼細節如下:
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()函數。代碼如下:
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) 將驅動添加到總線中。至此,驅動注冊流程結束。代碼如下:
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)對該設備總線進行注冊。代碼如下:
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 設備。代碼如下:
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 設備進行注冊。至此,設備注冊流程結束。代碼如下:
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;
}
Copyright © Linux教程網 All Rights Reserved