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

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

dm9000_txDaemon 任务:

 

 

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);

        /* Send packets till queue is empty */
        for (;;)
        {
            /* Get the next mbuf chain to transmit. */
            IF_DEQUEUE(&ifp->if_snd, m);
            if (!m)
                break;
            dm9000_sendpacket(sc, m);
        }
        ifp->if_flags &= ~IFF_OACTIVE;
    }
}

 

 

/* Send packet */
void dm9000_sendpacket(dm9000_softc_t *sc, struct mbuf *m)
{
    unsigned int length = 0;
    rtems_interval tmo;

     /*

整个发送任务没有什么好解释的,但我想解释一下这一段代码:

蓝色的代码,很显然,数据被Copy了两次。一次是从mbuf中拷贝到packet中,第二次是由packet拷贝到dm9000中。由于协议包的数据分布在mbuf所定义的链表中,不是一个连续的包,必须组合以后才可以输出到dm9000中。但是有没有别的好办法,可以减少一次Copy过程呢?办法是有的,那就是想办法,把两个不连续的指针所指向的数据接合起来。这个问题其实比较复杂,要分三种情况,DM9000总线分为三种工作模式:8 bit 、16bit 、32bit。三种情况下的操作都不一样。 详情请看下面的注释:

     */


#if 0
    struct mbuf *l;
    static uint8_t packet[2000];

    /* copy the mbuf chain into the transmit buffer */
    l = m;
    while (l != NULL) {
        memcpy(((char *)packet + length),  /* offset into pkt for mbuf */
               (char *)mtod(l, void *),       /* cast to void */
               l->m_len);                     /* length of this mbuf */

        length += l->m_len;               /* update offset */
        l = l->m_next;                        /* get next mbuf, if any */    
    }

    /* free the mbuf chain we just copied */
    m_freem(m);

    DM9000_DMP_PACKET("eth_send", packet, length);

    /* 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);

#else
    /* Move data to DM9000 TX RAM */
    DM9000_outb(DM9000_MWCMD, DM9000_IO); /* Prepare for TX-data */

   

 

    /* 依据DM9000的工作模式

        oubmbuf指针可能指向三个函数: dm9000_outmbuf_8bit、 dm9000_outmbuf_16bit、 dm9000_outmbuf_32bit

   */
    length = (sc->outmbuf)(m);

    m_freem(m);

#endif

    /* 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 */

    /* 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");
            break;
        }
    }
    DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */

    DM9000_DBG("transmit done/n/n");
}

 

/*

这三个函数即是将mbuf输出到dm9000中的拷贝过程。其中8bit比较简单,因为一个字节copy不需要考虑一些特殊问题。

 

但是16bit 就没那么幸运了,至少要考虑以下情况:

假设mbuf链表是由5个元素组成,依次所包含的数据长度是:

14 21 56 0 0

可以看到,14个字节,刚好可以组成7个双字(16bit)输出到dm9000里。

下面21只能组成10个双字,还余下一个字节,需要和下面的数据块组成双字输出到DM9000里。

本来56个字节自己刚好能组成28个双字的,现在只能组成27个,因为上面借去了一个。余下最后一个字节,单独的输出到DM9000里。

更特殊一点的情况:

14 21 0 56 0

当21向后面一个借字节时,发现是空的。只能再往下借了。

 

32Bit 的就更加复杂了,但是分析过程如同16bit。我没有编写相关的代码,图省事,就Copy了两次。主要是DM9000在mini2440上的工作模式,我配置成了16Bit。

*/

static uint32_t dm9000_outmbuf_8bit(struct mbuf *m)
{
    struct mbuf *l;
    uint32_t length = 0;

    l = m;
    while (l != NULL)
    {
        if (l->m_len > 0)
            dm9000_outblk_8bit(mtod(l, void *), l->m_len);

        length += l->m_len;               /* update offset */
        l = l->m_next;                        /* get next mbuf, if any */    
    }
    return (length);
}


