phy link检测机制记录

phy link检测机制记录

当我们在注册miibus的时候devm_mdiobus_register,会去扫描phy设备,如果扫描到以后会注册phydevice设备。然后我们可以使用mdiobus_get_phy来获取扫描到的设备,这个注册Phy设备的时候,会给phy设备创建一个work。
mdiobus_scan->get_phy_device->phy_device_create ->INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);

struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
				     bool is_c45,
				     struct phy_c45_device_ids *c45_ids)
{
	struct phy_device *dev;
	struct mdio_device *mdiodev;
	int ret = 0;

	/* We allocate the device, and initialize the default values */
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return ERR_PTR(-ENOMEM);

	mdiodev = &dev->mdio;
	mdiodev->dev.parent = &bus->dev;
	mdiodev->dev.bus = &mdio_bus_type;
	mdiodev->dev.type = &mdio_bus_phy_type;
	mdiodev->bus = bus;
	mdiodev->bus_match = phy_bus_match;
	mdiodev->addr = addr;
	mdiodev->flags = MDIO_DEVICE_FLAG_PHY;
	mdiodev->device_free = phy_mdio_device_free;
	mdiodev->device_remove = phy_mdio_device_remove;

	dev->speed = SPEED_UNKNOWN;
	dev->duplex = DUPLEX_UNKNOWN;
	dev->pause = 0;
	dev->asym_pause = 0;
	dev->link = 0;
	dev->interface = PHY_INTERFACE_MODE_GMII;

	dev->autoneg = AUTONEG_ENABLE;

	dev->is_c45 = is_c45;
	dev->phy_id = phy_id;
	if (c45_ids)
		dev->c45_ids = *c45_ids;
	dev->irq = bus->irq[addr];
	dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);

	dev->state = PHY_DOWN;

	mutex_init(&dev->lock);
	INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); //work

	/* Request the appropriate module unconditionally; don't
	 * bother trying to do so only if it isn't already loaded,
	 * because that gets complicated. A hotplug event would have
	 * done an unconditional modprobe anyway.
	 * We don't do normal hotplug because it won't work for MDIO
	 * -- because it relies on the device staying around for long
	 * enough for the driver to get loaded. With MDIO, the NIC
	 * driver will get bored and give up as soon as it finds that
	 * there's no driver _already_ loaded.
	 */
	if (is_c45 && c45_ids) {
		const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
		int i;

		for (i = 1; i < num_ids; i++) {
			if (c45_ids->device_ids[i] == 0xffffffff)
				continue;

			ret = phy_request_driver_module(dev,
						c45_ids->device_ids[i]);
			if (ret)
				break;
		}
	} else {
		ret = phy_request_driver_module(dev, phy_id);
	}

	if (!ret) {
		device_initialize(&mdiodev->dev);
	} else {
		kfree(dev);
		dev = ERR_PTR(ret);
	}

	return dev;
}

/**
 * phy_state_machine - Handle the state machine
 * @work: work_struct that describes the work to be done
 */
