RTEMS 4.9.5:QEMU MINI2440 BSP 中的网络驱动开发(下)

(原创文章,转载请注明出处,谢谢。)

 驱动编译运行,呵呵,跑起来了,欣喜之余,要看看还有什么问题没有解决,还有什么吸取的经验。首先:

1.DM9000的特性没有完全支持;
    * 没有操作DM9000的eeprom部分;
    * 没有调试dm9000的phy部分;
    * dm9000支持发送两个队列,我们只使用了一个;
    * dm9000发送采取的是查询策略,而非中断策略。效率低下。

2.QEMU 仿真 DM9000 有些问题,尚未确认。

3.延时函数。

我们一个问题一个问题说:

DM9000的EEPROM的支持问题,由于不影响大局可以暂时放一样,但是听rickleaf和我交流时,所这一块仿真时有问题。即在QEMU MINI2440 上不能正常工作,目前还不能确认是QEMU的问题还是驱动本省的问题,这个问题姑且放一放。

DM9000的内部的PHY和DM9000的MAC层已经无缝的结合在一起,两个自动协商,不要对其做匹配,即MAC工作在100Mbps下,phy肯定也工作在100Mbps下,MAC工作在全双工下,phy也工作在全双工下。这不用烦心。我实际的读过phy的寄存器,不是很成功,可以考虑是QEMU的仿真问题。

DM9000支持的两个发送队列的问题,我写得驱动只用了一个,没有使用第二个。这个问题,动手做过实验。发现程序并不稳定,我参考了Linux的驱动,在QEMU上仿真时,一段时候后就完蛋了。由于没有实际的开发板验证,实在是不知道是代码自身的问题,还是QEMU的问题。以下是改成双缓冲的 发送代码,在 dm9000_softc_t中增加了两个字段,txPktCnt,用于表示当前队列中发送包的数量。txPktLen表示队列中第二包的长度。

/* Send packet */
void dm9000_sendpacket(dm9000_softc_t *sc, struct mbuf *m)
{
    unsigned int length = 0;
    rtems_interval tmo; /* Move data to DM9000 TX RAM */
    DM9000_outb(DM9000_MWCMD, DM9000_IO);/* Prepare for TX-data */
    length = (sc->outmbuf)(m); m_freem(m); /* Move data to DM9000 TX RAM */
    DM9000_outb(DM9000_MWCMD, DM9000_IO); /* Prepare for TX-data */
    /* push the data to the TX-fifo */
    (sc->outblk)(packet, length);
    sc->txPktCnt++;
    if (sc->txPktCnt == 1)
    {
        /* Set TX length to DM9000 */
        DM9000_iow(DM9000_TXPLL, length & 0xff);
        DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff);
        /* Issue TX polling command */
        DM9000_iow(DM9000_TCR, TCR_TXREQ);/* Cleared after TX complete */
    }
    else
    {
        sc->txPktLen = length; /* wait for end of transmission */
        tmo = rtems_clock_get_ticks_since_boot() + 5 * rtems_clock_get_ticks_per_second();
        while ( !(DM9000_ior(DM9000_NSR) & (NSR_TX1END | NSR_TX2END)) ||
            !(DM9000_ior(DM9000_ISR) & IMR_PTM) )
        {
            if (rtems_clock_get_ticks_since_boot() >= tmo)
            {
                printf("transmission timeout/n");
                DM9000_iow(DM9000_ISR, IMR_PTM);
                return;
            }
            /*rtems_task_wake_after(0); *//*yeild CPU to other task*/
        }
        DM9000_iow(DM9000_ISR, IMR_PTM);
        /* Clear Tx bit in ISR */
        sc->txPktCnt --;
        if (sc->txPktCnt > 0)
        {
            /* Set TX length to DM9000 */
            DM9000_iow(DM9000_TXPLL, (sc->txPktLen) & 0xff);
            DM9000_iow(DM9000_TXPLH, ((sc->txPktLen) >> 8) & 0xff);
            /* Issue TX polling command */
            DM9000_iow(DM9000_TCR, TCR_TXREQ);
            /* Cleared after TX complete */
        }
    }
    DM9000_DBG("transmit done/n/n");
}

 

