在RT-Thread下为MPU手搓以太网MAC驱动-3

文章目录

这是个人驱动开发过程中做的一些记录,仅代表个人意见和理解,不喜勿喷

  • MAC驱动需要支持不同的PHY芯片

MAC驱动支持不同的PHY芯片

关于对PHY设备抽象的改进

系列文章2中有提及到,在RT-Thread下定义的PHY操作抽象接口并不是很合理,比如你的系统里面有2个PHY的时候,你需要对每个PHY的操作接口和具体的PHY设备实例进行深度绑定,否则你无法根据当前read操作所传入的参数来区分当前操作的是哪个PHY设备:

先让我们来看下按照现有RT-Thread PHY抽象接口的定义,现在需要实现一个获取当前link状态的操作接口的实现,抽象接口的定义和具体实现如下:

rt_phy_status (*get_link_status)(rt_bool_t *status);

c 复制代码
static rt_phy_status h3_kszplib_read(rt_uint32_t reg, rt_uint32_t *data)
{
    rt_mdio_t *mdio_bus   = h3_kszdev0.rt_phydev.bus;
    rt_uint32_t device_id = h3_kszdev0.rt_phydev.addr;

    if (4 != mdio_bus->ops->read(mdio_bus, device_id,
                                 reg, (void *)data, 4))
    {
        return RT_FALSE;
    }

    return PHY_STATUS_OK;
}
c 复制代码
static rt_phy_status h3_kszplib_linkstatus(rt_bool_t *status)
{
    rt_phy_status result;
    rt_uint32_t data;

    /* Read the basic status register. */
    result = h3_kszplib_read(MII_MSR, &data);
    RESULT_MATCH_CHECK(result, PHY_STATUS_FAIL, outs)

    if (!(MII_MSR_LINKSTATUS & data))
    {
        *status = RT_FALSE; /* link down. */
    }
    else
    {
        *status = RT_TRUE; /* link up. */
    }

outs:
    return result;
}

那对于h3_kszplib_read()接口函数来说,它需要知道自己是属于哪个PHY设备的实例,然后去获取该PHY设备实例下所拥有的MIDO设备和MDIO设备操作接口,也就是说它这个接口的实现和PHY设备实例进行了深度绑定。而我们实际设计中,对于h3_kszplib_read接口函数来说它没有必要去知道自己属于哪个PHY设备实例,它只需要获取到该PHY设备实例所提供的MDIO操作接口信息即可,因为原有的get_link_status()这个抽象接口无法提供PHY设备实例的信息,也就是说它也是和具体PHY设备实例进行了深度绑定,现在我们将对其深度绑定进行解藕操作。

RT-Thread下PHY设备抽象接口的改进

c 复制代码
struct rt_phy_ops
{
    rt_phy_status (*init)(struct rt_phy_device *phy, void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz);
    rt_phy_status (*read)(struct rt_phy_device *phy, rt_uint32_t reg, rt_uint32_t *data);
    rt_phy_status (*write)(struct rt_phy_device *phy, rt_uint32_t reg, rt_uint32_t data);
    rt_phy_status (*loopback)(struct rt_phy_device *phy, rt_uint32_t mode, rt_uint32_t speed, rt_bool_t enable);
    rt_phy_status (*get_link_status)(struct rt_phy_device *phy, rt_bool_t *status);
    rt_phy_status (*get_link_speed_duplex)(struct rt_phy_device *phy, rt_uint32_t *speed, rt_uint32_t *duplex);
};

在RT-Thread下定义的PHY操作抽象接口中,传入struct rt_phy_device *phy这个参数,后续的驱动代码可以根据传入的参数,来进行进一步的操作,将MDIO具体读写操作的请求独立出来,只需要从PHY设备的实例中获取到MDIO总线和PHY的物理地址,调用MDIO总线实例提供的读写操作接口即可。

