IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux ARM平台开发系列讲解(网络篇)1.8 PHY中断机制分析 -> 正文阅读

[系统运维]Linux ARM平台开发系列讲解(网络篇)1.8 PHY中断机制分析

作者:https://csdnimg.cn/release/blogv2/dist/components/js/pc_wap_commontools-60a48feb57.min.js

1. phy如何获取phy中断中的irq编号

phy_device_create函数里面, dev->irqbus->irq[addr]获得, 所以如果希望phy设备跟中断联系起来,需要在mdio bus里面为irq[addr]赋值

  • 源码路径: drivers\net\phy\phy_device.c
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->port = PORT_TP;
	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);
	device_initialize(&mdiodev->dev);

	dev->state = PHY_DOWN;

	mutex_init(&dev->lock);
	/* 注意这里初始化了工作队列 */
    /* dev->state_queue这个工作队列在这里只是初始化了一下,但并没有调度,在什么地方调度呢? */
	INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);

	/* 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) {
		put_device(&mdiodev->dev);
		dev = ERR_PTR(ret);
	}

	return dev;
}

2. net_device的和phy_device何时关联到一起

net_deviceopen函数通常会调用phy_connect函数将net_device的和phy_device关联到一起。

cpsw.c为例,函数调用的顺序为:

  • 源码路径:drivers\net\ethernet\ti\cpsw.c

cpsw_ndo_open->
cpsw_slave_open->
phy_connect(priv->ndev, slave->data->phy_id,&cpsw_adjust_link, slave->data->phy_if);->
phy_connect_direct->
phy_start_machine(phydev);->
queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);

cpsw_slave_open函数中,调用了phy_connect,如下:

phy = phy_connect(priv->ndev, slave->data->phy_id,
		 &cpsw_adjust_link, slave->data->phy_if);
struct phy_device *phy_connect(struct net_device *dev, const char *bus_id,
			       void (*handler)(struct net_device *),
			       phy_interface_t interface)
{
	struct phy_device *phydev;
	struct device *d;
	int rc;

	/* Search the list of PHY devices on the mdio bus for the
	 * PHY with the requested name
	 */
	d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id);
	if (!d) {
		pr_err("PHY %s not found\n", bus_id);
		return ERR_PTR(-ENODEV);
	}
	phydev = to_phy_device(d);

	rc = phy_connect_direct(dev, phydev, handler, interface);
	put_device(d);
	if (rc)
		return ERR_PTR(rc);

	return phydev;
}

这里phy_connect主要是将mdio总线在scan时,已经调用phy_device_create函数创建了phy_device, 并且把这个device注册到mdio总线上了,此处从总线上找到名称为bus_idphy_device. 将net_devicephy_device建立起联系。

3. phy_device_create 时对队列的初始化

phy_device_create中会有对state_queue的初始化

INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);

4. phy_device和phy_driver建立的关键

在上述中,提到了phy_connect,它主要是对phy_devicephy_driver进行连接,其中,调用了phy_connect_direct函数,这个函数才是devdrv建立的关键。如下源代码中:phy_attach_direct会找到phy_device,然后将其连接到phy_driver上。

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;
	/* 将设备连接到phy指针 */
	rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);
	if (rc)
		return rc;

	phy_prepare_link(phydev, handler);
	 /* 如果这个phy设备支持中断,就会进行中断的注册 */
	if (phy_interrupt_is_valid(phydev))
		phy_request_interrupt(phydev);

	return 0;
}

5. phy中断的注册

在上述第4小节函数phy_connect_direct中,phydriverdevice进行了匹配连接,在连接完成后,判断了phy是否支持中断,如果支持中断,就会进行phy中断的注册

	if (phy_interrupt_is_valid(phydev))
		phy_request_interrupt(phydev);
void phy_request_interrupt(struct phy_device *phydev)
{
	int err;

	err = request_threaded_irq(phydev->irq, NULL, phy_interrupt,
				   IRQF_ONESHOT | IRQF_SHARED,
				   phydev_name(phydev), phydev);
	if (err) {
		phydev_warn(phydev, "Error %d requesting IRQ %d, falling back to polling\n",
			    err, phydev->irq);
		phydev->irq = PHY_POLL;
	} else {
		if (phy_enable_interrupts(phydev)) {
			phydev_warn(phydev, "Can't enable interrupt, falling back to polling\n");
			phy_free_interrupt(phydev);
			phydev->irq = PHY_POLL;
		}
	}
}
EXPORT_SYMBOL(phy_request_interrupt);

6. phy中断的回调函数

在注册phy中断的过程中,注册了phy中断回调函数,进入回调函数后,会调用phy的工作队列,这个工作队列前面也提到了,在phy_device_create时已经对其进行了初始化。

static irqreturn_t phy_interrupt(int irq, void *phy_dat)
{
	struct phy_device *phydev = phy_dat;
	struct phy_driver *drv = phydev->drv;

	if (drv->handle_interrupt)
		return drv->handle_interrupt(phydev);

	if (drv->did_interrupt && !drv->did_interrupt(phydev))
		return IRQ_NONE;

	/* reschedule state queue work to run as soon as possible */
	phy_trigger_machine(phydev);

	/* did_interrupt() may have cleared the interrupt already */
	if (!drv->did_interrupt && phy_clear_interrupt(phydev)) {
		phy_error(phydev);
		return IRQ_NONE;
	}

	return IRQ_HANDLED;
}

返回总目录

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-06-26 17:06:53  更:2022-06-26 17:07:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 0:44:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码