本节主要梳理linux网口驱动流程。

设备间的逻辑关联图及抽象

如下图为cpu与mac、phy之间的逻辑关联图,针对mac与phy之间的连接,包括两部分:

  1. 通过mii/rgmii/sgmii/…与phy连接,进行数据的通信;
  2. 通过mdc/mdio与phy连接,实现对phy设备的控制命令的下发等功能

MII控制总线

MII Management interface用于MAC层芯片控制和配置phy设备,而ieee 802.3中规定了phy device寄存器的额地址空间为5位,最多可定义32各寄存器,而ieee 802.3定义了地址为0-15这16个寄存器的功能,主要包括control、status、phy identifer、auto-neg相关等,16-31由厂家自行定义。

phy驱动中重要的有两个结构体

PHY设备

struct phy_device {struct phy_driver *drv;    //PHY设备驱动struct mii_bus *bus;    //对应的MII总线struct device dev;    //设备文件u32 phy_id;    //PHY IDenum phy_state state;    //PHY状态u32 dev_flags;phy_interface_t interface;    //PHY接口int addr;    //PHY 总线地址(0~31)int speed;    //速度int duplex;    //双工模式int pause;    //停止int asym_pause;    //int link;    u32 interrupts;    //中断使能标志u32 supported;    u32 advertising;int autoneg;int link_timeout;    //026int irq;    //中断号void *priv;    //私有数据struct work_struct phy_queue;    //PHY工作队列struct delayed_work state_queue;    //PHY延时工作队列atomic_t irq_disable;    struct mutex lock;struct net_device *attached_dev;    //网络设备void (*adjust_link)(struct net_device *dev);void (*adjust_state)(struct net_device *dev);
};

PHY驱动

struct phy_driver {u32 phy_id;       //PHY IDchar *name;     //PHY名unsigned int phy_id_mask;u32 features;    //特性u32 flags;  //标记int (*config_init)(struct phy_device *phydev);  //配置初始化int (*probe)(struct phy_device *phydev); //探测到 probe方法int (*suspend)(struct phy_device *phydev); //唤醒int (*resume)(struct phy_device *phydev);   //挂起int (*config_aneg)(struct phy_device *phydev);  //支援(Auto-negotiation)配置int (*read_status)(struct phy_device *phydev);  //读支援(Auto-negotiation)状态int (*ack_interrupt)(struct phy_device *phydev);   //清中断int (*config_intr)(struct phy_device *phydev); //使能/禁用 中断int (*did_interrupt)(struct phy_device *phydev);  //判断是否由中断void (*remove)(struct phy_device *phydev); //移除int  (*hwtstamp)(struct phy_device *phydev, struct ifreq *ifr); //时间戳处理bool (*rxtstamp)(struct phy_device *dev, struct sk_buff *skb, int type); //接收时间戳void (*txtstamp)(struct phy_device *dev, struct sk_buff *skb, int type); //发送时间戳struct device_driver driver; //设备驱动文件
};

设备和驱动的注册函数

1 注册PHY设备

int phy_device_register(struct phy_device *phydev)
{int err;if (phydev->bus->phy_map[phydev->addr])   //判断PHY是否已经给注册了return -EINVAL;phydev->bus->phy_map[phydev->addr] = phydev;    //添加PHY到总线的phy_map里phy_scan_fixups(phydev); //执行匹配的fixupserr = device_register(&phydev->dev);   //注册设备if (err) {pr_err("phy %d failed to register\n", phydev->addr);goto out;}return 0;out:phydev->bus->phy_map[phydev->addr] = NULL;return err;
}
EXPORT_SYMBOL(phy_device_register);

PHY设备一般是动态注册的,在注册之前一般会调用get_phy_device函数

struct phy_device * get_phy_device(struct mii_bus *bus, int addr)
{struct phy_device *dev = NULL;u32 phy_id;int r;r = get_phy_id(bus, addr, &phy_id);   //1 获取PHY IDif (r)return ERR_PTR(r);if ((phy_id & 0x1fffffff) == 0x1fffffff)return NULL;if ((phy_id & 0x1fffffff) == 0x0000ffff)//add by cyj, support marvel phyreturn NULL;dev = phy_device_create(bus, addr, phy_id);    //2 创建PHY设备return dev;
}
EXPORT_SYMBOL(get_phy_device);