关于发送采用中断模式也是有同样的问题,我也做了实验,在QEMU上运行时也有不稳定的问题,但代码层面我的确找不到问题了。请高手们多多指正。我改来改去,发现查询方式最稳定,呵呵,那就用查询方式发布吧。

/* * Driver transmit daemon */
void dm9000_txDaemon (void *arg)
{
    dm9000_softc_t *sc = (dm9000_softc_t *)arg;
    struct ifnet *ifp = &sc->arpcom.ac_if;
    struct mbuf *m; rtems_event_set events;
    for (;;)
    {
        /* turn on TX interrupt, then wait for one */
        rtems_bsdnet_event_receive(
            START_TRANSMIT_EVENT,
            RTEMS_EVENT_ANY | RTEMS_WAIT,
            RTEMS_NO_TIMEOUT,
            &events);
        /* Get the next mbuf chain to transmit. */
        IF_DEQUEUE(&ifp->if_snd, m);
        if (!m)
        {
            ifp->if_flags &= ~IFF_OACTIVE;
            continue;
        }
        dm9000_sendpacket(sc, m);
    }
}

/* Send packet */
void dm9000_sendpacket(dm9000_softc_t *sc, struct mbuf *m)
{
    unsigned int length = 0;
    /* Move data to DM9000 TX RAM */
    DM9000_outb(DM9000_MWCMD, DM9000_IO);
    /* Prepare for TX-data */
    length = (sc->outmbuf)(m);
    m_freem(m);
    /* Set TX length to DM9000 */
    DM9000_iow(DM9000_TXPLL, length & 0xff);
    DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff);
    /* Issue TX polling command */
    DM9000_iow(DM9000_TCR, TCR_TXREQ);
    /* Cleared after TX complete */
}

/* interrupt handler */
rtems_isr dm9000_isr (rtems_vector_number v)
{
    int status;
#if !defined(CONFIG_NET_POLL_CONTROLLER)
    rEINTMASK |= 0x80;
#endif
    DM9000_iow(DM9000_IMR, IMR_PAR);
    status = DM9000_ior(DM9000_ISR);
    DM9000_iow(DM9000_ISR, status);
    if ((status & ISR_PRS))
    {
        rtems_event_send (softc.rxDaemonTid, START_RECEIVE_EVENT);
    }
    if ((status & ISR_PTS))
    {
        int txstat;
        txstat = DM9000_ior(DM9000_NSR);
        if (txstat & (NSR_TX1END | NSR_TX2END))
            rtems_event_send (softc.txDaemonTid, START_TRANSMIT_EVENT);
    }
    DM9000_iow(DM9000_IMR, IMR_PAR | IMR_PRM | IMR_PTM);
#if !defined(CONFIG_NET_POLL_CONTROLLER)
    rEINTPEND = 0x80;
    ClearPending(BIT_EINT4_7);
    rEINTMASK &= ~0x80;
#endif
}

最有关于一个udelay函数的问题,

udelay函数RTEMS没有提供该函数的实现,需要自己撰写。我就偷懒写了个for 循环的延迟。这样做是不对的。由于没有实际的硬件,QEMU执行ARM的代码完全不像实际那样。所以靠指令实现精确的1us的延迟,是不太可能。实际中使用应采用定时器精确测量多少条循环指令可以实现1us的延迟。这样才能做出较好的udelay来。


下面和大家深入地讨论一些问题:

查看代码,发现 mbuf 的相关宏 MGETHDR、MGET 使用 splimp()、splimp、splx()三个函数保护其临界区域。然而,在 rtems_bsdnet_internal.h 中这三个的宏定义为:

#define splnet()    0
#define splimp()    0

#define splx(_s)    do { (_s) = 0; } while(0)

 

