SylixOS -- 网卡驱动浅析
halNetifAttch():
在bspInit中会调用__enetInit函数初始化网口驱动,
static VOID halNetifAttch (VOID)
{__enetInit();
}#endif /* LW_CFG_NET_EN > 0 */
__enetInit():
传参(寄存器基地址和EMAC中断号),并初始化自旋锁。
INT __enetInit (VOID)
{
……netdev.priv = &_G_enetInfo;LW_SPIN_INIT(&_G_enetInfo.EentSlLock);__enetRegister(&netdev);
……
}
__enetRegister():
1.注册网络接口(__enetCoreInit,__enetCoreTx,__enetCoreRecv),
2.__enetSetHwaddr函数设置mac地址(如果添加NETDEV_INIT_LOAD_PARAM宏,那么注册网络时,会去检测系统etc目录下的ifparam.ini文件,如果没有这个文件,mac地址会为00:00:00:00:00:00。如果没有这个文件时,添加__enetSetHwaddr函数设置一下MAC地址。)
3.pNetDev相关参数赋值,最后将ip,netmask,gw与网口驱动绑定。绑定成功后创建线程用于检测网口连接状态(PHY芯片BMSR寄存器中有检测网口是否连接的状态位)
static VOID __enetRegister (struct netdev *pNetDev)
{
……static struct netdev_funcs net_drv = {.init = __enetCoreInit,.transmit = __enetCoreTx,.receive = __enetCoreRecv,};#if SET_MAC_AUTO__enetSetHwaddr(pNetDev); /* 设置mac地址 */
#endif
……lib_strcpy(ip, "192.168.3.202");lib_strcpy(gw, "192.168.3.1");lib_strcpy(netmask, "255.255.255.0");if (netdev_add(pNetDev,ip,netmask,gw,IFF_UP | IFF_RUNNING | IFF_BROADCAST | IFF_MULTICAST) == 0) {__enetThreadCreate(pNetDev);}
}
__enetLinkThread():
若检测到网口已连接,调__setEmacSpeed函数设置mac(查看网口支持哪种连接方式并将PHY设置成相应模式)
static PVOID __enetLinkThread (PVOID pvArg)
{
……__phyRead(pNetDev, CONFIG_DRIVER_REGEMAC_PHYADDR, MII_BMSR, &usValue);if (usValue & PHY_STATUS_LINK_ST) { /* 此时为连接成功状态 */_G_uiNetLinkStatus = PHY_STATUS_LINK_ST;/** 检测连接能力并设置mac*/__setEmacSpeed(pNetDev);netdev_set_linkup(pNetDev, 1, pNetDev->speed);}
……
}
__enetCoreInit():
1.开始init,transfer,receive函数编写
2.使能时钟,MAC初始化,PHY初始化,绑定中断服务。
3.PHY初始化函数功能大致和__enetLinkThread函数功能相似,检测网口连接状态。(不同的是,若检测到未连接,PHY会复位并进行自动协商,而__enetLinkThread函数直接退出。)通常在U-boot中已经进行过PHY复位和自动协商,所以这一步实际应用中可以省略。
static INT __enetCoreInit (struct netdev *pNetDev)
{
……__enetBoardInit(pNetDev);__enetCoreStart(pNetDev); /* MAC初始化 */__phyInit(pNetDev); /* PHY初始化 *//** 绑定中断服务函数*/API_InterVectorConnect(pEnet->iIrqNum,(PINT_SVR_ROUTINE)__enetIsr,(PVOID)pNetDev,"enet_isr");API_InterVectorEnable(pEnet->iIrqNum);return (ERROR_NONE);
}
EMAC中每一帧数据的接收和发送都需要通过DMA接口。
如框图所示,DMA通过AHB总线连接外部存储器,它包含用于缓冲帧数据的接收和发送FIFO,DMA模块用于管理发送和接收帧缓冲区队列,这些队列可以保留多个帧。
发送时,处理器将数据放在发送缓冲区(TX FIFO),通知DMA模块发送数据至MAC模块,最终发送到网络。
接收时,MAC模块从网络接收数据并通知DMA模块取至接收缓冲区(RX FIFO),完成后通知处理器来取。
DMA模块收发数据的单元被称为BD( Buffer Description,缓存描述符),网络通信的每个包都会被分成若干个帧,而每个帧则被保存在一个或多个BD中。所有的BD组成了一张BD 表,一般来说发送方向和接收方向的BD 表是各自独立的。
__enetBuffDescInit():
MAC初始化配置相关寄存器,使能中断,初始化EMAC 缓冲描述符(给描述符分配DMA内存,包括每个描述符对应的接收/发送缓冲区也要分配DMA内存),将分配内存后的描述符地址写入队列寄存器(DMA是通过队列寄存器中的地址找到BD描述符)
注意:描述符和对应缓冲区都需要用API_VmmDmaAlloc专用接口去分配内存。使用普通方式申请的内存可能会产生cache一致性问题,由于cache存在于CPU与内存之间,所以任何外设对内存的修改并不能保证cache中也得到同样的更新,同样处理器对缓存中内容的修改也不能保证内存中的数据得到更新。这一现象会导致DMA传输时好时坏。得到的数据并不完全正确。
static VOID __enetBuffDescInit (struct netdev *pNetDev)
{
……/** 给描述符分配DMA内存*/pEnet->pRxRing = (REGRxBDescriptor *)API_VmmDmaAlloc(RX_BUFS_MX * sizeof(struct RxBDescriptor));pEnet->pTxRing = (REGTxBDescriptor *)API_VmmDmaAlloc(TX_BUFS_MX * sizeof(struct TxBDescriptor));/** 接收描述符初始化*/pcRxbufAddr = (CHAR *)API_VmmDmaAllocAlign(RX_BUFS_MX * RX_BUFS_SIZE, 4);
……/** 发送描述符初始化*/pcTxbufAddr = (CHAR *)API_VmmDmaAlloc(TX_BUFS_MX * TX_BUFS_SIZE);
……/** 描述符写入队列寄存器*/writel((INT32)pEnet->pRxRing, ulBaseaddr + REG_EMAC_RBQP);writel((INT32)pEnet->pTxRing, ulBaseaddr + REG_EMAC_TBQP);
}
__enetCoreTx():
__enetCoreTx网络发送函数,将数据拷贝到指定缓冲区,填充描述符内容,开始发送,统计。
LW_SPIN_LOCK_QUICK为自旋锁加锁操作,防止在传输过程中再一次产生传输命令。结束后LW_SPIN_UNLOCK_QUICK解锁。(CPU正在填充前一个帧的BD描述符,操作还未结束,指针还停留在当前描述符未指向下一个BD,此时若再次产生一个传输命令,CPU会将前一个BD描述符中的信息覆盖,从而产生错误。)
KN_SMP_WMB()内存屏障,作用为不要将该段内嵌汇编指令与前面的指令重新排序;也就是在执行内嵌汇编代码之前,它前面的指令都执行完毕。同时该指令前的变量缓存在后面使用时需重新到内存中读取。
static INT __enetCoreTx (struct netdev *pNetDev, struct pbuf *pstPbuf)
{
……LW_SPIN_LOCK_QUICK(&pEnet->EentSlLock, &iRegFlag);
……/** 拷贝buf*/pbuf_copy_partial(pstPbuf, (PVOID)pEnet->pTxRing[pEnet->iCurrentTx].iTxBaddr, usLen, 0);/** 填充描述符内容*/KN_SMP_WMB();
……KN_SMP_WMB();/** 开始发送*/writel(readl(ulBaseAddr + REG_EMAC_CTL) | REG_EMAC_CTL_TSR , ulBaseAddr + REG_EMAC_CTL);netdev_statinfo_total_add(pNetDev, LINK_OUTPUT, usLen); /* 统计更新 */if (((UINT8 *)pstPbuf->payload)[0] & 1) {netdev_statinfo_mcasts_inc(pNetDev, LINK_OUTPUT); /* 统计发送广播数据包数 */} else {netdev_statinfo_ucasts_inc(pNetDev, LINK_OUTPUT); /* 统计发送单播数据包数 */}LW_SPIN_UNLOCK_QUICK(&pEnet->EentSlLock, iRegFlag);return (ERROR_NONE);
}
__enetCoreRecv():
__enetCoreRecv网络接收函数,通过RX中BD描述符内容,CPU去指定缓冲区得到数据,向上层传递数据。
static VOID __enetCoreRecv (struct netdev *pNetDev, INT (*input)(struct netdev *, struct pbuf *))
{
……while(1) {KN_SMP_WMB();if (!(pEnet->pRxRing[ulRxTail].iRxBaddrAndFlag & RX_DES_FLAG_OWNER)) {return;}
……if (input(pNetDev, pBuf)) { /* 向上层协议传递数据 */netdev_pbuf_free(pBuf);netdev_linkinfo_drop_inc(pNetDev);netdev_statinfo_discards_inc(pNetDev, LINK_INPUT);} else {netdev_linkinfo_recv_inc(pNetDev);netdev_statinfo_total_add(pNetDev, LINK_INPUT, ulLength);
}
……KN_SMP_WMB();}
__enetIsr():
__enetIsr中断函数,在发送/接收成功后会进入中断,对描述符进行相应处理。
static irqreturn_t __enetIsr (PVOID pvArg, UINT32 uiVector)
{
……/** 在读取时中断状态寄存器,标志位会被清除*/uiIntStatus = readl(ulBaseaddr + REG_EMAC_ISR);uiRevStatus = readl(ulBaseaddr + REG_EMAC_RSR);/** 接收成功*/if ((uiIntStatus & REG_EMAC_IxR_RCOM) || (uiRevStatus & REG_EMAC_RSR_REC)) {netdev_notify(pNetDev, LINK_INPUT, 1); /* 通知系统进行接收 *//** 清除REC(Frame Received)位*/writel(uiRevStatus | REG_EMAC_RSR_REC, ulBaseaddr + REG_EMAC_RSR);}/** 发送成功*/if (uiIntStatus & REG_EMAC_IxR_TCOM) {__resetTxBDescriptor(pNetDev); /* 复位发送描述符 */writel(REG_EMAC_TSR_COMP, ulBaseaddr + REG_EMAC_TSR);}
……return (LW_IRQ_HANDLED);
}
SylixOS -- 网卡驱动浅析相关推荐
- 网卡驱动描述符助手功能浅析
前言 网卡驱动里,CPU和MAC控制器都需要对DMA描述符空间进行读取或者写入.DMA描述符空间又会采用到CACHE和零拷贝技术,以往都是驱动自己去申请.关联内存和刷新(flush和invalidat ...
- e1000网卡驱动初感受
在网络上搜索到一片Linux-千兆网卡驱动实现机制浅析,自己大概浏览了一下,觉得写得很好,可是自己没有看明白的时候还是白扯.想起来一句话,文档时写给已经懂了的人的.这句话在我做一个小东西的时候领悟的特 ...
- linux网卡e1000下载,Linux E1000网卡驱动分析
本分析主要针对e1000网卡,驱动源码为7.3.20-k2.本文的目的不是为了讲述如何编写驱动程序,主要是分析网卡驱动内部的实现机制. Linux-千兆网卡驱动实现机制浅析 作者: Minit, 出处 ...
- udp数据报从网卡驱动到用户空间流程总结
附有相关介绍资料 NAPI驱动流程: 中断发生 -->确定中断原因是数据接收完毕(中断原因也可能是发送完毕,DMA完毕,甚至是中断通道上的其他设备中断) -->通过 ...
- 无法安装此计算机不存在英特尔,win2008serverr2intel网卡驱动无法安装不存在英特尔PRO适配器的解决方法...
无法安装驱动程序,此计算机上不存在英特尔(R),PRO适配器,甚至去官网下载了 intel 82579vforwindows 2008 r2 的驱动 都提示这个. 解决方法: intel官网下载for ...
- 备份一个万能网卡驱动
昨天安装了一个windows2008,因为是在pc上安装,还是个老机器,安装完成后竟然发现我的网卡驱动未成功安装,我就觉得弄个驱动精灵不就ok了吗,结果安装提示不支持server版本,安装驱动人生也不 ...
- linux拓实n87驱动下载,拓实n87网卡驱动for xp/win7官方版
拓实n87网卡驱动for xp/win7官方版是一个十分强大的网卡驱动管理软件,拓实n87网卡驱动for xp/win7官方版是ts N87高增益全向无线USB网卡驱动程序,拓实n87全面支持移动cm ...
- (超贴心)Centos7安装2.5G网卡驱动(Realtek 3000)
文章目录 前言 准备 正题 问题1: 问题2 问题3 问题4 前言 首先跟大家聊聊我的情况吧.因为网络极其关键,要不然服务器还服务个毛,就会是一个废物. 我是在最小化安装Centos7系统的时候,设置 ...
- 【技术贴】虚拟机 VMware win7 win8网卡驱动下载 解决虚拟机不识别网卡没有本地连接...
解决虚拟机VMware7.0下虚拟win7 win8找不到网卡,不能识别网卡.没有本地连接.(本篇文章只适合虚拟机win7/win8 32位环境) 废话不多说,直接入题.vmware 虚拟机 win7 ...
- Linux 网卡驱动相关——03
紧接上一篇,这里简要介绍net_device 结构和网卡驱动框架. struct net_device 是一个比sk_buff 更复杂的结构,里面包含了与TCP/IP协议栈通信的接口函数,但是自从2 ...
最新文章
- MySQL Replace INTO的使用
- LeetCode 21. Merge Two Sorted Lists--合并2个有序列表--python递归,迭代解法
- linux中链表的使用【转】
- 编辑器推荐KindEditor
- 【机器学习】怎样将Embedding融入传统机器学习框架?
- OpenGL 着色器的N体仿真
- java中sql去除游标_java.sql.SQLException:-ORA-01000:已超过最大打开游标
- 事情各大厂商在战场上布局
- linux运维和3dmax哪个简单,牛逼运维常用的工具系列-2
- 如何用for循环出数据库的数据
- 快速实现手势解锁功能
- uniapp开发原生android插件,获取浏览器cookie
- 专题讲座3 数论+博弈论 学习心得
- 如何区分冲突域和广播域?
- 如何在html修改图片大小,HTML – 如何在CSS中动态调整图像大小?
- 原生js实现购物车添加删除商品、计算价格功能
- STM32F1案例 ST7735 TFT液晶显示屏综合库使用
- UTF-16、UTF-16BE、UTF-16LE编码方式的区别
- gridControl控件的gridView实现全选
- APP系统开发模式一共有哪几种?