获取PHY ID

int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id)
{int phy_reg;//调用PHY的总线也就是mii总线的读方法获取PHY IDphy_reg = bus->read(bus, addr, MII_PHYSID1); //获取PHYS ID1命令if (phy_reg < 0)return -EIO;*phy_id = (phy_reg & 0xffff) << 16;phy_reg = bus->read(bus, addr, MII_PHYSID2); //获取PHYS ID1命令if (phy_reg < 0)return -EIO;*phy_id |= (phy_reg & 0xffff);return 0;
}
EXPORT_SYMBOL(get_phy_id);

创建PHY设备

static struct phy_device* phy_device_create(struct mii_bus *bus,int addr, int phy_id)
{struct phy_device *dev;dev = kzalloc(sizeof(*dev), GFP_KERNEL);   //分配phy设备内存if (NULL == dev)return (struct phy_device*) PTR_ERR((void*)-ENOMEM);dev->dev.release = phy_device_release;dev->speed = 0;  //速度dev->duplex = -1;   //双工模式dev->pause = dev->asym_pause = 0;dev->link = 1;   dev->interface = PHY_INTERFACE_MODE_GMII;   //接口模式GMIIdev->autoneg = AUTONEG_ENABLE;    //自动使能dev->addr = addr; //地址dev->phy_id = phy_id; //PHY IDdev->bus = bus;   //mii总线dev->dev.parent = bus->parent;    //父设备dev->dev.bus = &mdio_bus_type; //总线类型dev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL;   //中断/轮询dev_set_name(&dev->dev, PHY_ID_FMT, bus->id, addr);    //PHY 设备文件名dev->state = PHY_DOWN;   //状态DOWNmutex_init(&dev->lock);INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);    //初始化PHY状态机request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id));return dev;
}

2注册phy驱动

int phy_driver_register(struct phy_driver *new_driver)
{int retval;new_driver->driver.name = new_driver->name;  //驱动名new_driver->driver.bus = &mdio_bus_type;   //总线类型new_driver->driver.probe = phy_probe; //探测函数new_driver->driver.remove = phy_remove;   //移除函数retval = driver_register(&new_driver->driver);    //注册设备驱动if (retval) {printk(KERN_ERR "%s: Error %d in registering driver\n",new_driver->name, retval);return retval;}pr_debug("%s: Registered new driver\n", new_driver->name);return 0;
}
EXPORT_SYMBOL(phy_driver_register);

3 通过MDIO总线匹配

phy设备和驱动的总线类型都是mdio_bus_type

struct bus_type mdio_bus_type = {.name      = "mdio_bus",.match      = mdio_bus_match,  //匹配方法.pm       = MDIO_BUS_PM_OPS,
};
EXPORT_SYMBOL(mdio_bus_type);static int mdio_bus_match(struct device *dev, struct device_driver *drv)
{struct phy_device *phydev = to_phy_device(dev);   //获取PHY设备struct phy_driver *phydrv = to_phy_driver(drv);   //获取PHY驱动return ((phydrv->phy_id & phydrv->phy_id_mask) ==(phydev->phy_id & phydrv->phy_id_mask));    //比较phy_id
}

匹配成功就会调用phy驱动的probe方法,也即是phy_probe

static int phy_probe(struct device *dev)
{struct phy_device *phydev;struct phy_driver *phydrv;struct device_driver *drv;int err = 0;phydev = to_phy_device(dev);   //获取PHY设备drv = get_driver(phydev->dev.driver);phydrv = to_phy_driver(drv); //获取PHY驱动phydev->drv = phydrv;  //捆绑一下if (!(phydrv->flags & PHY_HAS_INTERRUPT))  //设置中断方式phydev->irq = PHY_POLL;mutex_lock(&phydev->lock);phydev->supported = phydrv->features;    //设置PHY设备特性phydev->advertising = phydrv->features;   //设置PHY设备特性phydev->state = PHY_READY;   //状态设置为"准备好"if (phydev->drv->probe) //如果驱动有probe方法err = phydev->drv->probe(phydev);  //则调用mutex_unlock(&phydev->lock);return err;
}

