当通过ethtool -p eth0 20 时,网口eth0的指示灯会闪烁或者常亮。其在kernel中实现的flow为
int dev_ethtool(struct net *net, struct ifreq *ifr)
{
case ETHTOOL_PHYS_ID:
rc = ethtool_phys_id(dev, useraddr);
break;
}
static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
{
#网口的驱动需要实现set_phys_id 回调函数
rc = ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE);
if (rc < 0)
return rc;
/* Drop the RTNL lock while waiting, but prevent reentry or
* removal of the device.
*/
busy = true;
dev_hold(dev);
rtnl_unlock();
#set_phys_id 函数如果返回0的话,说明driver自己会控制灯是常亮还是闪烁。并行在灯常亮或者闪烁
#时,当前thread 处于sleep状态
if (rc == 0) {
/* Driver will handle this itself */
schedule_timeout_interruptible(
id.data ? (id.data * HZ) : MAX_SCHEDULE_TIMEOUT);
} else {
#rc 不等于零的话,说明driver 只执行灯的亮或者灭的动作,所以这个采用while 循环来让灯 执行亮和灭 从而实现闪烁
/* Driver expects to be called at twice the frequency in rc */
int n = rc * 2, i, interval = HZ / n;
/* Count down seconds */
do {
/* Count down iterations per second */
i = n;
do {
rtnl_lock();
#灯的亮和灭交替进行
rc = ops->set_phys_id(dev,
(i & 1) ? ETHTOOL_ID_OFF : ETHTOOL_ID_ON);
rtnl_unlock();
if (rc)
break;
schedule_timeout_interruptible(interval);
} while (!signal_pending(current) && --i != 0);
} while (!signal_pending(current) &&
(id.data == 0 || --id.data != 0));
}
rtnl_lock();
dev_put(dev);
busy = false;
#最后把这个灯灭掉
(void) ops->set_phys_id(dev, ETHTOOL_ID_INACTIVE);
return rc;
}
我们以kernel-master\drivers\net\ethernet\hisilicon\hns 的实现的set_phys_id 来看看具体的实现
static const struct ethtool_ops hns_ethtool_ops = {
.set_phys_id = hns_set_phys_id,
}
int hns_set_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state)
{
struct hns_nic_priv *priv = netdev_priv(netdev);
struct hnae_handle *h = priv->ae_handle;
struct phy_device *phy_dev = netdev->phydev;
int ret;
if (phy_dev)
switch (state) {
#可以看到这里是由phy来实现等的亮和灭的
case ETHTOOL_ID_ON:
ret = hns_phy_led_set(netdev, HNS_LED_FORCE_ON);
if (ret)
return ret;
break;
case ETHTOOL_ID_OFF:
ret = hns_phy_led_set(netdev, HNS_LED_FORCE_OFF);
if (ret)
return ret;
break;
}
在hns_phy_led_set 中通过寄存器来控制灯的亮和灭
int hns_phy_led_set(struct net_device *netdev, int value)
{
int retval;
struct phy_device *phy_dev = netdev->phydev;
retval = phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_LED);
retval |= phy_write(phy_dev, HNS_LED_FC_REG, value);
retval |= phy_write(phy_dev, HNS_PHY_PAGE_REG, HNS_PHY_PAGE_COPPER);
if (retval) {
netdev_err(netdev, "mdiobus_write fail !\n");
return retval;
}
return 0;
}