PHY Linux 驱动

以太网 MAC(链路层)+PHY(物理层/RTL8201F,88E1111);集成型DM9000,RTL8139CP

由于网络数据传输量较大,不论是分开型还是集成型,通常会在MAC和PHY之间引入DMA,MAC和PHY之所以有分开,是因为MAC属于数字电路部分,而PHY则属于模拟部分,负责将接收到的数据传输给MAC层,MAC层将接收到的数据传输给上层协议,如IP层,IGMP层。PHY层也负责将由MAC层发送过来的数据,转换成差分信号,经过后边的变压器后送入RJ45接头的线缆中。
对应的数据格式为:

 前导符+开始位+目的MAC地址+源MAC地址+类型长度+数据+padding(optional)+32bit CRC

PHY层负责的任务还是比较多的,比如10M/100M速率的选择;全双工还是半双工;载波侦听和冲突检测,这些工作对应到具体芯片就是内部寄存器的一些设置。

PHY功能模块和MCU的链接采用MII/RMII方式链接,这样就可以使用MCU设置相关的寄存器。MII是16根线,RMII将收发数据线各减到两根,这其中涉及串并转换,即将MCU的类的并行总线最终转换成RJ45线上传输的单bit编码。100M是曼彻斯特编码,10M是NRZ编码,这类编码主要考虑负载均衡和纠错。也带来了额外的网络开销。


PHY层的初始化,主要是两个部分,一个是mdio总线的初始化,一个是PHY驱动初始化,对于通常可以使用缺省内核的PHY驱动程序,但是如果PHY芯片的内部寄存器和802.3定义的并不一样,这就需要自己实现该驱动,像8201这样的百兆速率PHY芯片就可以使用缺省的PHY驱动,但是对于一些功能强一点的集成多个PHY的switch而言,通常驱动可能需要自己实现,switch常用来更高速率传输的或者VLAN这种网络。


PHY层从driver/net/phy/phy_device.c文件开始。

subsys_initcall(phy_init);
module_exit(phy_exit);

又见subsys_initcall,该调用表明其在系统初始化时完成,

static int __init phy_init(void)
{
int rc;


rc = mdio_bus_init();
if (rc)
return rc;


rc = phy_driver_register(&genphy_driver);
if (rc)
mdio_bus_exit();


return rc;
}。


其驱动涉及如下几个重要部分:
总线- sturct mii_bus     (mii stand for media independent interface)
设备- struct phy_device
驱动- struct phy_driver 

mdiobus_register  ------probe函数会调用,创建该总线,总线然后扫描其上面的设备,如果发现有,则创建该设备。
   _|_
drovers\net\phy\mdio_bus.c                                             drivers\net\phy\phy_device.c
struct phy_device *mdiobus_scan(struct mii_bus * bus, int addr) ----get_phy_device--phy_device_create
                                                                                                                        |
        |    drivers\base\Core.c

 ----device_register---device_add(kobject_add构建设备树)

驱动会调用phy_driver_register去注册驱动,这是phy驱动也会形成一个链表,这里之所以不是树而上面的设备成为树,是因为

mii可能挂载在pci总线上,当然pci上可能有其它设备,这些设备和phy设备不在一个链表上,而是通过kobject链接管理的,对应的还有
一个kset,kset其实是kobject的一个集合,相同的kobject对象会指向同一个kset,如USB鼠标、键盘就属于同一个kset集。

上述的总线,设备,以及驱动(假设驱动已经编写好了,驱动中多半会有一个标示符,暂且将其看作为id,这个id指明了其支持的设备)内;
假设不支持热插拔,那么启动时,系统就会扫描设备,并串联进设备树,当驱动注册(register时),驱动就会去设备树中,找id号和其对
应的相等的,如果找到,那么这两个就捆绑在一起了,通常为XXX_attcah函数,应用层对该设备的操作就变为调用驱动中对应的read、write、
ioctl等。如果支持热插拔,比上面的就复杂了,因为驱动可能先于设备(总线)存在,所以当有新硬件连接时,内核会调用XXX_probe函数去
探测,并注册设备,然后去驱动链表中查找驱动,如果找到,则attach。对其操作方法与上述一样。

剩下一个问题就是驱动的编写了。

#define PHY_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
SUPPORTED_10baseT_Full | \
SUPPORTED_100baseT_Half | \
SUPPORTED_100baseT_Full | \
SUPPORTED_Autoneg | \
SUPPORTED_TP | \
SUPPORTED_MII)

 
static int 8201f_config_init(struct phy_device *phydev)
{
int val;
u32 features;


/* For now, I'll claim that the generic driver supports
* all possible port types */
features =PHY_BASIC_FEATURES ;


/* Do we support autonegotiation? */
val = phy_read(phydev, MII_BMSR);


if (val < 0)
return val;


if (val & BMSR_ANEGCAPABLE)
features |= SUPPORTED_Autoneg;


if (val & BMSR_100FULL)
features |= SUPPORTED_100baseT_Full;
if (val & BMSR_100HALF)
features |= SUPPORTED_100baseT_Half;
if (val & BMSR_10FULL)
features |= SUPPORTED_10baseT_Full;
if (val & BMSR_10HALF)
features |= SUPPORTED_10baseT_Half;


if (val & BMSR_ESTATEN) {
val = phy_read(phydev, MII_ESTATUS);


if (val < 0)
return val;


if (val & ESTATUS_1000_TFULL)
features |= SUPPORTED_1000baseT_Full;
if (val & ESTATUS_1000_THALF)
features |= SUPPORTED_1000baseT_Half;
}


phydev->supported = features;
phydev->advertising = features;


return 0;
}


int 8201f_config_aneg(struct phy_device *phydev)
{
int result;


if (AUTONEG_ENABLE != phydev->autoneg)
return genphy_setup_forced(phydev);


result = genphy_config_advert(phydev);


if (result < 0) /* error */
return result;


if (result == 0) {
/* Advertisment hasn't changed, but maybe aneg was never on to
* begin with?  Or maybe phy was isolated? */
int ctl = phy_read(phydev, MII_BMCR);


if (ctl < 0)
return ctl;


if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
result = 1; /* do restart aneg */
}


/* Only restart aneg if we are advertising something different
* than we were before. */
if (result > 0)
result = genphy_restart_aneg(phydev);


return result;
}

static struct phy_driver 8201f_driver = {
        .phy_id         = 0x001cc810,
        .name           = "Realtack 8201f",
        .phy_id_mask    = 0x0ffffff0,
        .features       = PHY_BASIC_FEATURES,
        .config_init    = 8201f_config_init,
        .config_aneg    = 8201f_config_aneg,
        .read_status    = genphy_read_status,
        .driver         = { .owner = THIS_MODULE,},
};

static int __init 8201f_init(void)
{
        int ret;


        ret = phy_driver_register(&8201f_driver);
        if (ret)
           phy_driver_unregister(&8201f_driver);
        return ret;
}


static void __exit 8201f_exit(void)
{
        phy_driver_unregister(&8201f_driver);


}

module_init(8201f_init);
module_exit(8201f_exit);

你可能感兴趣的:(网络)