(转载)原文链接:https://blog.csdn.net/u014044624/article/details/123303714
前面几章基本上完成了mdio模块驱动模型的分析,本篇文章主要讲述phy device的状态机以及phy device与net_device的关联。Phy device主要是对phy的抽象,而net_device主要是对mac的抽象,而mdio总线以及mii_bus主要用于对phy设备的控制(包括设置phy设备的工作模式、速率模式、是否为自适应、phy的link状态等),而net_device需要根据phy的工作模式、速率模式、phy的link状态进行状态的同步,因此需要将net_device与phy device进行绑定与解绑。本章主要分为两个方面:
一、phy_device与net_device的绑定与解绑
二、phy device的状态机
一、phy_device与net_device的绑定与解绑操作。
什么时候实现phy_device与net_device的绑定与解绑操作?
我们从现有各厂家的net_device驱动可以看到,大致存在两种模式:
- 在net_device的probe接口中,根据传递的mdio_bus_id、phy_id,查找phy_device并完成net_device与phy_device的关联;在net_device的remove接口中,完成net_device与phy_device的解绑操作;
- 在net_device->netdev_ops->ndo_open接口中,据传递的mdio_bus_id、phy_id,查找phy_device并完成net_device与phy_device的关联;在nnet_device->netdev_ops->ndo_stop中,完成net_device与phy_device的解绑操作(即应用层执行ifconfig xxx up/down操作时调用ndo_open/ndo_stop)。
关于phy_device与net_device的绑定与解绑主要涉及如下几个函数phy_connect、phy_disconnect
下面我们分析下这两个接口的主要工呢
phy_connect分析
该接口主要用来进行一个net_device类型的变量与phy_device类型的变量的关联、设置phy_device的状态为PHY_READY,并启动phy_device的状态机,详细处理流程如下图所示
- 确认phy_device是否存在(根据传递的phy_device_id,在mdio_bus_type中查找phy_deviec);
- 若phy_device存在,则进行net_device与phy_device的相互关联(phy_device->attached_dev、net_device->phydev),并调用phy_driver->config_init完成对phy device的初始化;
- 启动phy_device的状态机对应的延迟队列。
phy_disconnect分析
phy_disconnect的操作刚好与phy_connect接口的操作相反,主要提供的功能如下:
- 若phy_device支持中断,则调用phy_stop_interrupts关闭中断,并free该中断;
- 调用phy_stop_machine接口,关闭该phy_device的状态机对应的延迟队列;
- 解除net_device与phy_device关联,若该phy_device是与genphy_driver绑定,则调用device_release_driver解除它们之间的绑定关系。
phy_device irq相关说明
在上面的介绍中,我们提到了phy_device的中断,针对phy_device的中断而言,当两个phy之间的link up/down的变化后(两个phy完成速率协商或者连接断开时),则会触发该中断,从而触发phy_device的状态机的转变(状态转换至PHY_RUNNING/PHY_NOLINK)。
那若phy_device不支持中断模式呢?那就使用PHY_POLL模式,在该模式下则当phy_device处于PHY_RUNNING状态时,则将phy_device状态自动切换至PHY_CHANGELINK状态,而在PHY_CHANGELINK状态下通过对phy_device的状态寄存器,确定phy_device处于link up/down状态。这类似于之前介绍的MMC部分的mmc card的在位检测模式(若支持中断则通过中断方式通知mmc card的在位与否;若不支持中断,则进行周期1s的延迟队列的调度进行轮询检测)。但与mmc模块不同的是,针对phy_device的状态机对应的延迟队列而言,不管其phy_device是否支持link up/down中断,其状态机对应延迟队列均为周期1s执行(不支持中断则由phy_device的状态机自动切换phy的状态进行检测,支持中断则由中断处理函数中进行状态的切换)。
如下即为执行完phy_connect操作后,针对支持phy_device中断处理接口(phy_interrupt)以及其底半部处理接口(phy_change)与phy_state_machine之间的关联。
phy device的状态机
在调用phy_connect完成net_device与phy_device的关联后,则启动phy_device的状态机流转对应的延迟队列调度接口,进行phy_device的状态流转操作。如下图为phy_device的状态流程图,下面详细说明下各状态的流转说明:
- 当调用phy_device_create创建一个phy_device或者调用phy_remove移除phy_device时,即将phy_device的状态设置为PHY_DOWN;
- PHY_START状态,目前在linux3.10的内核中,没有将phy_device的状态切换至PHY_START步骤,因此先跳过;
- 当调用phy_connect完成phy_device与net_device的绑定后,即将phy_device的状态设置为PHY_READY状态;或者在phy_device_register、phy_driver_register时完成phy_device与phy_driver的绑定后,也会将其状态设置为PHY_READY。
- 当系统调用phy_start接口,且其状态为PHY_READY时,则将其状态流转至PHY_UP状态(一般在net_device->net_ops->ndo_start中,完成phy_connect调用后,则调用phy_start将phy状态流转至PHY_UP);
- 在PHY_UP状态时,若该phy_device设置了自适应,则自动将其状态流转至PHY_AN;若该PHY_DEVICE设置为FORCE状态,则自动将其状态流转至PHY_FORCING;
- 在PHY_AN状态下,根据读取的phy_device的状态寄存器,若linkup,则将其状态流转至PHY_RUNNING;否则将其状态流转至PHY_NOLINK;
- 在PHY_NOLINK状态下,根据读取的phy_device的状态寄存器,若linkup,则将其状态流转至PHY_RUNNING;
- 在PHY_FORCING状态下,若读取phy_device的状态寄存器,为linkup,则将其状态流转至PHY_RUNNING;
- PHY_RUNNING状态下,若phy_device不支持中断,则将其状态设置为PHY_CHANGELINK,进行phy_device linkup状态的轮询检测;
- 在PHY_CHANGELINK状态下,根据读取的phy_device的状态寄存器,若linkup,则将其状态流转至PHY_RUNNING;否则则将其状态设置为PHY_NOLINK;
- 当各驱动模块调用phy_stop接口后,则phy_device的状态跳转至PHY_HALTED状态(无条件跳转,一般在net_device->netdev_ops->ndo_stop中会调用phy_stop);
- 仅在phy_device处于PHY_HALTED状态时,方才会流转至该PHY_RESUMING状态,该状态可跳转至PHY_AN、PHY_NOLINK、PHY_RUNNING。
以上即为phy_device的状态,当phy_device处于PHY_RUNNING状态时(此时调用netif_carrier_on设置net_device状态),说明两端的phy协商成功,可进行数据通信。以上phy_device的状态流转由接口phy_state_machine实现,该接口的流程图如下:
主要说明如下:
- 当phy_device处于PHY_UP状态时,则phy_device状态机处理接口,设置needs_aneg为1,调用phy_start_ange接口,进入PHY_AN状态或PHY_FORCING状态,进行phy_device的状态的协商;
- 在PHY_AN状态时,则phy_state_machine判断是否需要将状态跳转至PHY_RUNNING或PHY_NOLINK;
- 在PHY_RUNNING状态时,若phy_device不支持中断,则将其状态跳转至PHY_CHANNGELINK状态,由phy_state_machine接口来轮询判断phy_device的状态是否跳转至PHY_RUNNING/PHY_NOLINK;
而像PHY_DOWN、PHY_START、PHY_READY、PHY_UP、PHY_HALTED、
PHY_RESUMING这几个状态,一般由外部模块调用相应的接口进行跳转,而非phy_state_machine自动流转。
针对net_device也有相应的状态定义,如下图所示主要涉及如下几个状态
enum netdev_state_t {undefined
__LINK_STATE_START,
__LINK_STATE_PRESENT,
__LINK_STATE_NOCARRIER,
__LINK_STATE_LINKWATCH_PENDING,
__LINK_STATE_DORMANT,
};
- 当net_device注册至系统时,即设置__LINK_STATE_PRESENT;
- 当执行ifconfig xxx up,启用一个net_device时,则将其状态设置为__LINK_STATE_START(记住此时并不代表phy已完成协商且处于linkup状态),并在调用net_device->netdev_ops->ndo_open时,设置__LINK_STATE_NOCARRIER;
- 当调用phy_connect完成phy_device与net_device的绑定以及启动phy_device的状态机后,且phy_device跳转至PHY_RUNNING的同时,清除__LINK_STATE_NOCARRIER位,此时方才表明可进行数据通信。
以上即是本次的主要内容,主要涉及phy_device与net_device之间的关联建立以及phy_device的状态机流转相关的内容。当phy_device处于PHY_RUNNING状态时,则将net_device的状态设置为carrier_on,即可进行mac层及mac层以上的数据通信工作。