SylixOS适配MPC8377网卡驱动(etsec)编写总结

原创 2016年08月30日 11:44:46

经过这一个月的学习,总算勉勉强强在uboot的基础上改好了网卡驱动:


发送只用了一个包缓冲buffer,大小就是1536(MTU1500的情况下),接收区缓冲20个,每个1536字节。发送没有用中断的方式,接收采用中断的方式进行。注意缓冲描述符在这个驱动里面是采用的静态全局变量的方式:

typedef volatile struct rtxbd {
	txbd8_t txbd[TX_BUF_CNT];
	rxbd8_t rxbd[PKTBUFSRX];
} RTXBD;
static RTXBD rtx __attribute__ ((aligned(8)));

首先我们需要熟读一下mpc8377的网卡相关与中断相关的datasheet,主要注意以下几个寄存器:

    中断控制器ipic的SIVCR、SIPNR(H/L)、SIMSR(H/L)、SEMSR

    网卡控制器etsec的IEVENT、IMASK、DMACTRL、MACCFG1、MACCFG2,还有一些PHY相关寄存器(好吧其实我没怎么看PHY的)

设置相关寄存器的宏定义、相关寄存器位的置位宏定义,注意ievent寄存器是写1清除中断,imask是写0屏蔽中断。

整个网卡的驱动初始化流程(以SylixOS的BSP为例):

1.在bsp初始化中调用的是halNetifAttch (VOID)函数:

        完成的功能:

         设置网卡物理地址mac、网关、默认ip、设置ipv6相关、调用上层协议(应该是lwip)里面的初始化函数(涉及内核,不需要我们实现)。

         然后用协议栈的netif_add函数添加网卡,并绑定初始化函数tsecEthInit

2.tsecEthInit函数:

       从这里开始的实现,部分抄袭了uboot网卡驱动,这里提一句,uboot里面是最简单的轮询的方式才进行接收,比较消耗处理器,而且速度相对而言也比较慢。所以我要改造uboot里面的网卡驱动,将接收从轮询改为中断的方式进行。
      这个函数里面进行的步骤如下:

2.1分配DMA区域给实际包缓冲区域:

         
for (i = 0; i < PKTBUFSRX; i++) {
        NetRxPackets[i] = API_VmmDmaAllocAlign(PKTSIZE_ALIGN, PKTALIGN);
    }

    NetTxPacket = API_VmmDmaAllocAlign(PKTSIZE_ALIGN, PKTALIGN);
 然后分配网卡数据结构:
      priv = (struct tsec_private *)sys_malloc(sizeof(*priv));        /*  分配网卡数据结构            */

调用tsec_initialize()函数

2.2 int tsec_initialize(int index, struct tsec_private *priv):

           为结构体priv里的结构分配内存并初始化phy寄存器、mac控制寄存器、bufferdescriptor(缓冲描述符)。前者调用的是init_phy函数,后二者调用tsec_init函数

  init_phy函数抄自UBOOT:

            
static int init_phy(struct tsec_private *priv)
{
	struct phy_info *curphy;
	volatile tsec_t *regs = (volatile tsec_t *)(TSEC_BASE_ADDR);

	/* Assign a Physical address to the TBI */
	regs->tbipa = CFG_TBIPA_VALUE;
	regs = (volatile tsec_t *)(TSEC_BASE_ADDR + TSEC_SIZE);
	regs->tbipa = CFG_TBIPA_VALUE;
	asm("sync");

	/* Reset MII (due to new addresses) */
	priv->phyregs->miimcfg = MIIMCFG_RESET;
	asm("sync");

	priv->phyregs->miimcfg = MIIMCFG_INIT_VALUE;
	asm("sync");

	while (priv->phyregs->miimind & MIIMIND_BUSY) ;

	/* Get the cmd structure corresponding to the attached
	 * PHY */
	curphy = get_phy_info(priv);

	if (curphy == NULL) {
		priv->phyinfo = NULL;
		printk(KERN_ERR"%s: No PHY found\n", priv->name);

		return 0;
	}

	priv->phyinfo = curphy;

	phy_run_commands(priv, priv->phyinfo->config);

	return 1;
}

          int tsec_init(struct tsec_private *priv)调用init_registers初始化其他通用控制器(比如重要的ievent与imask),然后调用static                                        startup_tsec(struct tsec_private *priv)来进行缓冲描述符的初始化;

            