static uint32_t dm9000_outmbuf_16bit(struct mbuf *m)
{
    uint32_t offset = 0;
    uint32_t len;
    uint8_t data[2];
    uint8_t *ptr;
    uint32_t length = 0;
    struct mbuf *l;

    l = m;
    while (l != NULL)
    {
        if (l->m_len > 0)
        {
            ptr = ((uint8_t *)mtod(l, void *));
            len = l->m_len - offset;

            if (offset == 1)
            {
                data[1] = *ptr;
                dm9000_outblk_16bit(data, 2);
            }

            dm9000_outblk_16bit((ptr + offset), (len & 0xFFFFFFFE));
            offset = len & 0x1;

            if (offset)
            {
                data[0] = *(ptr + l->m_len - 1);
            }
        }
        length += l->m_len;
        l = l->m_next;
    }

    if (offset)
        dm9000_outblk_16bit(data, 1);

    return (length);
}


static uint32_t dm9000_outmbuf_32bit(struct mbuf *m)
{
    struct mbuf *l;
    uint32_t length = 0;
    uint8_t packet[2000];

    l = m;
    while (l != NULL) {
        memcpy(((char *)packet + length),  /* offset into pkt for mbuf */
               (char *)mtod(l, void *),       /* cast to void */
               l->m_len);                     /* length of this mbuf */

        length += l->m_len;               /* update offset */
        l = l->m_next;                        /* get next mbuf, if any */    
    }
    dm9000_outblk_32bit(packet, length);
    return (length);
}


 

dm9000_rxDaemon 函数:

 

/* dm9000 reader task */
void dm9000_rxDaemon(void *arg)
{
    dm9000_softc_t *sc = (dm9000_softc_t *)arg;
    struct ifnet *ifp = &sc->arpcom.ac_if;
    struct mbuf *m;
    struct ether_header *eh;
    rtems_event_set events;
    uint8_t rxbyte;
    uint16_t RxStatus, RxLen = 0;

    /* Input packet handling loop */
    for (;;) {
        rtems_bsdnet_event_receive(
            START_RECEIVE_EVENT,
            RTEMS_EVENT_ANY | RTEMS_WAIT,
            RTEMS_NO_TIMEOUT,
            &events);

        for (;;) {

             /*申请mbuf*/
             /* get an mbuf this packet */
            MGETHDR(m, M_WAIT, MT_DATA);

            /* now get a cluster pointed to by the mbuf */
            /* since an mbuf by itself is too small */
            MCLGET(m, M_WAIT);

            /* set the type of mbuf to ifp (ethernet I/F) */
            m->m_pkthdr.rcvif = ifp;
            m->m_nextpkt = 0;

            DM9000_ior(DM9000_MRCMDX);  /* Dummy read */

            /* Get most updated data,
                only look at bits 0:1, See application notes DM9000 */
            rxbyte = DM9000_inb(DM9000_DATA) & 0x03;

            /* Status check: this byte must be 0 or 1 */
            if (rxbyte > DM9000_PKT_RDY) {
                DM9000_iow(DM9000_RCR, 0x00);   /* Stop Device */
                DM9000_iow(DM9000_ISR, 0x80);   /* Stop INT request */
                printf("DM9000 error: status check fail: 0x%x/n",
                    rxbyte);
                m_freem(m);

                /*错误恢复*/
                rtems_task_wake_after(2 * rtems_clock_get_ticks_per_second());
                dm9000_init_hw(sc);
                break;
            }

            if (rxbyte != DM9000_PKT_RDY)
            {
                m_freem(m);
                break; /* No packet received, ignore */
            }
            DM9000_DBG("receiving packet/n");

            DM9000_outb(DM9000_MWCMD, DM9000_IO);
            /* A packet ready now  & Get status/length */
            (sc->rx_status)(&RxStatus, &RxLen);

            DM9000_DBG("rx status: 0x%04x rx len: %d/n", RxStatus, RxLen);

            if ((RxStatus & 0xbf00) || (RxLen < 0x40)
                || (RxLen > DM9000_PKT_MAX)) {
                if (RxStatus & 0x100) {
                    printf("rx fifo error/n");
                }
                if (RxStatus & 0x200) {
                    printf("rx crc error/n");
                }
                if (RxStatus & 0x8000) {
                    printf("rx length error/n");
                }
                if (RxLen > DM9000_PKT_MAX) {
                    printf("rx length too big/n");
                    /*dm9000_reset();*/

                    /*错误恢复*/
                    dm9000_init_hw(sc);
                }
                m_freem(m);
            } else {
                /* Move data from DM9000 */
                /* Read received packet from RX SRAM */
                (sc->inblk)(m->m_ext.ext_buf, RxLen); /*接收数据包*/
                DM9000_DMP_PACKET("eth_rx", rdptr, RxLen);

                DM9000_DBG("passing packet to upper layer/n");

                /* set the length of the mbuf */
                m->m_len = RxLen - (sizeof(struct ether_header) + 4);/*移动到数据头位置*/
                m->m_pkthdr.len = m->m_len;

                /* strip off the ethernet header from the mbuf */
                /* but save the pointer to it */
                eh = mtod (m, struct ether_header *);
                m->m_data += sizeof(struct ether_header);
                ether_input(ifp, eh, m);/*输出到以太网协议栈内*/
            }
        }
    } /* for (;;) */
}

 

 

