ethtool -p 点灯

当通过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;
}

你可能感兴趣的:(Linux,源码分析)