void phy_state_machine(struct work_struct *work)
{
	struct delayed_work *dwork = to_delayed_work(work);
	struct phy_device *phydev =
			container_of(dwork, struct phy_device, state_queue);
	bool needs_aneg = false, do_suspend = false;
	enum phy_state old_state;
	int err = 0;

	mutex_lock(&phydev->lock);

	old_state = phydev->state;

	switch (phydev->state) {
	case PHY_DOWN:
	case PHY_READY:
		break;
	case PHY_UP:
		needs_aneg = true;

		break;
	case PHY_NOLINK:
	case PHY_RUNNING:
		err = phy_check_link_status(phydev);  //检测Phy状态
		break;
	case PHY_HALTED:
		if (phydev->link) {
			phydev->link = 0;
			phy_link_down(phydev, true); //通告phy的down状态
		}
		do_suspend = true;
		break;
	}

	mutex_unlock(&phydev->lock);

	if (needs_aneg)
		err = phy_start_aneg(phydev);
	else if (do_suspend)
		phy_suspend(phydev);

	if (err < 0)
		phy_error(phydev);

	if (old_state != phydev->state) {
		phydev_dbg(phydev, "PHY state change %s -> %s\n",
			   phy_state_to_str(old_state),
			   phy_state_to_str(phydev->state));
		if (phydev->drv && phydev->drv->link_change_notify)
			phydev->drv->link_change_notify(phydev);
	}

	/* Only re-schedule a PHY state machine change if we are polling the
	 * PHY, if PHY_IGNORE_INTERRUPT is set, then we will be moving
	 * between states from phy_mac_interrupt().
	 *
	 * In state PHY_HALTED the PHY gets suspended, so rescheduling the
	 * state machine would be pointless and possibly error prone when
	 * called from phy_disconnect() synchronously.
	 */
	mutex_lock(&phydev->lock);
	if (phy_polling_mode(phydev) && phy_is_started(phydev))
		phy_queue_state_machine(phydev, PHY_STATE_TIME);
	mutex_unlock(&phydev->lock);
}
static int phy_check_link_status(struct phy_device *phydev)
{
	int err;

	WARN_ON(!mutex_is_locked(&phydev->lock));

	/* Keep previous state if loopback is enabled because some PHYs
	 * report that Link is Down when loopback is enabled.
	 */
	if (phydev->loopback_enabled)
		return 0;

	err = phy_read_status(phydev);
	if (err)
		return err;

	if (phydev->link && phydev->state != PHY_RUNNING) {
		phydev->state = PHY_RUNNING;
		phy_link_up(phydev);  //通告phy up状态
	} else if (!phydev->link && phydev->state != PHY_NOLINK) {
		phydev->state = PHY_NOLINK;
		phy_link_down(phydev, true); //通告phy down状态
	}

	return 0;
}

static void phy_link_up(struct phy_device *phydev)
{
	phydev->phy_link_change(phydev, true, true);  //回调通知
	phy_led_trigger_change_speed(phydev);
}

static void phy_link_down(struct phy_device *phydev, bool do_carrier)
{
	phydev->phy_link_change(phydev, false, do_carrier); //回调通知
	phy_led_trigger_change_speed(phydev);
}

所以说我们主要是设置回调 的方式来将phy的状态反馈给net_device或者用户。

方式一:
使用phy_connect_direct注册

int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,
		       void (*handler)(struct net_device *),
		       phy_interface_t interface)
{
	int rc;

	if (!dev)
		return -EINVAL;

	rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);  //这个里面设置phy_link_change
	if (rc)
		return rc;

	phy_prepare_link(phydev, handler);//设置回调函数
	if (phy_interrupt_is_valid(phydev))
		phy_request_interrupt(phydev);

	return 0;
}
EXPORT_SYMBOL(phy_connect_direct);
int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,u32 flags, phy_interface_t interface)
{
.....
phydev->phy_link_change = phy_link_change;
.......
}
static void phy_link_change(struct phy_device *phydev, bool up, bool do_carrier)
{
	struct net_device *netdev = phydev->attached_dev;

	if (do_carrier) {
		if (up)
			netif_carrier_on(netdev);  //设置mac状态
		else
			netif_carrier_off(netdev);
	}
	phydev->adjust_link(netdev);//这个就是我们设置的回调函数
	if (phydev->mii_ts && phydev->mii_ts->link_state)
		phydev->mii_ts->link_state(phydev->mii_ts, phydev);
}
static void phy_prepare_link(struct phy_device *phydev,
			     void (*handler)(struct net_device *))
{
	phydev->adjust_link = handler;
}

例子:

static int r8169_phy_connect(struct rtl8169_private *tp)
{
	struct phy_device *phydev = tp->phydev;
	phy_interface_t phy_mode;
	int ret;

	phy_mode = tp->supports_gmii ? PHY_INTERFACE_MODE_GMII :
		   PHY_INTERFACE_MODE_MII;

	ret = phy_connect_direct(tp->dev, phydev, r8169_phylink_handler,
				 phy_mode);
	if (ret)
		return ret;

	if (!tp->supports_gmii)
		phy_set_max_speed(phydev, SPEED_100);

	phy_support_asym_pause(phydev);

	phy_attached_info(phydev);

	return 0;
}

//根据phy的状态设置mac
static void r8169_phylink_handler(struct net_device *ndev)
{
	struct rtl8169_private *tp = netdev_priv(ndev);

	if (netif_carrier_ok(ndev)) {  //获取MAC状态
		rtl_link_chg_patch(tp);
		pm_request_resume(&tp->pci_dev->dev);
	} else {
		pm_runtime_idle(&tp->pci_dev->dev);
	}

	if (net_ratelimit())
		phy_print_status(tp->phydev);
}