static void startup_tsec(struct tsec_private *priv)
{
	int i;
	volatile tsec_t *regs = priv->regs;

	/* Point to the buffer descriptors */
	regs->tbase = (unsigned int)(&rtx.txbd[txIdx]);
	regs->rbase = (unsigned int)(&rtx.rxbd[rxIdx]);

	/* Initialize the Rx Buffer descriptors */
	for (i = 0; i < PKTBUFSRX; i++) {
		rtx.rxbd[i].status = RXBD_EMPTY | RXBD_INTERRUPT;
		rtx.rxbd[i].length = 0;
		rtx.rxbd[i].bufPtr = (UINT32)NetRxPackets[i];
	}
	rtx.rxbd[PKTBUFSRX - 1].status |= RXBD_WRAP;             /*  BD的最后一个status要为回卷   */

	/* Initialize the TX Buffer Descriptors */
	for (i = 0; i < TX_BUF_CNT; i++) {
		rtx.txbd[i].status = TXBD_INTERRUPT;
		rtx.txbd[i].length = 0;
		rtx.txbd[i].bufPtr = 0;
	}
	rtx.txbd[TX_BUF_CNT - 1].status |= TXBD_WRAP;

	/* Start up the PHY */
	if(priv->phyinfo)
		phy_run_commands(priv, priv->phyinfo->startup);

	adjust_link(priv);

	/* Enable Transmit and Receive */
	regs->maccfg1 |= (MACCFG1_RX_EN | MACCFG1_TX_EN);

	/* Tell the DMA it is clear to go */
	regs->dmactrl |= DMACTRL_INIT_SETTINGS;
	regs->tstat = TSTAT_CLEAR_THALT;
	regs->rstat = RSTAT_CLEAR_RHALT;
	regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS);

	regs->imask=IMASK_RXFEN0 | IMASK_RXB0;/////先只要接收中断打开
}

             这里注意一个很重要的地方,我之前一直是失败的状态就是因为这里:
             rtx.rxbd[i].status = RXBD_EMPTY | RXBD_INTERRUPT;  //在uboot原始驱动里面是不使用中断的,所以不会有后面那个interrupt部分,必须加上。

2.3 记录网卡数据并设置网卡参数:

             
    pNetif->state = priv;                                               /*  记录网卡数据结构            */

#if LWIP_NETIF_HOSTNAME
    pNetif->hostname = HOSTNAME;                                        /*  设置主机名                  */
#endif /* LWIP_NETIF_HOSTNAME */

    pNetif->name[0] = IFNAME0;                                          /*  设置网卡名                  */
    pNetif->name[1] = IFNAME1;

    NETIF_INIT_SNMP(pNetif, snmp_ifType_ethernet_csmacd, 100000000);    /*  初始化 SNMP                 */

    pNetif->output     = etharp_output;                                 /*  安装驱动发送函数,上层函数,不用实现               */
    pNetif->linkoutput = __tsecEthTx;                                   /*  发包函数,需要自己写      */  
#if LWIP_IPV6 > 0
    pNetif->output_ip6 = ethip6_output;
#endif                                                                  /*  LWIP_IPV6 > 0 ,这个函数不用实现     */

    lib_memcpy(pNetif->hwaddr,
               priv->enetaddr,
               ETHARP_HWADDR_LEN);                                      /*  设置 MAC 地址               */

    pNetif->hwaddr_len = ETHARP_HWADDR_LEN;                             /*  设置 MAC 地址长度           */

    pNetif->mtu = 1500;                                                 /*  设置最大传输单元            */

    pNetif->flags = NETIF_FLAG_BROADCAST |
                    NETIF_FLAG_ETHARP    |
                    NETIF_FLAG_ETHERNET;    
      这个套路基本上是固定的。
      然后这里我为了保证网卡链路链接上,用了一个while循环,这可能不太对:
     