三 初始化流程

主函数主要初始化mdio总线、注册通用的phy设备驱动

static int __init phy_init(void)
{int rc;rc = mdio_bus_init();  //初始化mdio总线if (rc)return rc;rc = phy_driver_register(&genphy_driver);  //注册通用的PHY设备驱动if (rc)mdio_bus_exit();return rc;
}static struct phy_driver genphy_driver = {.phy_id = 0xffffffff,.phy_id_mask = 0xffffffff,.name  = "Generic PHY",.config_init = genphy_config_init, //初始化函数.features = 0,.config_aneg = genphy_config_aneg, //配置自协商(Auto-negotiation).read_status = genphy_read_status, //读状态.suspend = genphy_suspend,.resume   = genphy_resume,.driver    = {.owner= THIS_MODULE, },
};

genphy_driver驱动中有初始化配置方法,配置速度、半双工/全双工、自协商模式等。

static int genphy_config_init(struct phy_device *phydev)
{int val;u32 features;//默认支持特性features = (SUPPORTED_TP | SUPPORTED_MII| SUPPORTED_AUI | SUPPORTED_FIBRE |SUPPORTED_BNC);val = phy_read(phydev, MII_BMSR); //读基础状态if (val < 0)return val;if (val & BMSR_ANEGCAPABLE)    //支持(auto-negotiation)features |= SUPPORTED_Autoneg;if (val & BMSR_100FULL)    //100兆全双工features |= SUPPORTED_100baseT_Full;if (val & BMSR_100HALF)   //100兆半双工features |= SUPPORTED_100baseT_Half;if (val & BMSR_10FULL)    //10兆全双工features |= SUPPORTED_10baseT_Full;if (val & BMSR_10HALF)  //10兆半双工features |= SUPPORTED_10baseT_Half;if (val & BMSR_ESTATEN) {val = phy_read(phydev, MII_ESTATUS);  //读扩展状态if (val < 0)return val;if (val & ESTATUS_1000_TFULL)  //1000兆全双工features |= SUPPORTED_1000baseT_Full;if (val & ESTATUS_1000_THALF)   //1000兆半双工features |= SUPPORTED_1000baseT_Half;}phydev->supported = features;  //PHY特性phydev->advertising = features;return 0;
}

四 PHY状态机

状态类型

enum phy_state {PHY_DOWN=0,PHY_STARTING,    //开始PHY_READY,  //准备好PHY_PENDING,   //挂起PHY_UP,     //开启PHY_AN,     //判断连接状态中 negotiatingPHY_RUNNING,   //运行PHY_NOLINK, //开启 未连接PHY_FORCING,    //设置中PHY_CHANGELINK,    //连接状态改变PHY_HALTED, //停止PHY_RESUMING    //唤醒
};

状态机phy_state_machine