都是为空的。并且网络中的许多操作都是依靠这几个锁来实现临界区域的保护。这三个宏这么定义,显然什么保护都没有。照理说,不保护,那么在多任务情况下,那是得不到正确结果的,然而我们实际使用中却能得到正确结果,这又是为什么呢?

 阅读RTEMS官方的文档  networking.pdf 可以找到答案。他们提供了一个 rtems_bsdnet_semaphore_obtain() 和 rtems_bsdnet_semaphore_release() 进行这个级别的互斥。用于解决网络内部的数据结构一致性的问题。

 

这里还有最后问题:为什么要这样设计呢?这样设计的优缺点在哪里?

这个问题,我想了很久,这个答案可能会仁者见仁智者见智,姑且抛砖引玉吧。这么设计首先不像uC/OS-II这样的操作系统也不像Linux这样的操作系统,动不动就关中断,我们都知道 RTOS 的最重要的一个性能指标就是实时性。其中对中断的响应延迟时间是个非常重要的指标。RTEMS这样的设计,最明显的就是不用关中断,即使关了中断,时间也非常的短,直接的好处就是响应中断延迟小。自然实时性就会好。

 

这么设计的优点就是实时性好。除去这个优点,我们看看缺点在哪里。首先,网络的接收、发送、协议栈的运转是三个任务。他们之间不能同时运行。这会对网络的性能有比较大的影响,尤其是我们的系统中有1个以上的网络驱动,假设有两个网口,那么就有5个任务。5个任务抢占调度,在一个网口有大数据量发送时,必然会影响第二个网口的性能。当然这也不是一成不变的,如果MAC支持DMA链,可以在DMA链上多放一些缓冲区,可以对网络瞬时来的数据做一定的缓冲。对性能也会有比较大的改善。但仅对于DM9000这种没有DMA的芯片来说,对性能多多少少还是有些影响吧,特别是对突发访问有影响。(我手头没有条件做测试,不然肯定会做个实验,在实际板子上验证我的思路。)

 

讲到这里,我想起来了,看到Linux里DM9000发送时,使用两个发送缓冲区,发送采用中断触发。看看他的临界区域的保护,两个开关中断的自旋锁之间竟然放着数据包的拷贝过程(蓝色字体标识的)。这个在实时操作系统中是难以想象的,关中断时间长不说,占用总线资源时间也长。这对RTOS来说是个噩梦。所以我考虑来考虑去还是用了查询方法,也就是基于实时性的考虑,虽然那样写在实际中DM9000的工作效率并不高。但可以使用一个队列元素,采用中断触发,作为一些性能改善。如果一些朋友有更好的建议,更好的解释和方法,恳请一定赐教!!!谢谢。

 

 

static int
dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    unsigned long flags;
    board_info_t *db = netdev_priv(dev);

    dm9000_dbg(db, 3, "%s:/n", __func__);

    if (db->tx_pkt_cnt > 1)
        return NETDEV_TX_BUSY;

    spin_lock_irqsave(&db->lock, flags);

    /* Move data to DM9000 TX RAM */
    writeb(DM9000_MWCMD, db->io_addr);

    (db->outblk)(db->io_data, skb->data, skb->len);
    dev->stats.tx_bytes += skb->len;

    db->tx_pkt_cnt++;
    /* TX control: First packet immediately send, second packet queue */
    if (db->tx_pkt_cnt == 1) {
        /* Set TX length to DM9000 */
        iow(db, DM9000_TXPLL, skb->len);
        iow(db, DM9000_TXPLH, skb->len >> 8);

        /* Issue TX polling command */
        iow(db, DM9000_TCR, TCR_TXREQ);    /* Cleared after TX complete */

        dev->trans_start = jiffies;    /* save the time stamp */
    } else {
        /* Second packet */
        db->queue_pkt_len = skb->len;
        netif_stop_queue(dev);
    }

    spin_unlock_irqrestore(&db->lock, flags);

    /* free this SKB */
    dev_kfree_skb(skb);

    return 0;
}

 

(原创文章,转载请注明出处,谢谢。)

你可能感兴趣的:(工作,网络,struct,Semaphore,command,任务)