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

来源:互联网 发布:专业ps软件 编辑:程序博客网 时间:2024/06/04 01:17

经过这一个月的学习,总算勉勉强强在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的网卡驱动改成中断形式,我觉得虽然效率非常不理想,但是总算是有所收获了吧,还是比较高兴的。
1 0