在phy_device_create函数中,开启了状态机

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);int needs_aneg = 0;int err = 0;mutex_lock(&phydev->lock);if (phydev->adjust_state)phydev->adjust_state(phydev->attached_dev);switch(phydev->state) {case PHY_DOWN:       //关闭((ifconfig eth0 down)case PHY_STARTING: //开始case PHY_READY:     //准备好case PHY_PENDING:  //挂起break;case PHY_UP:  //开启(ifconfig eth0 up)needs_aneg = 1;phydev->link_timeout = PHY_AN_TIMEOUT;break;case PHY_AN:  //判断连接状态中 negotiatingerr = phy_read_status(phydev);if (err < 0)break;if (!phydev->link) {phydev->state = PHY_NOLINK;netif_carrier_off(phydev->attached_dev);phydev->adjust_link(phydev->attached_dev);break;}err = phy_aneg_done(phydev);if (err < 0)break;if (err > 0) {phydev->state = PHY_RUNNING;netif_carrier_on(phydev->attached_dev);phydev->adjust_link(phydev->attached_dev);} else if (0 == phydev->link_timeout--) {int idx;needs_aneg = 1;if (phydev->drv->flags & PHY_HAS_MAGICANEG)break;idx = phy_find_valid(0, phydev->supported);phydev->speed = settings[idx].speed;phydev->duplex = settings[idx].duplex;phydev->autoneg = AUTONEG_DISABLE;pr_info("Trying %d/%s\n", phydev->speed,DUPLEX_FULL ==phydev->duplex ?"FULL" : "HALF");}break;case PHY_NOLINK:  //开启 未连接err = phy_read_status(phydev);if (err)break;if (phydev->link) {phydev->state = PHY_RUNNING;netif_carrier_on(phydev->attached_dev);phydev->adjust_link(phydev->attached_dev);}break;case PHY_FORCING:   //设置中err = genphy_update_link(phydev);if (err)break;if (phydev->link) {phydev->state = PHY_RUNNING;netif_carrier_on(phydev->attached_dev);} else {if (0 == phydev->link_timeout--) {phy_force_reduction(phydev);needs_aneg = 1;}}phydev->adjust_link(phydev->attached_dev);break;case PHY_RUNNING:   //运行if (PHY_POLL == phydev->irq)phydev->state = PHY_CHANGELINK;break;case PHY_CHANGELINK:  //连接状态改变err = phy_read_status(phydev);if (err)break;if (phydev->link) {phydev->state = PHY_RUNNING;netif_carrier_on(phydev->attached_dev);} else {phydev->state = PHY_NOLINK;netif_carrier_off(phydev->attached_dev);}phydev->adjust_link(phydev->attached_dev);if (PHY_POLL != phydev->irq)err = phy_config_interrupt(phydev,PHY_INTERRUPT_ENABLED);break;case PHY_HALTED:    //停止if (phydev->link) {phydev->link = 0;netif_carrier_off(phydev->attached_dev);phydev->adjust_link(phydev->attached_dev);}break;case PHY_RESUMING: //唤醒err = phy_clear_interrupt(phydev);if (err)break;err = phy_config_interrupt(phydev,PHY_INTERRUPT_ENABLED);if (err)break;if (AUTONEG_ENABLE == phydev->autoneg) {err = phy_aneg_done(phydev);if (err < 0)break;if (err > 0) {err = phy_read_status(phydev);if (err)break;if (phydev->link) {phydev->state = PHY_RUNNING;netif_carrier_on(phydev->attached_dev);} elsephydev->state = PHY_NOLINK;phydev->adjust_link(phydev->attached_dev);} else {phydev->state = PHY_AN;phydev->link_timeout = PHY_AN_TIMEOUT;}}else {err = phy_read_status(phydev);if (err)break;if (phydev->link) {phydev->state = PHY_RUNNING;netif_carrier_on(phydev->attached_dev);} elsephydev->state = PHY_NOLINK;phydev->adjust_link(phydev->attached_dev);}break;}mutex_unlock(&phydev->lock);if (needs_aneg)    //需要自动配置(例如ifconfig eth0 up就会调用)err = phy_start_aneg(phydev);  //开始自动配置if (err < 0)phy_error(phydev);schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ);
}

运行ifconfig eth0 up

case PHY_UP: //开启(ifconfig eth0 up)needs_aneg = 1;phydev->link_timeout = PHY_AN_TIMEOUT;break;

相应处理

if (needs_aneg)  //需要自动协商机制(例如ifconfig eth0 up就会调用)err = phy_start_aneg(phydev);    //开始自动配置

调用phy_start_aneg自动配置函数