方式二:
使用phylink设备的方式 struct phylink ;通过phy的状态,反向配置mac。
先将mac的配置操作注册进去:

struct phylink *phylink_create(struct phylink_config *config,
			       struct fwnode_handle *fwnode,
			       phy_interface_t iface,
			       const struct phylink_mac_ops *ops)
{
	struct phylink *pl;
	int ret;

	pl = kzalloc(sizeof(*pl), GFP_KERNEL);
	if (!pl)
		return ERR_PTR(-ENOMEM);

	mutex_init(&pl->state_mutex);
	INIT_WORK(&pl->resolve, phylink_resolve);  //work

	pl->config = config;
	if (config->type == PHYLINK_NETDEV) {
		pl->netdev = to_net_dev(config->dev);
	} else if (config->type == PHYLINK_DEV) {
		pl->dev = config->dev;
	} else {
		kfree(pl);
		return ERR_PTR(-EINVAL);
	}

	pl->phy_state.interface = iface;
	pl->link_interface = iface;
	if (iface == PHY_INTERFACE_MODE_MOCA)
		pl->link_port = PORT_BNC;
	else
		pl->link_port = PORT_MII;
	pl->link_config.interface = iface;
	pl->link_config.pause = MLO_PAUSE_AN;
	pl->link_config.speed = SPEED_UNKNOWN;
	pl->link_config.duplex = DUPLEX_UNKNOWN;
	pl->link_config.an_enabled = true;
	pl->ops = ops;  //这里就是将MAC的操作配置进来,通过Phy状态配置MAC
	__set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
	timer_setup(&pl->link_poll, phylink_fixed_poll, 0);  

	bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
	linkmode_copy(pl->link_config.advertising, pl->supported);
	phylink_validate(pl, pl->supported, &pl->link_config);

	ret = phylink_parse_mode(pl, fwnode);
	if (ret < 0) {
		kfree(pl);
		return ERR_PTR(ret);
	}

	if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
		ret = phylink_parse_fixedlink(pl, fwnode);
		if (ret < 0) {
			kfree(pl);
			return ERR_PTR(ret);
		}
	}

	pl->cur_link_an_mode = pl->cfg_link_an_mode;

	ret = phylink_register_sfp(pl, fwnode);
	if (ret < 0) {
		kfree(pl);
		return ERR_PTR(ret);
	}

	return pl;
}
EXPORT_SYMBOL_GPL(phylink_create)

2、挂钩子函数