c 复制代码
static rt_phy_status h3_kszplib_read(struct rt_phy_device *phy,
                                     rt_uint32_t reg, rt_uint32_t *data)
{
    rt_mdio_t  *mdio_bus = phy->bus;
    rt_uint32_t phy_addr = phy->addr;

    if (4 != mdio_bus->ops->read(mdio_bus, phy_addr, reg, (void *)data, 4))
    {
        return RT_FALSE;
    }

    return PHY_STATUS_OK;
}
c 复制代码
static rt_phy_status h3_kszplib_linkstatus(struct rt_phy_device *phy, rt_bool_t *status)
{
    rt_phy_status result;
    rt_uint32_t data;

	RT_ASSERT(phy != RT_NULL);

    /* Read the basic status register. */
    result = h3_kszplib_read(phy, MII_MSR, &data);
    RESULT_MATCH_CHECK(result, PHY_STATUS_FAIL, outs)

    if (!(MII_MSR_LINKSTATUS & data))
    {
        *status = RT_FALSE; /* link down. */
    }
    else
    {
        *status = RT_TRUE; /* link up. */
    }

outs:
    return result;
}

用户驱动可以通过以下方法去获取在自己驱动中定义的每个PHY设备的实例,进而获取更多驱动所需要的信息,来更好地实现PHY驱动的兼容性。

c 复制代码
	struct h3_kszplib_dev *kszplib_dev;
    RT_ASSERT(phy != RT_NULL);

    kszplib_dev = rt_container_of(phy, struct h3_kszplib_dev, rt_phydev);

关于对PHY设备抽象的改进

通过上述的修改后,可以通过BSP_USING_PHY0这样的宏定义(在Kconfig中进行定义和选择),来实现驱动中对多个PHY的驱动支持和灵活配置。

c 复制代码
#ifdef BSP_USING_PHY0
static struct rt_phy_ops h3_ksz0plib_ops =
{
    .init                  = H3_KSZPLIB_PHY0INIT,
    .read                  = h3_kszplib_read,
    .write                 = h3_kszplib_write,
    .loopback              = h3_kszplib_loopback,
    .get_link_status       = h3_kszplib_linkstatus,
    .get_link_speed_duplex = H3_KSZPLIB_PHY0LINKS,
};

static struct h3_kszplib_dev h3_kszdev0 = 
{
    .name      = PHY0_DEVICE_NAME,
    .phy_addr  = PHY0_DEVICE_ADDRESS,
    .rt_phydev =
    {
        .ops   = &h3_ksz0plib_ops,
    }
};
#endif // BSP_USING_PHY0

同样在PHY设备注册代码中,也可以采取非常灵活的方式,来实现对多个PHY设备的注册操作

c 复制代码
int h3_kszplib_init(void)
{
    rt_uint32_t table_sz = sizeof(h3_kszplib_devtable)/sizeof(uint32_t);
    struct h3_kszplib_dev *kszplib_dev;

    for (uint32_t i = 1; i < table_sz; i++)
    {
        kszplib_dev = h3_kszplib_devtable[i];
        rt_hw_phy_register(&kszplib_dev->rt_phydev, kszplib_dev->name);
    }

    return RT_EOK;
}
相关推荐
大聪明-PLUS6 小时前
管理 Linux 内核模块
linux·嵌入式·arm·smarc
DIY机器人工房8 小时前
简单理解:M483SIDAE这款 MCU(微控制器)的核心规格参数
单片机·嵌入式硬件·嵌入式·diy机器人工房·m483sidae
大聪明-PLUS9 小时前
了解 Docker:镜像是如何创建的
linux·嵌入式·arm·smarc
乔碧萝成都分萝11 小时前
十八、使用class分类管理设备
linux·驱动开发·嵌入式
DIY机器人工房12 小时前
简单理解:珠海航宇微科技(航宇微)、芯探索、XM1002他们之间的关系
科技·单片机·嵌入式·diy机器人工房·芯探索·xm1002·航宇微
DIY机器人工房13 小时前
解决方法:用新唐 NuMicro M483这款单片机遇到的一些问题
单片机·嵌入式硬件·嵌入式·diy机器人工房·新唐m483
星源~13 小时前
TensorFlow 开发环境搭建指南:Anaconda 与 Miniconda 抉择及环境搭建步骤
人工智能·python·tensorflow·嵌入式·mcu+ai
charlie1145141911 天前
嵌入式现代C++教程:C++98——从C向C++的演化(3)
c语言·开发语言·c++·笔记·学习·嵌入式
云卓SKYDROID1 天前
无人机舵机驱动模块技术解析
无人机·驱动·知识科普·高科技·云卓科技
无聊到发博客的菜鸟1 天前
STM32 手册寄存器属性
stm32·单片机·嵌入式·rtos·寄存器