Linux内核中DM8168的网口驱动移植
来源:互联网 发布:淘宝模特拍照视频 编辑:程序博客网 时间:2024/05/21 16:23
背景
在DM8168的EVM板中,两个EMAC模块分别连接到两个PHY上,用同一个MDIO模块管理,两个PHY配置为不同地址。内核启动后可以注册为两个eth设备。
然而在新研FXX板中,DM8168改变了网口连接方式,其EMAC0未引连接PHY,而是直接用GMII接口连接到对端某芯片上。因此需要修改内核驱动代码来完成适配。
分析
分析内核与网口有关的驱动可知,EMAC和MDIO都作为内核platform设备由platform虚拟总线来管理。因此对驱动的分析分为两部分,一是一是device部分,一是device_driver部分。在内核启动过程中,两者先后注册到总线上,然后适配在一起共同工作。
网口设备
两个网口和一个MDIO都是platform_device的设备。设备注册和初始化的调用关系为: omap2_init_devices()
–>ti81xx_ethernet_init()
–>ti816x_ethernet_init()
。
先看omap2_init_devices()
,它被声明为:
======== arch/arm/mach-omap2/devices.c 2208 2208 ========arch_initcall(omap2_init_devices);
因此是在系统初始化的某一个阶段调用的。
再看ti816x_ethernet_init()
函数:
- 把MAC地址等存入ti816x_emac1_pdata私有数据变量中
- 把ti816x_emac1_device设备的私有信息指针指向ti816x_emac1_pdata
- 调用
platform_device_register()
注册ti816x_emac1_device设备 - 调用
platform_device_register()
注册ti816x_mdio_device设备 - 与1~3步同样的方式注册ti816x_emac2_device设备
- 调用
ti816x_emac_mux()
把EMAC1的引脚配置为EMAC功能。
我们来看一看静态的网口私有变量结构体:
======== arch/arm/mach-omap2/devices.c 1125 1133 ========static struct emac_platform_data ti816x_emac1_pdata = { .rmii_en = 0, .phy_id = "0:01",};static struct emac_platform_data ti816x_emac2_pdata = { .rmii_en = 0, .phy_id = "0:02",};
结构体中这两个字段尤其是phy_id字段特别有用,下面会分析到。
网口驱动
驱动分为MDIO驱动和EMAC驱动两部分。
MDIO驱动
============ drivers/net/davince_md.c 460 464 ========static int __init davinci_mdio_init(void){ return platform_driver_register(&davinci_mdio_driver);}device_initcall(davinci_mdio_init);
这个函数也是在设备初始化的某一个阶段调用的。这里将davinci_mdio_driver这个驱动注册到了platform总线中。在注册过程中,会调用其probe方法davinci_mdio_probe()
,它申请一条MDIO总线,然后调用mdiobus_register()
注册之,这条总线上将来会挂接PHY设备。
mdiobus_register()
–>davinci_mdio_reset()
&mdiobus_scan()
–>get_phy_device()
–>get_phy_id()
mdiobus_register()
主要流程是:
- 调用
davinci_mdio_reset()
使能MDIO模块,再等待一段足够长的时间让总线遍历所有PHY,然后把活动的PHY地址记录入phy_mask中。 - 按照活动PHY地址,依次调用
mdiobus_scan()
。
mdiobus_scan()
–>get_phy_device()
–>get_phy_id()
:
调用davinci_mdio_read()
读取某个PHY的ID,读取到正确值后就调用phy_device_create()
创建这样一个设备。然后把设备注册到MDIO总线上,注册时按照0:01这样的规则为设备命名,其中0代表总线ID,01代表PHY的地址。
这样MDIO总线和总线上的设备就依次注册完成。再打印出这些设备后davinci_mdio_probe()
结束。
EMAC驱动
============ drivers/net/davince_emac.c 2042 2046 ========static int __init davinci_emac_init(void){ return platform_driver_register(&davinci_emac_driver);}late_initcall(davinci_emac_init);
这个函数也是在设备初始化的某一个阶段调用的。
这里将davinci_emac_driver这个驱动注册到了platform总线中。在注册过程中,会调用其probe方法davinci_emac_probe()
。主要流程是:
- 申请一个net_device结构体和私有数据结构体
申请结构体调用alloc_etherdev()
实现,它是alloc_netdev_mq()
的一个包裹函数,会调用kzalloc()
申请一个net_device结构体加上私有数据再加对齐数据的大小,然后调用ether_setup()
初始化net_device中的一些函数指针和一些变量字段。这个过程中会把设备名称初始化为ethx这样的结构,其中x由注册顺序决定。对于以太网设备申请的net_device和私有数据是在内存中连续的。 - 对两个结构体的各字段进行初始化
然后probe会初始化私有数据结构体中的各字段,以及net_device结构体中其余一些函数指针和变量字段。 - 调用register_netdev()注册。
最后register_netdev()函数是register_netdevice()的包裹函数,这个函数在《Understanding Linux Network Intervals》中有详细描述,这里略过。
设备私有数据结构中有一个phy_id指针字段,它被指向EMAC设备私有数据中的phy_id。这个字段决定了对外的物理连接。
在emac_dev_open()
函数中,有这样一段对phy的配置:
=========== drivers/net/davince_emac.c 1593 1631 ============== priv->phydev = NULL; /* use the first phy on the bus if pdata did not give us a phy id */ if (!priv->phy_id) { struct device *phy; phy = bus_find_device(&mdio_bus_type, NULL, NULL, match_first_device); if (phy) priv->phy_id = dev_name(phy); } if (priv->phy_id && *priv->phy_id) { priv->phydev = phy_connect(ndev, priv->phy_id, &emac_adjust_link, 0, PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phydev)) { dev_err(emac_dev, "could not connect to phy %s\n", priv->phy_id); priv->phydev = NULL; return PTR_ERR(priv->phydev); } priv->link = 0; priv->speed = 0; priv->duplex = ~0; dev_info(emac_dev, "attached PHY driver [%s] " "(mii_bus:phy_addr=%s, id=%x)\n", priv->phydev->drv->name, dev_name(&priv->phydev->dev), priv->phydev->phy_id); } else { /* No PHY , fix the link, speed and duplex settings */ dev_notice(emac_dev, "no phy, defaulting to 100/full\n"); priv->link = 1; priv->speed = SPEED_100; priv->duplex = DUPLEX_FULL; emac_update_phystatus(priv); }
当phy_id这个指针为空时,则从MDIO总线上找到第一个设备成为phy_id。
如果phy_id指针不为空,且指向内容不为空,则调用phy_connect()
来连接指定PHY,将连接状态置为断开。
否则表示没有指定默认PHY且没有找到可用PHY,将连接配置为100M全双工。
这样私有数据的phydev字段为空,因此之后所有对PHY的操作都不真正实现。
实际上在phy_id的定义里,有这样一段注释:
============== include/linux/davince_emac.h 29 35 ================= /* * phy_id can be one of the following: * - NULL : use the first phy on the bus, * - "" : force to 100/full, no mdio control * - "<bus>:<addr>" : use the specified bus and phy */ const char *phy_id;
因此对于FXX板来说,要配置为千兆,则需要将设备结构体的该字段设置为”“,然后在上述emac_dev_open()
中的else分支中将priv->speed赋值为SPEED_1000即可。
总结
虽然最终代码上的修改可能只有两三行,但是背后可以挖掘的内容还有很多,例如是设备先注册还是驱动先注册,协议栈何时启动等等,以后慢慢分析。
- Linux内核中DM8168的网口驱动移植
- linux-2.6.35内核移植—网卡驱动的移植
- Linux 2.6 内核驱动移植
- Linux 2.6 内核驱动移植
- S3C2410 LCD驱动的移植(基于linux-2.6.15.4内核)
- Linux内核驱动在Tx2440上的移植详解
- linux-2.6.35内核移植—网卡驱动的添加
- linux-2.6.35内核移植—网卡驱动的添加
- linux-2.6.35内核移植—LCD驱动的添加
- linux-2.6.35内核移植—USB驱动的添加
- 基于Linux-2.6.35内核的wifi驱动移植
- linux-2.6.26.5内核的 lcd驱动移植…
- linux-2.6.26.5内核的 lcd驱动移植…
- Linux内核驱动在Tx2440上的移植详解(七、LCD背光驱动移植)
- linux-2.6.26.5内核的 lcd驱动移植 lcd 触摸屏移植 tslib-1.4移植
- linux-2.6.26.5内核的 lcd驱动移植 lcd 触摸屏移植 tslib-1.4移植 (转)
- linux-2.6.26.5内核的 lcd驱动移植 lcd 触摸屏移植 tslib-1.4移植
- 【Linux驱动】Linux-2.6.20.4内核移植
- Discuz如何删除内部插件
- Android屏幕适配
- BZOJ3218 UOJ#77 A+B Problem(最小割+主席树)
- RSA加密工具类
- Epoll模型详解
- Linux内核中DM8168的网口驱动移植
- POJ--3750 小孩报数问题
- git最新版本安装
- hadoop学习之路(二)hadoop基本概念原理以及单词统计任务源码分析
- Android 立方体翻转效果
- POJ 3273 Monthly Expense 二分查找的应用
- mycat 性能采集工具
- OpenGL ES 学习教程(十一) Skin Mesh (骨骼动画)
- 快速排序QuickSort