int phylink_connect_phy(struct phylink *pl, struct phy_device *phy)
{
	int ret;

	/* Use PHY device/driver interface */
	if (pl->link_interface == PHY_INTERFACE_MODE_NA) {
		pl->link_interface = phy->interface;
		pl->link_config.interface = pl->link_interface;
	}

	ret = phylink_attach_phy(pl, phy, pl->link_interface);
	if (ret < 0)
		return ret;

	ret = phylink_bringup_phy(pl, phy, pl->link_config.interface);
	if (ret)
		phy_detach(phy);

	return ret;
}
EXPORT_SYMBOL_GPL(phylink_connect_phy);
static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
			       phy_interface_t interface)
{
	struct phylink_link_state config;
	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
	char *irq_str;
	int ret;

	/*
	 * This is the new way of dealing with flow control for PHYs,
	 * as described by Timur Tabi in commit 529ed1275263 ("net: phy:
	 * phy drivers should not set SUPPORTED_[Asym_]Pause") except
	 * using our validate call to the MAC, we rely upon the MAC
	 * clearing the bits from both supported and advertising fields.
	 */
	phy_support_asym_pause(phy);

	memset(&config, 0, sizeof(config));
	linkmode_copy(supported, phy->supported);
	linkmode_copy(config.advertising, phy->advertising);

	/* Clause 45 PHYs switch their Serdes lane between several different
	 * modes, normally 10GBASE-R, SGMII. Some use 2500BASE-X for 2.5G
	 * speeds. We really need to know which interface modes the PHY and
	 * MAC supports to properly work out which linkmodes can be supported.
	 */
	if (phy->is_c45 &&
	    interface != PHY_INTERFACE_MODE_RXAUI &&
	    interface != PHY_INTERFACE_MODE_XAUI &&
	    interface != PHY_INTERFACE_MODE_USXGMII)
		config.interface = PHY_INTERFACE_MODE_NA;
	else
		config.interface = interface;

	ret = phylink_validate(pl, supported, &config);
	if (ret) {
		phylink_warn(pl, "validation of %s with support %*pb and advertisement %*pb failed: %d\n",
			     phy_modes(config.interface),
			     __ETHTOOL_LINK_MODE_MASK_NBITS, phy->supported,
			     __ETHTOOL_LINK_MODE_MASK_NBITS, config.advertising,
			     ret);
		return ret;
	}

	phy->phylink = pl;
	phy->phy_link_change = phylink_phy_change;   //这个钩子,和方式一不一样

	irq_str = phy_attached_info_irq(phy);
	phylink_info(pl,
		     "PHY [%s] driver [%s] (irq=%s)\n",
		     dev_name(&phy->mdio.dev), phy->drv->name, irq_str);
	kfree(irq_str);

	mutex_lock(&phy->lock);
	mutex_lock(&pl->state_mutex);
	pl->phydev = phy;
	pl->phy_state.interface = interface;
	linkmode_copy(pl->supported, supported);
	linkmode_copy(pl->link_config.advertising, config.advertising);

	/* Restrict the phy advertisement according to the MAC support. */
	linkmode_copy(phy->advertising, config.advertising);
	mutex_unlock(&pl->state_mutex);
	mutex_unlock(&phy->lock);

	phylink_dbg(pl,
		    "phy: setting supported %*pb advertising %*pb\n",
		    __ETHTOOL_LINK_MODE_MASK_NBITS, pl->supported,
		    __ETHTOOL_LINK_MODE_MASK_NBITS, phy->advertising);

	if (phy_interrupt_is_valid(phy))
		phy_request_interrupt(phy);

	return 0;
}
static void phylink_phy_change(struct phy_device *phydev, bool up,
			       bool do_carrier)
{
	struct phylink *pl = phydev->phylink;

	mutex_lock(&pl->state_mutex);
	pl->phy_state.speed = phydev->speed;  
	pl->phy_state.duplex = phydev->duplex;
	pl->phy_state.pause = MLO_PAUSE_NONE;
	if (phydev->pause)
		pl->phy_state.pause |= MLO_PAUSE_SYM;
	if (phydev->asym_pause)
		pl->phy_state.pause |= MLO_PAUSE_ASYM;
	pl->phy_state.interface = phydev->interface;
	pl->phy_state.link = up;
	mutex_unlock(&pl->state_mutex);   //上面记录phy状态

	phylink_run_resolve(pl);  //让work 工作,根据phy状态设置MAC

	phylink_dbg(pl, "phy link %s %s/%s/%s\n", up ? "up" : "down",
		    phy_modes(phydev->interface),
		    phy_speed_to_str(phydev->speed),
		    phy_duplex_to_str(phydev->duplex));
}
static void phylink_resolve(struct work_struct *w)
{
	struct phylink *pl = container_of(w, struct phylink, resolve);
	struct phylink_link_state link_state;
	struct net_device *ndev = pl->netdev;
	int link_changed;

	mutex_lock(&pl->state_mutex);
	if (pl->phylink_disable_state) {
		pl->mac_link_dropped = false;
		link_state.link = false;
	} else if (pl->mac_link_dropped) {
		link_state.link = false;
	} else {
		switch (pl->cur_link_an_mode) {
		case MLO_AN_PHY:
			link_state = pl->phy_state;
			phylink_resolve_flow(pl, &link_state);
			phylink_mac_config_up(pl, &link_state);
			break;

		case MLO_AN_FIXED:
			phylink_get_fixed_state(pl, &link_state);
			phylink_mac_config_up(pl, &link_state);
			break;

		case MLO_AN_INBAND:
			phylink_mac_pcs_get_state(pl, &link_state);

			/* If we have a phy, the "up" state is the union of
			 * both the PHY and the MAC */
			if (pl->phydev)
				link_state.link &= pl->phy_state.link;

			/* Only update if the PHY link is up */
			if (pl->phydev && pl->phy_state.link) {
				link_state.interface = pl->phy_state.interface;

				/* If we have a PHY, we need to update with
				 * the pause mode bits. */
				link_state.pause |= pl->phy_state.pause;
				phylink_resolve_flow(pl, &link_state);
				phylink_mac_config(pl, &link_state);
			}
			break;
		}
	}

	if (pl->netdev)
		link_changed = (link_state.link != netif_carrier_ok(ndev));
	else
		link_changed = (link_state.link != pl->old_link_state);

	if (link_changed) {
		pl->old_link_state = link_state.link;
		if (!link_state.link)
			phylink_mac_link_down(pl);
		else
			phylink_mac_link_up(pl, link_state);
	}
	if (!link_state.link && pl->mac_link_dropped) {
		pl->mac_link_dropped = false;
		queue_work(system_power_efficient_wq, &pl->resolve);
	}
	mutex_unlock(&pl->state_mutex);
}