int phy_start_aneg(struct phy_device *phydev)
{int err;mutex_lock(&phydev->lock);if (AUTONEG_DISABLE == phydev->autoneg)phy_sanitize_settings(phydev);err = phydev->drv->config_aneg(phydev);  //调用驱动的config_aneg方法,默认是genphy_config_anegif (err < 0)goto out_unlock;if (phydev->state != PHY_HALTED) { //调整修改PHY设备状态if (AUTONEG_ENABLE == phydev->autoneg) {phydev->state = PHY_AN;phydev->link_timeout = PHY_AN_TIMEOUT;} else {phydev->state = PHY_FORCING;phydev->link_timeout = PHY_FORCE_TIMEOUT;}}
out_unlock:mutex_unlock(&phydev->lock);return err;
}
EXPORT_SYMBOL(phy_start_aneg);

调用默认的自动协商方法genphy_config_aneg

int genphy_config_aneg(struct phy_device *phydev)
{int result;if (AUTONEG_ENABLE != phydev->autoneg)return genphy_setup_forced(phydev);result = genphy_config_advert(phydev);if (result < 0) /* error */return result;if (result == 0) {int ctl = phy_read(phydev, MII_BMCR);  //获取状态if (ctl < 0)return ctl;if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))result = 1; /* do restart aneg */}if (result > 0)result = genphy_restart_aneg(phydev);  //重新开启自动协商机制return result;
}
EXPORT_SYMBOL(genphy_config_aneg);

接着调用genphy_restart_aneg,重新开启自动协商机制

int genphy_restart_aneg(struct phy_device *phydev)
{int ctl;ctl = phy_read(phydev, MII_BMCR); //获取基本状态if (ctl < 0)return ctl;ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); //使能自动协商机制使能 重启/* Don't isolate the PHY if we're negotiating */ctl &= ~(BMCR_ISOLATE);ctl = phy_write(phydev, MII_BMCR, ctl);   //写命令return ctl;
}
EXPORT_SYMBOL(genphy_restart_aneg);

其他常用的API

static inline int phy_read(struct phy_device *phydev, u32 regnum);   //PHY读
static inline int phy_write(struct phy_device *phydev, u32 regnum, u16 val);    //PHY写
void phy_start(struct phy_device *phydev);  //PHY开始
void phy_stop(struct phy_device *phydev);   //PHY停止
int phy_init_hw(struct phy_device *phydev); //PHY初始化硬件
struct phy_device * phy_attach(struct net_device *dev,const char *bus_id, u32 flags, phy_interface_t interface);    //PHY接上
void phy_detach(struct phy_device *phydev); //PHY分离
struct phy_device *phy_find_first(struct mii_bus *bus); //查找mii_bus总线上第一个PHY
int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,void (*handler)(struct net_device *), u32 flags,phy_interface_t interface);    //PHY直接连接网络设备
struct phy_device * phy_connect(struct net_device *dev, const char *bus_id,void (*handler)(struct net_device *), u32 flags,phy_interface_t interface);  //PHY连接网络设备
void phy_disconnect(struct phy_device *phydev); //PHY断开与网络设备的连接
int phy_start_interrupts(struct phy_device *phydev);//PHY开始中断
int phy_stop_interrupts(struct phy_device *phydev); //PHY停止中断
int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd);   //ethtool工具sset功能
int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd);   //ethtool工具gset功能
int phy_mii_ioctl(struct phy_device *phydev,struct ifreq *ifr, int cmd);    //通用PHY/mii接口

