MII即媒体独立接口,也叫介质无关接口。
它包括一个数据接口,以及一个MAC和PHY之间的管理接口(图1)。
数据接口包括分别用于发送器和接收器的两条独立信道。每条信道都有自己的数据、时钟和控制信号。MII数据接口总共需16个信号。
管理接口是个双信号接口:一个是时钟信号,另一个是数据信号。通过管理接口,上层能监视和控制PHY。
RMII口是用两根线来传输数据的,
MII口是用4根线来传输数据的,
GMII是用8根线来传输数据的。
GMII (Gigabit MII)
GMII是8bit并行同步收发接口,采用8位接口数据,工作时钟125MHz,因此传输速率可达1000Mbps。同时兼容MII所规定的10/100 Mbps工作方式。
GMII接口数据结构符合IEEE以太网标准。该接口定义见IEEE802.3-2000。
发送器:
◇ GTXCLK——吉比特TX..信号的时钟信号(125MHz)
◇ TXCLK——10/100M信号时钟
◇ TXD[7..0]——被发送数据 ------- mii 为4,所以一个信道是8,两个是16
◇ TXEN——发送器使能信号
◇ TXER——发送器错误(用于破坏一个数据包)
注:在千兆速率下,向PHY提供GTXCLK信号,TXD、TXEN、TXER信号与此时钟信号同步。否则,在10/100M速率下,PHY提供 TXCLK时钟信号,其它信号与此信号同步。其工作频率为25MHz(100M网络)或2.5MHz(10M网络)。
接收器:
◇ RXCLK——接收时钟信号(从收到的数据中提取,因此与GTXCLK无关联)
◇ RXD[7..0]——接收数据
◇ RXDV——接收数据有效指示
◇ RXER——接收数据出错指示
◇ COL——冲突检测(仅用于半双工状态)
管理配置
◇ MDC——配置接口时钟
◇ MDIO——配置接口I/O
管理配置接口控制PHY的特性。该接口有32个寄存器地址,每个地址16位。其中前16个已经在“IEEE 802.3,2000-22.2.4Management Functions”中规定了用途,其余的则由各器件自己指定。
MII/RMII只是一种接口,对于10M线速,MII的速率是2.5M,RMII则是5M;对于100M线速,MII的速率是25M,RMII则是50M。
SGMII--Serial Gigabit Media IndependentInterface
SGMII是PHY与MAC之间的接口,类似与GMII和RGMII,只不过GMII和RGMII都是并行的,而且需要随路时钟,PCB布线相对麻烦,而且不适应背板应用。
而SGMII是串行的,不需要提供另外的时钟,MAC和PHY都需要CDR去恢复时钟。另外SGMII是有8B/10b编码的,速率是1.25G
在 linux 配置PHY
drivers/net/phy
配置的参数 自适应, 1000M, 全双工。
phydev-> autonet, speed, duplex.
1. MDIO简介
The MDIO interface is a simple, two-wire, serial interface to connect a management entity and a managed PHY for the purposes of controlling the PHY and gathering status from the PHY.
The two lines include the MDC line [Management Data Clock], and the MDIO line [Management Data Input/Output]. The clock is point-to-point, while the data line is a bi-directional multi-drop interface.
The data line is Tri-state able and can drive 32 devices.
MDIO接口,MAC与PHY间的管理接口(MII是数据接口),有2根线:时钟线MDC,数据线MDIO(双向)
MDIO工作流程:
* Preamle(PRE) 在没有传输数据的空闲状态时,数据线MDIO处于高阻态(一直为1)。
* Start of Frame(ST) MAC驱动MDIO线,出现一个2bit的开始标识码(01)。
* Operation Code(OP) MAC驱动MDIO线,出现一个2bit数据来标识是读操作(10)还是写操作(01)。
* PHY Address(PHYAD) MAC驱动MDIO线,出现一个5bit数据标识PHY的地址。
* Reg Address(REGAD) MAC驱动MDIO线,出现一个5bitPHY寄存器地址。
* Turnaround(TA) 写操作的话,MAC驱动MDIO线,出现10
读操作的话,MDIO pin of MAC must be put in high-impedance state
在第二个周期,PHY驱动MDIO线,出现0
* Data MDIO串行读出/写入16bit的寄存器数据。
* MDIO恢复成空闲状态,同时MDIO进入高阻状态。
下面是PHY芯片 BCM5461 的一个例子:
2. PowerPC对MDIO的支持
PowerPC操作MDIO时,涉及以下寄存器:
MIIMCFG 配置寄存器
MIIMCOM 命令寄存器
MIIMADD 地址寄存器
MIIMCON 控制寄存器
MIIMSTAT 状态寄存器
MIIMIND 指示寄存器
以MPC8560举例,这些寄存器在CCSR中的位置如下:
2.1 MIIMCFG:配置寄存器
ResetMgmt: 用于重置MDIO模块
MgmtClockSet:时钟设置,是CCB的 2的n次方之一
2.2 MIIMCOM 命令寄存器
ReadCycle: 0->1 触发MDIO读时序
2.3 MIIMADD 地址寄存器
PHYaddr:PHY地址,共5bit,系统最多联31个PHY(地址0为保留)
REGaddr:寄存器地址,共5bit,一个PHY上最多32个寄存器地址(可以使用shadow value技术,访问更多的寄存器)
2.4 MIIMCON 控制寄存器
PHYcontrol:在写流程时,这里存放要写入寄存器的值
2.5 MIIMSTAT 状态寄存器
PHYstatus:读流程时,PHY reg的内容会放到此
2.6 MIIMIND 指示寄存器
NotVal:若置1,表示读流程结束,可以去读MIIMSTAT
Scan: 若置1,表示扫描流程进行中
Busy: 只有置0时,才能进行新的读写流程
3. linux中MDIO的实现
读写PHY寄存器时通过2个函数
phy_read()和phy_write(),
最终调用
int gfar_local_mdio_read(struct gfar_mii *regs, int mii_id, int regnum)
int gfar_local_mdio_write(struct gfar_mii *regs, int mii_id, int regnum, u16 value)
参数regs就是MDIO相关寄存器:
struct gfar_mii {
u32 miimcfg; /* 0x.520 - MII Management Config Register */
u32 miimcom; /* 0x.524 - MII Management Command Register */
u32 miimadd; /* 0x.528 - MII Management Address Register */
u32 miimcon; /* 0x.52c - MII Management Control Register */
u32 miimstat; /* 0x.530 - MII Management Status Register */
u32 miimind; /* 0x.534 - MII Management Indicator Register */
};
参数mii_id,就是PHY的id
参数regnum,就是寄存器地址
上代码,简单不解释
int gfar_local_mdio_read(struct gfar_mii *regs, int mii_id, int regnum)
{
u16 value;
/* Set the PHY address and the register address we want to read */
gfar_write(®s->miimadd, (mii_id <
/* Clear miimcom, and then initiate a read */
gfar_write(®s->miimcom, 0);
gfar_write(®s->miimcom, MII_READ_COMMAND);
/* Wait for the transaction to finish */
while (gfar_read(®s->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
cpu_relax();
/* Grab the value of the register from miimstat */
value = gfar_read(®s->miimstat);
return value;
}
int gfar_local_mdio_write(struct gfar_mii *regs, int mii_id,
int regnum, u16 value)
{
/* Set the PHY address and the register address we want to write */
gfar_write(®s->miimadd, (mii_id <
/* Write out the value we want */
gfar_write(®s->miimcon, value);
/* Wait for the transaction to finish */
while (gfar_read(®s->miimind) & MIIMIND_BUSY)
cpu_relax();
return 0;
}
内核启动时的准备工作
4.1 初始化网络相关的全局数据结构,并挂载处理网络相关软中断的钩子函数
start_kernel()
--> rest_init()
--> do_basic_setup()
--> do_initcall
-->net_dev_init
__init net_dev_init()
{
//每个CPU都有一个CPU私有变量 _get_cpu_var(softnet_data)
//_get_cpu_var(softnet_data).poll_list很重要,软中断中需要遍历它的
for_each_possible_cpu(i) {
struct softnet_data *queue;
queue = &per_cpu(softnet_data, i);
skb_queue_head_init(&queue->input_pkt_queue);
queue->completion_queue = NULL;
INIT_LIST_HEAD(&queue->poll_list);
queue->backlog.poll = process_backlog;
queue->backlog.weight = weight_p;
}
open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); //在软中断上挂网络发送handler
open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL); //在软中断上挂网络接收handler
}
4.2 加载网络设备的驱动
NOTE:这里的网络设备是指MAC层的网络设备,即TSEC和PCI网卡(bcm5461是phy)
在网络设备驱动中创建net_device数据结构,并初始化其钩子函数 open(),close() 等
挂载TSEC的驱动的入口函数是 gfar_probe
// 平台设备 TSEC 的数据结构
static struct platform_driver gfar_driver = {
.probe = gfar_probe,
.remove = gfar_remove,
.driver = {
.name = "fsl-gianfar",
},
};
int gfar_probe(struct platform_device *pdev)
{
dev = alloc_etherdev(sizeof (*priv)); // 创建net_device数据结构
dev->open = gfar_enet_open;
dev->hard_start_xmit = gfar_start_xmit;
dev->tx_timeout = gfar_timeout;
dev->watchdog_timeo = TX_TIMEOUT;
#ifdef CONFIG_GFAR_NAPI
netif_napi_add(dev, &priv->napi,gfar_poll,GFAR_DEV_WEIGHT); //软中断里会调用poll钩子函数
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
dev->poll_controller = gfar_netpoll;
#endif
dev->stop = gfar_close;
dev->change_mtu = gfar_change_mtu;
dev->mtu = 1500;
dev->set_multicast_list = gfar_set_multi;
dev->set_mac_address = gfar_set_mac_address;
dev->ethtool_ops = &gfar_ethtool_ops;
}
五、启用网络设备
5.1 用户调用ifconfig等程序,然后通过ioctl系统调用进入内核
socket的ioctl()系统调用
--> sock_ioctl()
--> dev_ioctl() //判断SIOCSIFFLAGS
--> __dev_get_by_name(net, ifr->ifr_name) //根据名字选net_device
--> dev_change_flags() //判断IFF_UP
--> dev_open(net_device) //调用open钩子函数
对于TSEC来说,挂的钩子函数是 gfar_enet_open(net_device)
5.2 在网络设备的open钩子函数里,分配接收bd,挂中断ISR(包括rx、tx、err),对于TSEC来说
gfar_enet_open
--> 给Rx Tx Bd 分配一致性DMA内存
--> 把Rx Bd的“EA地址”赋给数据结构,物理地址赋给TSEC寄存器
--> 把Tx Bd的“EA地址”赋给数据结构,物理地址赋给TSEC寄存器
--> 给 tx_skbuff 指针数组 分配内存,并初始化为NULL
--> 给 rx_skbuff 指针数组 分配内存,并初始化为NULL
--> 初始化Tx Bd
--> 初始化Rx Bd,提前分配存储以太网包的skb,这里使用的是一次性dma映射
(注意:#define DEFAULT_RX_BUFFER_SIZE 1536保证了skb能存一个以太网包)
rxbdp = priv->rx_bd_base;
for (i = 0; i < priv->rx_ring_size; i++) {
struct sk_buff *skb = NULL;
rxbdp->status = 0;
//这里真正分配skb,并且初始化rxbpd->bufPtr, rxbdpd->length
skb = gfar_new_skb(dev, rxbdp);
priv->rx_skbuff[i] = skb;
rxbdp++;
}
rxbdp--;
rxbdp->status |= RXBD_WRAP; // 给最后一个bd设置标记WRAP标记
--> 注册TSEC相关的中断handler: 错误,接收,发送
request_irq(priv->interruptError, gfar_error, 0, "enet_error", dev)
request_irq(priv->interruptTransmit, gfar_transmit, 0, "enet_tx", dev)//包发送完
request_irq(priv->interruptReceive, gfar_receive, 0, "enet_rx", dev) //包接收完
-->gfar_start(net_device)
// 使能Rx、Tx
// 开启TSEC的 DMA 寄存器
// Mask 掉我们不关心的中断event
最终,TSEC相关的Bd等数据结构应该是下面这个样子的
六、中断里接收以太网包
TSEC的RX已经使能了,网络数据包进入内存的流程为:
网线 --> Rj45网口 --> MDI 差分线
--> bcm5461(PHY芯片进行数模转换) --> MII总线
--> TSEC的DMA Engine 会自动检查下一个可用的Rx bd
--> 把网络数据包 DMA 到 Rx bd 所指向的内存,即skb->data
接收到一个完整的以太网数据包后,TSEC会根据event mask触发一个 Rx 外部中断。
cpu保存现场,根据中断向量,开始执行外部中断处理函数do_IRQ()
do_IRQ 伪代码
{
上半部处理硬中断
查看中断源寄存器,得知是网络外设产生了外部中断
执行网络设备的rx中断handler(设备不同,函数不同,但流程类似,TSEC是gfar_receive)
1. mask 掉 rx event,再来数据包就不会产生rx中断
2. 给napi_struct.state加上 NAPI_STATE_SCHED 状态
3. 挂网络设备自己的napi_struct结构到cpu私有变量_get_cpu_var(softnet_data).poll_list
4. 触发网络接收软中断
下半部处理软中断
依次执行所有软中断handler,包括timer,tasklet等等
执行网络接收的软中断handler net_rx_action
1. 遍历cpu私有变量_get_cpu_var(softnet_data).poll_list
2. 取出poll_list上面挂的napi_struct 结构,执行钩子函数napi_struct.poll()
(设备不同,钩子函数不同,流程类似,TSEC是gfar_poll)
3. 若poll钩子函数处理完所有包,则打开rx event mask,再来数据包的话会产生rx中断
4. 调用napi_complete(napi_struct *n)
把napi_struct 结构从_get_cpu_var(softnet_data).poll_list 上移走
同时去掉 napi_struct.state 的 NAPI_STATE_SCHED 状态
}
6.1 TSEC的接收中断处理函数
gfar_receive
{
#ifdef CONFIG_GFAR_NAPI
// test_and_set当前net_device的napi_struct.state 为 NAPI_STATE_SCHED
// 在软中断里调用 net_rx_action 会检查状态 napi_struct.state
if (netif_rx_schedule_prep(dev, &priv->napi)) {
tempval = gfar_read(&priv->regs->imask);
tempval &= IMASK_RX_DISABLED; //mask掉rx,不再产生rx中断
gfar_write(&priv->regs->imask, tempval);
// 将当前net_device的 napi_struct.poll_list 挂到
// CPU私有变量__get_cpu_var(softnet_data).poll_list 上,并触发软中断
// 所以,在软中断中调用 net_rx_action 的时候,就会执行当前net_device的
// napi_struct.poll()钩子函数,即 gfar_poll()
__netif_rx_schedule(dev, &priv->napi);
}
#else
gfar_clean_rx_ring(dev, priv->rx_ring_size);
#endif
}
https://blog.csdn.net/jk198310/article/details/12909341
Linux 网络子系统
今天记录一下Linux网络子系统相关的东西. 因为感觉对这一块还是有一个很大的空白,这件事情太可怕了. 摘抄多份博客进行总结一下Linux网络子系统的相关东西. 一. Linux网络子系统体系结构 L ...
Linux网络子系统
再Linux的世界里,万物皆文件,通过虚拟文件系统VFS,程序可以用标准的Linux系统调用对不同的文件系统,甚至不同介质上的文件系统进行读写操作.下面我们揭示Linux网络子系统的秘密 sockfs ...
Linux 网络子系统之网络协议接口层(一)
Linux 网络设备驱动之网络协议接口层介绍. 网络协议接口层最主要的功能是给上层协议提供透明的数据包发送和接收接口. 当上层ARP或IP需要发送数据包时,它将调用网络协议接口层的dev_queue_ ...
linux网络子系统内核分析
1.选择路由 若要将数据包发至PC2,则linux系统通过查询路由表可知168.1.1.10(目的地址)的网关地址为192.168.1.1,此时linux系统选择网卡1发送数据包. 2.邻居子系统(通 ...
末学者笔记--Linux网络模式及网卡配置
一.linux网络的模式 三种模式:桥接,Nat和仅主机模式 一.桥接模式:配置桥接模式的虚拟机作为独立计算机存在 特点: 1. 虚拟机可以上外网2. 可以和局域网内任意一台电脑通信3. 可以和宿主机 ...
Linux 网络子系统之网络协议接口层(二)
这一篇主要围绕网络协议接口层的发送函数的解析 int dev_queue_xmit(struct sk_buff *skb) 函数解析 声明: /* include/linux/netdevice.h ...
Linux 网络子系统之结构介绍
Linux 网络设备驱动程序的体系结构 图片说明如下: 网络协议接口层 网络协议接口层向网络层协议提供统一的数据包收发接口,不论上层协议是ARP还是IP,都通过 dev_queue_xmit() 函数 ...
Linux 网络子系统之NAPI书签
只是一个书签 http://blog.csdn.net/ustc_dylan/article/details/6116334
随机推荐
perl 删除过期文件
#!/usr/bin/perl `find /bak/ >list.txt`; open LIST,"/root/list.txt"; while ( ...
hihocoder #1327
传送门 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个只包含小写字母'a'-'z'的字符串 S ,你需要将 S 中的字符重新排序,使得任意两个相同的字符不连在一 ...
Android自定义dialogdemo
很多时候,我们需要自己去定义dialog,目前我们就遇见了这样一个需求,我的想法是自己定义一个dialog,如果有list的话就使用listview,如果有msg的话就使用msg,并且取消和确定按钮也 ...
c++sort函数的用法浅析
(一)为什么要用c++标准库里的排序函数 Sort()函数是c++一种排序方法之一,学会了这种方法也打消我学习c++以来使用的冒泡排序和选择排序所带来的执行效率不高的问题!因为它使用的排序方法是类似于 ...
转:Ext GridPanel根据条件显示复选框
Ext GridPanel实现复选框选择框: var selectModel = new Ext.grid.CheckboxSelectionModel({ singleSelect : false ...
Vue.js 指南-基础
Installation 可以使用的方式: script标签方式加载vue.js cdn https://unpkg.com/[email protected]/dist/vue.js npm Introductio ...
MongoDb进阶实践之五 MongoDB修改命令详述
一.引言 上一篇文章我们已经详细介绍了MongoDB数据库的有关查询的内容,但是这只是所有查询命令的冰山一角.所有查询命令都写完也没有必要,我只是写了一些常用的命令,对MongoDB的 ...
移动端rem与px适应js
方法一: (function (doc, win) { var docEl = doc.documentElement, resizeEvt = "orientationchange&quo ...
为什么要用kafka、rabbit等消息队列
1.解耦: 允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束. 2.冗余: 消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险.许多消息队列所采用的&q ...
maven-shade-plugin插件
maven-shade-plugin主要是maven-assembly-plugin的后继者,用来将一个自启动jar项目的依赖打包到一个大的jar中,比如dubbo就是这么做的.具体可参考http:/ ...