例子:

static int stmmac_phy_setup(struct stmmac_priv *priv)
{
	struct fwnode_handle *fwnode = of_fwnode_handle(priv->plat->phylink_node);
	int mode = priv->plat->phy_interface;
	struct phylink *phylink;

	priv->phylink_config.dev = &priv->dev->dev;
	priv->phylink_config.type = PHYLINK_NETDEV;

	phylink = phylink_create(&priv->phylink_config, fwnode,
				 mode, &stmmac_phylink_mac_ops);
	if (IS_ERR(phylink))
		return PTR_ERR(phylink);

	priv->phylink = phylink;
	return 0;
}

static const struct phylink_mac_ops stmmac_phylink_mac_ops = {
	.validate = stmmac_validate,
	.mac_pcs_get_state = stmmac_mac_pcs_get_state,
	.mac_config = stmmac_mac_config,
	.mac_an_restart = stmmac_mac_an_restart,
	.mac_link_down = stmmac_mac_link_down,
	.mac_link_up = stmmac_mac_link_up,
};
static int stmmac_init_phy(struct net_device *dev)
{
	struct stmmac_priv *priv = netdev_priv(dev);
	struct device_node *node;
	int ret;

	node = priv->plat->phylink_node;

	if (node)
		ret = phylink_of_phy_connect(priv->phylink, node, 0); //这里

	/* Some DT bindings do not set-up the PHY handle. Let's try to
	 * manually parse it
	 */
	if (!node || ret) {
		int addr = priv->plat->phy_addr;
		struct phy_device *phydev;

		phydev = mdiobus_get_phy(priv->mii, addr);
		if (!phydev) {
			netdev_err(priv->dev, "no phy at addr %d\n", addr);
			return -ENODEV;
		}

		ret = phylink_connect_phy(priv->phylink, phydev);
	}

	return ret;
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个IO-Link PHY驱动示例,代码基于C语言: ```c #include <stdint.h> #include <stdbool.h> #include <stdio.h> /* 定义IO-Link PHY寄存器地址 */ #define IOLINK_PHY_REG_CTRL 0x00 #define IOLINK_PHY_REG_STATUS 0x01 #define IOLINK_PHY_REG_DATA 0x02 #define IOLINK_PHY_REG_CFG 0x03 /* 定义IO-Link PHY控制寄存器位 */ #define IOLINK_PHY_CTRL_RESET (1 << 0) #define IOLINK_PHY_CTRL_ENABLE (1 << 1) /* 定义IO-Link PHY状态寄存器位 */ #define IOLINK_PHY_STATUS_READY (1 << 0) #define IOLINK_PHY_STATUS_LINK (1 << 1) #define IOLINK_PHY_STATUS_ERROR (1 << 2) /* 定义IO-Link PHY配置寄存器位 */ #define IOLINK_PHY_CFG_DATA_SIZE (0x0F << 0) #define IOLINK_PHY_CFG_DATA_SIZE_8 (0x00 << 0) #define IOLINK_PHY_CFG_DATA_SIZE_16 (0x01 << 0) #define IOLINK_PHY_CFG_DATA_SIZE_24 (0x02 << 0) #define IOLINK_PHY_CFG_DATA_SIZE_32 (0x03 << 0) #define IOLINK_PHY_CFG_DATA_SIZE_40 (0x04 << 0) #define IOLINK_PHY_CFG_DATA_SIZE_48 (0x05 << 0) #define IOLINK_PHY_CFG_DATA_SIZE_56 (0x06 << 0) #define IOLINK_PHY_CFG_DATA_SIZE_64 (0x07 << 0) /* 定义IO-Link PHY数据寄存器位 */ #define IOLINK_PHY_DATA_MASK (0x3FFF << 0) #define IOLINK_PHY_DATA_ERROR (1 << 15) /* 定义IO-Link PHY错误码 */ #define IOLINK_PHY_ERROR_NONE 0 #define IOLINK_PHY_ERROR_TIMEOUT 1 #define IOLINK_PHY_ERROR_CRC 2 /* 定义IO-Link PHY驱动结构体 */ typedef struct { uint8_t addr; // IO-Link PHY地址 uint8_t cfg; // IO-Link PHY配置寄存器值 } iolink_phy_t; /* IO-Link PHY初始化函数 */ void iolink_phy_init(iolink_phy_t *phy) { /* 将控制寄存器清零 */ uint8_t ctrl = 0; ctrl &= ~IOLINK_PHY_CTRL_RESET; ctrl &= ~IOLINK_PHY_CTRL_ENABLE; /* 将配置寄存器设置为默认值 */ uint8_t cfg = phy->cfg; /* 写入控制寄存器和配置寄存器 */ iolink_phy_write_reg(phy->addr, IOLINK_PHY_REG_CTRL, ctrl); iolink_phy_write_reg(phy->addr, IOLINK_PHY_REG_CFG, cfg); } /* IO-Link PHY读取数据函数 */ int iolink_phy_read_data(iolink_phy_t *phy, uint16_t *data) { /* 等待IO-Link PHY准备好 */ uint32_t timeout = 10000; while ((iolink_phy_read_reg(phy->addr, IOLINK_PHY_REG_STATUS) & IOLINK_PHY_STATUS_READY) == 0) { if (--timeout == 0) { return IOLINK_PHY_ERROR_TIMEOUT; } } /* 读取数据 */ uint16_t val = iolink_phy_read_reg(phy->addr, IOLINK_PHY_REG_DATA); /* 检查错误 */ if (val & IOLINK_PHY_DATA_ERROR) { return IOLINK_PHY_ERROR_CRC; } /* 返回数据 */ *data = val & IOLINK_PHY_DATA_MASK; return IOLINK_PHY_ERROR_NONE; } /* IO-Link PHY写入数据函数 */ int iolink_phy_write_data(iolink_phy_t *phy, uint16_t data) { /* 等待IO-Link PHY准备好 */ uint32_t timeout = 10000; while ((iolink_phy_read_reg(phy->addr, IOLINK_PHY_REG_STATUS) & IOLINK_PHY_STATUS_READY) == 0) { if (--timeout == 0) { return IOLINK_PHY_ERROR_TIMEOUT; } } /* 写入数据 */ iolink_phy_write_reg(phy->addr, IOLINK_PHY_REG_DATA, data); /* 等待IO-Link PHY发送完数据 */ timeout = 10000; while ((iolink_phy_read_reg(phy->addr, IOLINK_PHY_REG_STATUS) & IOLINK_PHY_STATUS_LINK) == 0) { if (--timeout == 0) { return IOLINK_PHY_ERROR_TIMEOUT; } } /* 等待IO-Link PHY接收完数据 */ timeout = 10000; while ((iolink_phy_read_reg(phy->addr, IOLINK_PHY_REG_STATUS) & IOLINK_PHY_STATUS_READY) == 0) { if (--timeout == 0) { return IOLINK_PHY_ERROR_TIMEOUT; } } /* 检查错误 */ uint16_t val = iolink_phy_read_reg(phy->addr, IOLINK_PHY_REG_DATA); if (val & IOLINK_PHY_DATA_ERROR) { return IOLINK_PHY_ERROR_CRC; } /* 写入成功 */ return IOLINK_PHY_ERROR_NONE; } /* IO-Link PHY读取寄存器函数 */ uint8_t iolink_phy_read_reg(uint8_t addr, uint8_t reg) { /* TODO: 实现IO-Link PHY读取寄存器函数 */ } /* IO-Link PHY写入寄存器函数 */ void iolink_phy_write_reg(uint8_t addr, uint8_t reg, uint8_t val) { /* TODO: 实现IO-Link PHY写入寄存器函数 */ } ``` 以上代码仅为示例,实际使用时需要根据具体的IO-Link PHY芯片手册进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值