dm9000_ioctl 函数:

/*  Driver ioctl handler */
static int
dm9000_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
{
    dm9000_softc_t *sc = ifp->if_softc;
    int error = 0;

    switch (command) {
    case SIOCGIFADDR:
    case SIOCSIFADDR:
        ether_ioctl (ifp, command, data); /*定式,设置获取IF的地址。让协议栈去做 */
        break;

    case SIOCSIFFLAGS:
        switch (ifp->if_flags & (IFF_UP | IFF_RUNNING))/*启动停止驱动*/
        {
        case IFF_RUNNING:
            dm9000_stop (sc);
            break;

        case IFF_UP:
            dm9000_init (sc);
            break;

        case IFF_UP | IFF_RUNNING:
            dm9000_stop (sc);
            dm9000_init (sc);
            break;

        default:
            break;
        } /* switch (if_flags) */
        break;

    case SIO_RTEMS_SHOW_STATS:/*打印驱动的一些统计数据,这里我偷懒了,没有统计任何数据,其实,大家可以把中断的次数、接收到的数据包、发送的数据包等等都统计下来,可以用作调试*/
        dm9000_stats (sc);
        break;

        /*
         * FIXME: All sorts of multicast commands need to be added here!
         */
    default:
        error = EINVAL;
        break;
    } /* switch (command) */
    return error;
}

 


驱动程序中断相关函数

驱动程序中断相关函数相对比较简单,实在没啥可讲的。就贴出来,占一些篇幅吧。

 

static void dm9000_isr_on(const rtems_irq_connect_data *unused)
{

    rEXTINT0 = 0x12222222;
    /*Initialize the interrupt, mini2440 eint7*/
    /*eint4_7 use irq mode*/
    rINTMOD &= (~BIT_EINT4_7);
    /*enable eint7, io -> eint7*/
    rGPFCON = ((rGPFCON & 0x3FFF) | (0x2 << 14));
    /*enable eint7*/
    rEINTMASK &= (~0x80);
    /*enable eint4-7*/
    rINTMSK &= (~BIT_EINT4_7);
    return;
}


static void dm9000_isr_off(const rtems_irq_connect_data *unused)
{
    /* disable all various TX/RX interrupts */
    rINTMSK |= BIT_EINT4_7;
    rEINTMASK |= 0x80;
    return;
}

/* Tests to see if dm9000 interrupts are enabled, and
 * returns non-0 if so.
 * If interrupt is not enabled, returns 0.
 */
static int dm9000_isr_is_on(const rtems_irq_connect_data *irq)
{
    return ((rINTMSK & BIT_EINT4_7) == 0)? 1:0; /* any interrupts enabled? */
}

 

/* 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);
    }


    DM9000_iow(DM9000_IMR, IMR_PAR | IMR_PRM);
#if !defined(CONFIG_NET_POLL_CONTROLLER)
    rEINTPEND = 0x80;
    ClearPending(BIT_EINT4_7);
    rEINTMASK &= ~0x80;
#endif
}

 

 

至此,关键的地方我们都说到了,下一篇,我们总结一下这个驱动。并点评一下要点。

 

 

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

你可能感兴趣的:(网络,struct,command,header,null,events)