while (!priv->link) {                                               /*优先初始化网口链路*/
                phy_run_commands(priv, priv->phyinfo->startup);

                if (priv->link) {
                    adjust_link(priv);
                    pNetif->link_speed = priv->speed * 1000 * 1000;
                    netif_set_link_up(pNetif);
                    break;
                }
            }

  2.4 注册中断(这里我自己只注册了接收中断并使能)

              也就是说网卡驱动需要中断控制器驱动先写好的情况下才能完成,我之前已经写了中断控制器的驱动了,那个相对而言比较简单,就不再这里赘述,值得注意的是IPIC在MPC8377里面实际上中断屏蔽相关寄存器已经变成了SIMSR_H、SIMSR_L、SEMSR三个,注意根据中断号来对不同的屏蔽寄存器进行操作来使能禁能中断。

3. 中断响应函数receiveIsr:

          完成的功能:

         3.1 关中断,然后使用netJobAdd函数添加一个netTask进行收包:

        
if(pNetif!=NULL){
    	if (netJobAdd((VOIDFUNCPTR)tsec_recv,
    		pNetif,
    		0, 0, 0, 0, 0) == ERROR_NONE) {
    	            /*
    	             * 关闭接收中断
    	             */
    		regs->imask &= ~IMASK_RXFEN0;
    	}else{
    		printk(KERN_INFO"The netJobRing is full.\n");
    		regs->imask &= IMASK_RXFEN0;
    	}
    }else{
    	printk(KERN_INFO"receiveIsr:error irq\n");

    }
               tesc_recv的收包函数主要是调整字节,往上层传递包,不过值得注意的是在完成收包之后,一定要记住再次把缓冲描述符初始化,不然就没法再用了,我之前就是因为没有完成这个操作,所以导致后面一直是BSY、busy状态没法继续收包:
while (!(rtx.rxbd[rxIdx].status & RXBD_EMPTY)) {

               …………
               rtx.rxbd[rxIdx].length = 0;

             /* Set the wrap bit if this is the last element in the list */
               rtx.rxbd[rxIdx].status =
             RXBD_EMPTY | RXBD_INTERRUPT | (((rxIdx + 1) == PKTBUFSRX) ? RXBD_WRAP : 0);
             rxIdx = (rxIdx + 1) % PKTBUFSRX;///检查下一个缓冲描述符
}
                 同样要记住把intterrupt位设置,因为uboot里同样这里没有设置中断,我之前忘记设置了,搞了半天都没有收包中断,所以特别蛋疼。设置完之后记得把index设置到下一个位置再次进行检查。

                3.2 在ievent里写1清中断,并且设置imask再使能中断


4.用到的调试技巧:

没有MPC8377的调试器,所以调试只能通过打印的方式进行。打印过的主要信息是:
    中断控制器里收到的中断控制号(注意串口的不要打印,不然会输出很多很多……)
    收包函数里面打印包的信息,查看数据有没有收到。
   
打印ievent和imask寄存器值,这里可以使用SylixOS的系统API来安装一个控制台指令,这样就能比较方便的打一条指令就能查看寄存器的值了。     
API_TShellKeywordAdd("printEn", (PCOMMAND_START_ROUTINE)__tsecCallDebug); /*安装Debug指令*/
在_tsecCallDebug里面打印自己需要的寄存器的值。

如果嫌打指令麻烦,可以创建线程,但是不推荐,因为打印速度比较快,会看不清信息。建议还是用安装Debug指令的方式查看寄存器的值。

还可以用ints指令看看中断安装是否成功(看enable返回以及最后的count值有没有增加,如下图所示)




