一:交换机的原理机制
信号转发的网络设备,介入交换机的任意两个节点共享信号通路,工作与OSI的数据链路层,同事可以进行多个端口的数据传输,交换机上电后会自动创建一个端口地址表,叫做MAC地址表,,会记录mac地址和哪个端口连接,然后自动学习,每次进到交换机的信息,都会记录下穿送过来的设备地址的mac地址,过程就是,学习mac地址,广播mac地址,查找mac地址,配对mac地址。
二:switch交换芯片的基本概念介绍:
port:就是r45接口,vlan:以port为基础定义vlan组,这种vlan用来隔离不同的网络。通过区分802.1Q标签所带的VLAN ID值不同来划分到不同的VLAN组, 一般这种VLAN会与QoS结合起来应用。qos:prot qos,为不同的port定义不同的优先级,differentqos,用IP TOS定义优先级,802.1P 在802.1P标签里定义不同的优先级,可以和802.1Q VLAN结合起来应用。,MACqos,可以根据特定的mac和ip地址定义不同的优先级。
IEEE802.3数据格式:目标地址(6),原地址(6),长度(2),数据(46~1500),fcs(4)
1:接口介绍:
SMI接口:串行管理接口,也被称作MII管理接口,包括MDC和MDIO两条信号线,MDIO用来读写PHY的状态,MDC用来提供时钟,MII用于连接MAC和PHY,包含两种信号,一个接口用于MAC和phy之间的以太网数据,一个用于PHY的管理,读写phy的寄存器大道控制phy的状态,MDIO是双向的,一个mac最多连接32个phy,mac作为主,phy作为从,写phy寄存器时,由mac驱动MDIO向phy写入数据,读phy寄存器时,前半段由mac驱动发送寄存器地址,phy驱动回复寄存器的值。MDC由MAC输出,是非周期性的,不要求提供固定时钟,phy芯片用于输入,上升沿出发MDIO的读写,MDC的时钟频率可以是DC-2.5MHz,即最小的时钟周期为400ns。
MDIO数据格式定义在IEEE 802.3以太网的标准中
Preamble+Start:32bits的前导码以及2bit的开始位。
OP Code:2bits的操作码,10表示读,01表示写。
PHYAD:5bits的PHY地址,一般PHY地址从0开始顺序编号,例如6口switch中PHY地址为0-5。
REGAD:5bits的寄存器地址,即要读或写的寄存器。
Turn Around:2bits的TA,在读命令中,MDIO在此时由MAC驱动改为PHY驱动,并等待一个时钟周期准备发送数据。在写命令中,不需要MDIO方向发生变化,则只是等待两个时钟周期准备写入数据。
Data:16bits数据,在读命令中,PHY芯片将读到的对应PHYAD的REGAD寄存器的数据写到Data中,在写命令中,MAC将要写入对应PHYAD的REGAD寄存器的值写入Data中。
Idle:空闲状态,此时MDIO无源驱动,处高阻状态,但一般用上拉电阻使其处在高电平。
为了确保PHY能准确采样,当MAC向mdio写数据的时候,需要在MDC的上升沿之前就把数据写到MDIO上,
接下来是phy寄存器的介绍:
mii接口包括四个状态,mac与物理层的发送状态,物理层到mac层的接收状态,mac的指示状态,物理层和mac层的物理控制传输信息。
三:基于hisi3536的mdio总线的配置
函数位于:drivers/net/ethernet/stmmac/stmmac_mdio.c中,调用在drivers/net/ethernet/stmmac/stmmac_main.c
stmmac_mdio_register(struct net_device *ndev):用于注册mdio总线。
stmmac_mii_bus = mdiobus_alloc();分配一个mdio总线
if (priv->phy_addr != -1)
irqlist[priv->phy_addr] = priv->phy_irq;分配一个IRQ对phy地址。
tnkclk = mdio_clk_init();初始化mdio时钟
stmmac_mii_bus->read = &stmmac_mdio_read;读数据从phy驱动mii寄存器的读取数据
stmmac_mii_bus->write = &stmmac_mdio_write;写寄存器
stmmac_mii_bus->reset = &stmmac_mdio_reset;mdio总线的读写复位
stmmac_mii_bus->priv = ndev;总线的私有数据是总线的设备信息
err = mdiobus_register(stmmac_mii_bus);注册一个mdio总线,注册参数包括操作参数,父设备信息,中断列表
if (!driver_for_each_device (&(stmmacphy_driver.driver), NULL, (void *)priv,stmmac_associate_phy)):搜寻驱动为每个设备,最后将调用stmmac_associate_phy,功能。
stmmac_associate_phy(struct device *dev, void *data):浏览所有的phy,注册和检查是否连接到我们的mac地址,如果是,填充phy配置到我们的本地结构体中。
stmmac_external_phy_reset(i, syscfg_base_ioaddr);复位phy寄存器配置:有两种方式,寄存器复位和gpio配置。
stmmac_syscfg_phy_cfg(priv, SPEED_100, DUPLEX_FULL, 0);对phy的配置以及复位,通过phy的配置对mac寄存器的配置
stmmac_open(struct net_device *dev):在mac设备打开的钩子函数中,stmmac_init_phy(dev);初始化phy驱动,对每个网口调用phy芯片连接。
static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, u16 phydata){
struct net_device *ndev = bus->priv;
struct stmmac_priv *priv = netdev_priv(ndev);
unsigned int mii_address = priv->hw->mii.addr;
unsigned int mii_data = priv->hw->mii.data;
//16位寄存器
u16 value = (((phyaddr << 11) & (0x0000F800)) | ((phyreg << 6) & (0x000007C0)))| MII_WRITE;
value |= MII_BUSY | ((priv->clk_csr & 0xF) << 2);
/* Wait until any existing MII operation is complete */
if (stmmac_mdio_busy_wait(priv->ioaddr, mii_address))
return -EBUSY;
/* Set the MII address register to write */
//写数据到phy的数据配置寄存器
writel(phydata, priv->ioaddr + mii_data);
//写地址到phy的地址配置寄存器
writel(value, priv->ioaddr + mii_address);
/* Wait until any existing MII operation is complete */
return stmmac_mdio_busy_wait(priv->ioaddr, mii_address);
}
小知识点:
desc->des2 = dma_map_single(priv->device, skb->data,nopaged_len, DMA_TO_DEVICE);流式dma 映射,驱动需要从别的模块传经来的地址空间作为dma 的缓冲区,那就考虑流式dma映射,初始纳入的地址空间必须是位于内核块的线性区域,驱动的处理主要确保每次的dma操作前后cache的一致性问题。
一致性dma映射:该缓冲区的存在周期与所在的驱动模块一样长,就用一致性dma映射,这种映射在一开始为dma 操作分区解决了cache一致性的问题。map_page
priv->dma_rx = (struct dma_desc *)dma_alloc_coherent(priv->device, rxsize * sizeof(struct dma_desc),&priv->dma_rx_phy, GFP_KERNEL);
priv->dma_rx内核的虚拟起始地址,内核调用此地址,&priv->dma_rx_phy,返回的内核的物理起始地址。
四:mac接口配置
static const struct stmmac_ops dwmac1000_ops = {
.core_init = dwmac1000_core_init, 初始化,一般用来配置mac中断
.rx_coe = dwmac1000_rx_coe_supported,支持校验分流管理
.dump_regs = dwmac1000_dump_regs, 打印mac寄存器基地址
.host_irq_status = dwmac1000_irq_status, intr_status = readl(ioaddr + GMAC_INT_STATUS);读取中断状态,主要发送接收中断状态
.set_filter = dwmac1000_set_filter,多播缓冲设置
.flow_ctrl = dwmac1000_flow_ctrl, writel(flow, ioaddr + GMAC_FLOW_CTRL);流控制设置
.pmt = dwmac1000_pmt,电压管理模式
.set_umac_addr = dwmac1000_set_umac_addr,设置mac 地址
.get_umac_addr = dwmac1000_get_umac_addr,
};
writel(0x60f, ioaddr + GMAC_INT_MASK);标记mac中断
有一个设备配置结构体struct mac_device_info mac。
mac的连接设置
mac->link.port = GMAC_CONTROL_PS;
mac->link.duplex = GMAC_CONTROL_DM;
mac->link.speed = GMAC_CONTROL_FES;
mac->mii.addr = GMAC_MII_ADDR; //对应的mii的偏移地址
mac->mii.data = GMAC_MII_DATA; //对应的mii的偏移数据
五:基于mac的dma 操作
const struct stmmac_dma_ops dwmac1000_dma_ops = {
.init = dwmac1000_dma_init,dma 初始化,axi总线初始化,dma中断初始化,发送接收源初始化
.dump_regs = dwmac1000_dump_dma_regs,打印寄存器
.dma_mode = dwmac1000_dma_operation_mode,dma模式初始化
.dma_diagnostic_fr = dwmac1000_dma_diagnostic_fr,
.enable_dma_transmission = dwmac_enable_dma_transmission,使能dma发送
.enable_dma_receive = dwmac_enable_dma_receive,使能dma 接收writel(1, ioaddr + (channel * 0x100) + DMA_XMT_POLL_DEMAND);根据通道号选择。
.get_dma_rx_state = dwmac_get_dma_rx_state,读取dma通道的接收状态
.enable_dma_irq = dwmac_enable_dma_irq, 使能中断
.disable_dma_irq = dwmac_disable_dma_irq,
.start_tx = dwmac_dma_start_tx, 开始发送
.stop_tx = dwmac_dma_stop_tx,
.start_rx = dwmac_dma_start_rx,开始接收
.stop_rx = dwmac_dma_stop_rx,
.dma_interrupt = dwmac_dma_interrupt, intr_status = readl(ioaddr + (channel * 0x100) + DMA_STATUS);读取中断状态,根据中断状态操作
};
六:增强型设备驱动结构体:在程序中实际调用的发送接收接口函数,就饿是dma的上层接口函数。
const struct stmmac_desc_ops enh_desc_ops = {
.tx_status = enh_desc_get_tx_status,根据dma 的状态返回函数接口的接收状态
.rx_status = enh_desc_get_rx_status,
.get_tx_len = enh_desc_get_tx_len,
.init_rx_desc = enh_desc_init_rx_desc,
.init_tx_desc = enh_desc_init_tx_desc,
.get_tx_owner = enh_desc_get_tx_owner,
.get_rx_owner = enh_desc_get_rx_owner,
.release_tx_desc = enh_desc_release_tx_desc,
.prepare_tx_desc = enh_desc_prepare_tx_desc,
.clear_tx_ic = enh_desc_clear_tx_ic,
.close_tx_desc = enh_desc_close_tx_desc,
.get_tx_ls = enh_desc_get_tx_ls,
.set_tx_owner = enh_desc_set_tx_owner,
.set_rx_owner = enh_desc_set_rx_owner,
.get_rx_frame_len = enh_desc_get_rx_frame_len,
.get_rx_dirty_flag = enh_desc_get_rx_dirty_flag,
.set_rx_dirty_flag = enh_desc_set_rx_dirty_flag,
.clear_rx_dirty_flag = enh_desc_clear_rx_dirty_flag,
.get_rx_curr_flag = enh_desc_get_rx_curr_flag,
.set_rx_curr_flag = enh_desc_set_rx_curr_flag,
.clear_rx_curr_flag = enh_desc_clear_rx_curr_flag,
.set_tx_igmp = enh_desc_set_tx_igmp,
.clean_tx_igmp = enh_desc_clean_tx_igmp,
};
七:数据接收包:
当产生mac中断后调用该函数static int stmmac_poll(struct napi_struct *napi, int budget)
status = priv->hw->desc->rx_status(&priv->dev->stats,&priv->xstats, p);读取接收帧状态
八:基于phy的连接:通过调用
stmmac_open(struct net_device *dev);打开网络设备,stmmac_init_phy(dev);初始化phy
phydev = phy_connect(dev, phy_id, &stmmac_adjust_link,priv->phy_interface);连接一个phy设备到网络驱动
d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id);通过总线名称查找注册到总线上的phy设备
rc = phy_connect_direct(dev, phydev, handler, interface);连接一个phy设备到网络驱动上,
stmmac_adjust_link(struct net_device *dev):当phy的状态发生变化,通过这个函数改变,修改mac寄存器,修改phy的寄存器,配置速率,控制方式,连接状态。
九:所用的设备驱动:/driver/net/phy、phy_device.c文档中创建phy的驱动
static struct phy_driver genphy_driver = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic PHY",phy驱动名称
.config_init = genphy_config_init, //配置初始化
.features = 0,
.config_aneg = genphy_config_aneg, //自动配置参数
.read_status = genphy_read_status, //读取phy的状态。连接状态,双工,自动配置等功能
.suspend = genphy_suspend, //低功率使能
.resume = genphy_resume,
.driver = {.owner= THIS_MODULE, },
};