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

文章目录

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

  • MAC驱动需要兼容不同的MPU平台

MAC驱动兼容不同的MPU平台

MAC驱动中断处理代码

在MAC驱动下,提供了通用的中断处理代码,在通用中断处理代码下会调用每个MAC驱动实际注册的中断处理函数:

c 复制代码
static void h3_macplib_interrupt(int vector, void *param)
{
    struct h3_macplib_dev *macplib_dev;

    RT_ASSERT(param != RT_NULL);

    macplib_dev = (struct h3_macplib_dev *)param;
    RT_ASSERT((int)macplib_dev->irqnum == vector);

    macplib_dev->mac_ops->macdev_interrupt(&macplib_dev->mac_dev);
}

MAC驱动在向RT-Thread注册network device时,所提供的初始化接口的实现:

c 复制代码
static rt_err_t h3_macplib_initial(rt_device_t dev)
{
    struct mac_async_filter filter = {0};
    struct h3_macplib_dev *macplib_dev;

    macplib_dev = (struct h3_macplib_dev *)dev->user_data;
    RT_ASSERT(macplib_dev != RT_NULL);

    /* Disable GMAC interrupt and register IRQ handler */
    rt_hw_interrupt_mask(macplib_dev->irqnum);
    rt_hw_interrupt_install(macplib_dev->irqnum, h3_macplib_interrupt,
                            (void *)macplib_dev, macplib_dev->name);

    /* Device is ready to work after final initialization */
    macplib_dev->mac_ops->macdev_init(&macplib_dev->mac_dev,
                                      (void *)macplib_dev->regs);

    /* Register frame RX callback function to notify Ethernet driver */
    macplib_dev->mac_ops->macdev_register(&macplib_dev->mac_dev,
                                          MAC_ASYNC_RECEIVE_CB,
                                          h3_macplib_rxcallback);

    /* set MAC hardware address */
    memcpy(filter.mac, macplib_dev->mac_addr, 6);
    filter.tid_enable = false;
    macplib_dev->mac_ops->macdev_filter(&macplib_dev->mac_dev, 0, &filter);

    /* Enable GMAC device */
    macplib_dev->mac_ops->macdev_enable(&macplib_dev->mac_dev);

    /* The last should enable GMAC interrupt handler */
    rt_hw_interrupt_umask(macplib_dev->irqnum);

    return RT_EOK;
}

MAC驱动下MDIO访问接口的实现

在定义MAC驱动操作接口的抽象时,就有定义PHY的寄存器读写接口,而MDIO操作PHY寄存器时就需要用到MAC操作接口所提供的PHY读写接口:

c 复制代码
/* RT-Thread MDIO bus operation */
static struct rt_mdio_bus_ops h3_mdiobus_ops =
{
    .init   = h3_mdioplib_init,
    .read   = h3_mdioplib_read,
    .write  = h3_mdioplib_write,
    .uninit = RT_NULL,
};

MDIO操作接口读取接口的代码实现:

c 复制代码
static rt_size_t h3_mdioplib_read(void *bus, rt_uint32_t addr,
                                  rt_uint32_t reg, void *data, rt_uint32_t size)
{
    rt_uint16_t val;
    rt_uint32_t *data_ptr = (rt_uint32_t *)data;
    struct h3_macplib_dev *macplib_dev;
    struct rt_mdio_bus    *mdioplib_bus = (struct rt_mdio_bus *)bus;

    RT_ASSERT(data != NULL);
    RT_ASSERT(bus  != NULL);

    if (4 != size) {
        return 0;
    }

    macplib_dev = (struct h3_macplib_dev *)mdioplib_bus->hw_obj;
    macplib_dev->mac_ops->macdev_readphy(&macplib_dev->mac_dev,
                                         (rt_uint16_t)addr, (rt_uint16_t)reg,
                                         &val);

    /* Get data from MII register. */
    *data_ptr = (rt_uint32_t)val;

    return 4;
}

那到这里的话,整个MAC驱动中最重要的部分已经完成,接下来将介绍PHY驱动代码的实现。

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

MAC驱动支持不同的PHY芯片

对PHY设备的抽象

需要对PHY设备做出抽象,不同的MPU产品中会存在MAC接口外接不同的PHY芯片,那在我们系统中每个PHY芯片就会有对应数量的PHY设备实例:

c 复制代码
struct h3_kszplib_dev
{
    const char    *name;
    uint32_t       phy_addr;
    struct rt_phy_device rt_phydev;
} ;

RT-Thread下对PHY设备的操作接口做出抽象,在编写MAC驱动的时候,也需要完成对PHY设备操作接口的实现:

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

RT-Thread下定义的PHY操作抽象接口并不是很合理,比如你的系统里面有2个PHY的时候,你需要对每个PHY的操作接口做独立的实现,否则你无法根据当前read操作所传入的参数来区分当前操作的是哪个PHY设备。

如果你对上述说法不是很理解,参考下前面的h3_macplib_initial()函数,可以通过函数传入的参数dev,去获取到当前ethernet device对应的具体以太网设备实例包含的私有信息,根据这个信息用户驱动可以访问到自己定义的数据结构、操作接口等。

相关推荐
winddevil17 小时前
[rCore学习笔记 029] 动态内存分配器实现-以buddy_system_allocator源码为例
rust·嵌入式·rcore
卑微求AC1 天前
(C语言贪吃蛇)14.用绝对值方式解决不合理的走位
linux·c语言·开发语言·嵌入式·c语言贪吃蛇
卑微求AC1 天前
(C语言贪吃蛇)13.实现贪吃蛇四方向的移动
linux·c语言·嵌入式·c语言贪吃蛇
玄奕子2 天前
GPT对话知识库——bootloader是什么?ymodel协议是什么?
stm32·gpt·嵌入式·传输协议·嵌入式驱动
FreakStudio3 天前
全网最适合入门的面向对象编程教程:55 Python字符串与序列化-字节序列类型和可变字节字符串
python·单片机·嵌入式·面向对象·电子diy
我想学LINUX3 天前
一文带你掌握 tmux -- 高效的终端复用工具
linux·嵌入式硬件·嵌入式·策略模式·tmux·tmux命令
极客小张4 天前
基于STM32和FPGA的射频数据采集系统设计流程
c语言·stm32·物联网·算法·fpga开发·毕业设计·嵌入式
winddevil4 天前
[rCore学习笔记 028] Rust 中的动态内存分配
rust·嵌入式·rcore
武汉唯众智创5 天前
嵌入式边缘计算软硬件开发“1+X”考证建设方案
嵌入式·边缘计算软硬件开发·嵌入式“1+x”
玄奕子6 天前
GPT对话知识库——编写IIC驱动的过程
stm32·单片机·gpt·嵌入式·iic