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网卡驱动实现篇

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

SylixOS网卡驱动调用篇

SylixOS网卡驱动调用篇本Markdown编辑器使用[StackEdit][6]修改而来,用它写博客,将会带来全新的体验哦: Markdown和扩展Markdown简洁的语法 代码块高亮 图片链接...
  • PCSean
  • PCSean
  • 2017年04月11日 10:17
  • 142

安装Broadcom Linux hybrid 无线网卡驱动总结

安装Broadcom Linux hybrid 无线网卡驱动总结 系统:centos6.6 驱动:hybrid-portsrc_x86_32-v5_100_82_112.tar.gz   ...

wince下DM9000A网卡驱动移植及学习总结---4

6.       CQueue(队列)类分析 与CQueue类紧密相关的是队列header结构体:PCQUEUE_GEN_HEADER,原型如下: typedef struct      _C...
  • yamafe
  • yamafe
  • 2013年07月24日 22:33
  • 528

wince下DM9000A网卡驱动移植及学习总结---3

接上次继续:     3.       初始化完成后,网卡将可以收发数据了。最先执行的是MiniportQueryInformation函数: NDIS_STATUS MiniportQue...
  • yamafe
  • yamafe
  • 2013年07月24日 22:31
  • 623

Linux网卡驱动程序编写

Linux网卡驱动程序编写 工作需要写了我们公司一块网卡的Linux驱动程序。经历一个从无到有的过程,深感技术交流的重要。Linux作为挑战微软垄断的强有力武器,日益受到大家的喜爱。真希望她能在...

wince下DM9000A网卡驱动移植及学习总结---2

下面我将详细分析整个网卡驱动。 1.       Driver.cpp中有函数入口:DriverEntry,初始化一个Miniport Driver时该函数会被第一个调用,用来注册一个Miniport...
  • yamafe
  • yamafe
  • 2013年07月13日 23:52
  • 1112

网卡驱动编写必读-重要概念 分享

一、什么是网卡?     网卡现在已经上成为了目前电脑里的标准配置之一。小小的网卡,究竟蕴涵着多少秘密呢?让我们一起来看。     我们最常用的网络设备当属网卡了。网卡本身是LAN(局域网...

wince下DM9000A网卡驱动移植及学习总结---1

wince下DM9000A驱动移植学习总结 函数层次调用关系:     首先,对src\Dm9000a文件夹下的文件做一个说明:     Driver.cpp文件中主要是驱动入口DriverEntry...
  • yamafe
  • yamafe
  • 2013年07月13日 23:19
  • 626

网卡驱动源代码及总结

  • 2009年12月13日 10:31
  • 70KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:SylixOS适配MPC8377网卡驱动(etsec)编写总结
举报原因:
原因补充:

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