zoukankan      html  css  js  c++  java
  • linux网络设备—PHY

    一.结构体

    1.PHY设备

    struct phy_device {
    	struct phy_driver *drv;	//PHY设备驱动
    	struct mii_bus *bus;	//对应的MII总线
    	struct device dev;	//设备文件
    	u32 phy_id;	//PHY ID
    	enum 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;	//026
    	int 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);
    };

    2.PHY驱动

    struct phy_driver {
    	u32 phy_id;		//PHY ID
    	char *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);	//执行匹配的fixups
    	err = device_register(&phydev->dev);	//注册设备
    	if (err) {
    		pr_err("phy %d failed to register
    ", 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);	//获取PHY ID
    	if (r)
    		return ERR_PTR(r);
    	if ((phy_id & 0x1fffffff) == 0x1fffffff)
    		return NULL;
    	dev = phy_device_create(bus, addr, phy_id);	//创建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 ID
    	phy_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;	//接口模式GMII
    	dev->autoneg = AUTONEG_ENABLE;	//自动使能
    	dev->addr = addr; //地址
    	dev->phy_id = phy_id; //PHY ID
    	dev->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;	//状态DOWN
    	mutex_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
    ",new_driver->name, retval);
    		return retval;
    	}
    	pr_debug("%s: Registered new driver
    ", new_driver->name);
    	return 0;
    }
    EXPORT_SYMBOL(phy_driver_register);

    3.匹配

    PHY设备和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);

    匹配函数mdio_bus_match

    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;
    }

    三.初始化过程

    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;
    }

    初始化过程主要是初始化mdio总线

    接着注册通用的PHY设备驱动

    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, },
    };

    初始化配置方法

    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状态机

    1.状态分类

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

    2.状态机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:	//判断连接状态中 negotiating
    			err = 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
    ", 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);
    					} 
    					else
    						phydev->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);
    				} 
    				else
    					phydev->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);
    }

    3.运行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_aneg
    	if (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_config_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接口
    void phy_print_status(struct phy_device *phydev);	//PHY打印状态


     

  • 相关阅读:
    获取文件当前目录及其大小
    PLC工作原理动图,一图搞懂一个原理
    欧拉角的详解
    欧拉角的详解
    PLC/Pragmas
    ASCII码对照表
    C++ 的关键字(保留字)完整介绍
    C++ 基本语法
    pytorch笔记1
    pytorchnum_flat_features(x)
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3424161.html
Copyright © 2011-2022 走看看