一.结构体

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\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); //获取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\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.匹配

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\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);
}
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打印状态

linux网络设备—PHY的更多相关文章

  1. linux网络设备驱动程序

    4.linux网络设备驱动程序体系结构 -------------------------------------- | 数据包发送 | 数据包接收 | ----->网络协议接口层 | dev_ ...

  2. Linux网络设备驱动架構學習(三)

    Linux网络设备驱动架構學習(三) 接下來會從以下幾個方面介紹網絡設備驅動的編寫流程: 1.網絡設備的註冊與註銷 2.網絡設備的初始化 3.網絡設備的打開與釋放 4.網絡數據發送流程 5.網絡數據接 ...

  3. Linux网络设备驱动架构

    Linux网络设备驱动程序体系结构分为四层:网络协议接口层.网络设备接口层.提供实际功能的设备驱动层以及网络设备与媒介层. (1)网络协议接口层向网络层协议提供统一的数据包收发接口,不论上层协议是AR ...

  4. linux网络设备驱动

    Linux网络设备驱动 Linux网络驱动程序的体系结构可划分为4个层次.Linux内核源代码中提供了网络设备接口及以网络子系统的上层的代码,移植特定网络硬件的驱动程序的主要工作就是完成设备驱动功能层 ...

  5. Linux网络设备驱动架構學習(二)

    Linux网络设备驱动架構學習(二) 接下來會從以下幾個方面介紹網絡設備驅動的編寫流程: 1.網絡設備的註冊與註銷 2.網絡設備的初始化 3.網絡設備的打開與釋放 4.網絡數據發送流程 5.網絡數據接 ...

  6. linux网络设备理解

    网络层次 linux网络设备驱动与字符设备和块设备有很大的不同. 1. 字符设备和块设备对应/dev下的一个设备文件.而网络设备不存在这样的设备文件.网络设备使用套接字socket访问,虽然也使用re ...

  7. 《linux设备驱动开发详解》笔记——14 linux网络设备驱动

    14.1 网络设备驱动结构 网络协议接口层:硬件无关,标准收发函数dev_queue_xmit()和netif_rx();  注意,netif_rx是将接收到的数据给上层,有时也在驱动收到数据以后调用 ...

  8. Linux网络设备驱动(一) _驱动模型

    Linux素来以其强大的网络功能著名,同时, 网络设备也作为三大设备之一, 成为Linux驱动学习中必不可少的设备类型, 此外, 由于历史原因, Linux并没有强制对网络设备贯彻其"一切皆 ...

  9. Linux网络设备驱动的实现

    结论: 1.对一个设备驱动而言,主要从两个方面进行着手,一个是控制流,一个是数据流. 控制流就是如何控制网络设备,数据流则说的是报文的收发流程. 2.网络设备可以是Linux服务器的网卡,也可以是嵌入 ...

随机推荐

  1. python接口自动化测试十九:函数

    # 函数 a = [1, 3, 6, 4, 85, 32, 46]print(sum(a)) # sum,求和函数 def add(): a = 1, b = 2, return a + bprint ...

  2. Windows C#入门环境搭建

    Windows C#入门环境搭建 1. 安装Microsoft .NET Framework目录: C:\Windows\Microsoft.NET\Framework,查看已经安装的版本. 如果未安 ...

  3. vtiger7菜单管理

    添加了新模块,但是菜单上却没显示. 和菜单相关的表有4张 我们要把新建的message放到support模块下面 1.把默认的父级目录tools改成support 2. app2tab 0表示不显示, ...

  4. thinkphp注册验证

    在model中新建一个UserModel //覆盖原本的设置 //一次性获得全部验证错误 protected $patchValidate = true; //实现表单项目验证 //通过重写父类属性_ ...

  5. 解决python中遇到的乱码问题

    1. 解决中文乱码的一种可行方法 # -*- coding:utf-8 -*- from __future__ import unicode_literals import chardet def s ...

  6. Codeforces 460D Little Victor and Set(看题解)

    Little Victor and Set 其他都很好求, 只有k == 3的时候很难受.. 我们找到第一个不大于l的 t, 答案为 l, 3 * t, (3 * t) ^ l 感觉好像是对的, 感觉 ...

  7. php中$this->是什么意思

    $this 的含义是表示    实例化后的 具体对象! 我们一般是先声明一个类,然后用这个类去实例化对象! 但是,当我们在声明这个类的时候,想在类本身内部使用本类的属性或者方法.应该怎么表示呢? 例如 ...

  8. HDU 1542 矩形面积并【离散化+线段树+扫描线】

    <题目链接> 题目大意: 给你n个矩形,求出它们面积的并. 解题分析: 此题主要用到了扫描线的思想,现将各个矩形的横坐标离散化,然后用它们离散化后的横坐标(相当于将矩形的每条竖线投影在x轴 ...

  9. 洛谷.3391.文艺平衡树(fhq Traep)

    题目链接 //注意反转时先分裂r,因为l,r是针对整棵树的排名 #include<cstdio> #include<cctype> #include<algorithm& ...

  10. tab------左右布局

    <!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title> ...