最后贴一下互ping图,哈哈哈,搞了将近3个多星期,在没有什么嵌入式基础的情况下,能搞完中断控制器驱动和把uboot的网卡驱动改成中断形式,我觉得虽然效率非常不理想,但是总算是有所收获了吧,还是比较高兴的。
版权声明:本文为博主原创文章,转载请写明来源。

SylixOS适配MPC8377网卡驱动(etsec)编写总结

经过这一个月的学习,总算勉勉强强在uboot的基础上改好了网卡驱动: 发送只用了一个包缓冲buffer,大小就是1536(MTU1500的情况下),接收区缓冲20个,每个1536字节。发送没有用中...
  • hahajinbu
  • hahajinbu
  • 2016年08月30日 11:44
  • 1029

eclipse如何调试system_server

打算开始Android系统学习,君预善其事,必先利其器。如果能够在eclipse中调试system_server, 对于理解Android系统服务能够起到很大的帮助. 在网上查了点资...
  • bluepeople1
  • bluepeople1
  • 2016年09月13日 11:19
  • 326

SylixOS龙芯1C适配总结

1.龙芯1C简介 1.1龙芯1C简介 龙芯 1C300(以下简称 1C)芯片是基于 LS232 处理器核的高性价比单芯片系统,可应用于指纹生物识别、物联传感等领域。1C 包含浮点处理单元,可以有效增强...
  • jj801238
  • jj801238
  • 2017年09月19日 10:37
  • 271

网卡驱动编写方法

在此仅仅讨论网络设备驱动的一般写法,有关硬件部分的相关代码由于硬件规格不同,予以省略。有什么地方错误,或补充,欢迎大家提出。  1, 驱动模块的加载和卸载 如果网络设备(包括wireless)是...
  • cupidove
  • cupidove
  • 2013年07月17日 10:01
  • 3211

使用lldb 和debugsever 调试程序

lldb 大家应该很熟悉,已经被苹果集成到xcode开发工具中,我们平时写程序的时候,调试程序经常会打断点,程序运行到断点的地方会自动停下来,然后在控制台中就可以使用lldb 的命令来进行调试。 但是...
  • woaizijiheni
  • woaizijiheni
  • 2016年04月22日 16:45
  • 2209

weblogic配置debug本地调试

一、在startWebLogic.cmd中加入 set JAVA_OPTIONS=%JAVA_OPTIONS% -Xdebug -Xnoagent -Djava.compiler=NONE   -...
  • guogang83
  • guogang83
  • 2012年12月06日 16:33
  • 3064

SylixOS网卡驱动实现篇

开发环境  操作系统:SylixOS  编程环境:RealEvo-IDE3.1  硬件平台:IMX6Q实验箱 技术实现 网卡驱动的收发功能,是通过管理收发描述符的方式实现的...
  • PCSean
  • PCSean
  • 2017年03月10日 11:35
  • 212

SylixOS 网卡驱动netdev_notify函数分析

/* if netdev detected a packet in netdev buffer, driver can call this function to receive this packe...
  • Ivan804638781
  • Ivan804638781
  • 2017年04月27日 11:30
  • 166

一次PHY驱动的调试有感

做为一个驱动开发工程师, 经常会碰到十分坑爹的问题. 如果很快解决了还好, 若是运气不好一直找不到思路, 那时候真是处理崩溃边缘. 有时候辛辛苦苦调试了几个月, 最后知道是硬件问题, 那时候真的是想骂...
  • shinezhang86
  • shinezhang86
  • 2015年09月09日 10:11
  • 1365

vxworks phy调试

1.  在BDdrv/*.c文件中编译target/config/mpc8377/vxbEtsecEnd.c#include "vxbEtsecEnd.c" STATUS myPhyRead ( V...
  • qingfengtsing
  • qingfengtsing
  • 2012年04月11日 21:09
  • 3022
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:SylixOS适配MPC8377网卡驱动(etsec)编写总结
举报原因:
原因补充:

(最多只允许输入30个字)