网口调试基础之一网口phy驱动相关推荐

  1. Linux phy驱动开发总结

    文章目录 基础 须知 数据结构 数据结构UML类图 MAC驱动 初始化简述 驱动实现 关于MAC连接PHY PHY驱动 动态注册 静态注册 通用PHY 参考模板 IP18xx驱动调试 须知: 问题 方 ...

  2. 网口调试方式以及性能测试iperf |CSDN创作打卡

    文章目录 调试步骤 网口phy/mac调试方法 PHY 寄存器读写调试 查看PHY是否识别 网口性能测试iperf 调试步骤 阅读对应PHY的技术手册,弄清楚MDIO使用条款 测试PHY的外部供电是否 ...

  3. 转载:ZYNQ+linux网口调试笔记(1)PS-GEM0

    ZYNQ+linux网口调试笔记(1)PS-GEM0 转载原文:https://www.jianshu.com/p/a4e25e8b2f5e 开发环境 Windows SDK 2017.4 Ubunt ...

  4. zynq linux ip配置,ZYNQ+linux网口调试笔记(2)PS-GEM1

    1. 开发环境 Windows SDK 2017.4 Ubuntu Petalinux 2017.4 硬件平台:米联客ZYNQ开发板MIZ7035 2. 开发目标 在ZYNQ上使用gigE Visio ...

  5. zynq linux如何使用pl ip,ZYNQ+linux网口调试笔记(3)PL-ETH

    1. 开发环境 Windows SDK 2017.4 Ubuntu Petalinux 2017.4 硬件平台:米联客ZYNQ开发板MIZ7035 2. 开发目标 在ZYNQ上使用gigE Visio ...

  6. 网口扫盲二:Mac与Phy组成原理的简单分析

    1. general 下图是网口结构简图.网口由CPU.MAC和PHY三部分组成.DMA控制器通常属于CPU的一部分,用虚线放在这里是为了表示DMA控制器可能会参与到网口数据传输中. MAC(Medi ...

  7. 使用SSCOM V5.13进行串口网口调试

    一.网口调试: 网口无需连接,即可收发数据,客户端软件正常运行,客户端电脑无需运行SSCOM V5.13.只有客户端软件运行之后端口才能正常工作. 二.串口调试:

  8. 有人物联网485转网口模块网口调试助手1035未知错误

    有人物联网485转网口模块网口调试助手1035未知错误 问题 解决 问题 项目使用有人物联网485转网口模块USR-TCP232-304,将模块接入实验室路由器,IP地址设置为动态IP,路由器上查得I ...

  9. 网络驱动->PHY驱动调试

    1. Linux 系统网络协议层架构 网络协议框架图: 网络子系统是 linux 操作系统里很重要的一部分.关于这部分有很多的参考资料.这里主要说明一下 phy 芯片在整个子系统里的位置.从这个结构里 ...

最新文章

  1. 如何建立图像数据矩阵和图像显示灰度之间的关系!_放射技术考试第四章第一节 数字图像的特征...
  2. java 中while编译之后_从APK反编译的Java-while循环什么也不做
  3. wireshark 常用命令
  4. 【python数据挖掘课程】十二.Pandas、Matplotlib结合SQL语句对比图分析
  5. RabbitMQ死信队列代码架构图
  6. 【今晚七点半】:白板与开源
  7. 反转二叉树 java_leetcode刷题笔记-226. 翻转二叉树(java实现)
  8. python足球投注_/usr/lib目录属性更改引发的蝴蝶效应
  9. mysql怎么让自增id不连续_MySQL中自增主键不连续之解决方案。(20131109)
  10. XINS 3.1.0 Alpha2 发布,远程 API 调用规范
  11. P5057 [CQOI2006]简单题
  12. mybatis开发中遇到的小问题
  13. 针对英特尔xtu超频软件安装失败以及英伟达GeForce Experience安装程序无法继续的解决方法
  14. 计算机专用英语词汇1695个词汇表(传)
  15. 由三点画圆到未来日记:失控中的位置隐私
  16. 如何下载网页中的360全景图片(720全景图片)到本地?
  17. STM32H743内部所有SRAM的使用
  18. 【仙女踩坑实录】Macbook修改文件创建时间
  19. (二)UPF之电压域、低功耗模式编码(Primary Supply Set、Power State)
  20. Deep Learning for Single Image Super-Resolution: A Brief Review SISR综述分析

热门文章

  1. 学习python,北京尚学堂,第31课到第60课的个人的总结
  2. XNA游戏开发之2D游戏
  3. 字符串格式的格林威治时间转换为Date类型
  4. 可视对讲终端 平安城市智慧灯杆一键可视对讲
  5. shopee店铺数据分析(二)
  6. 【人体姿态】Stacked Hourglass算法详解
  7. 基姆拉尔森计算公式(快速推导当前日期是周几)
  8. mysql大数据迁移,备份
  9. xm-select 二级联动 layui
  10. python+selenium:移除时间控件readonly属性,实现send_keys输入