if_dp83902a.c

来自「eCos操作系统源码」· C语言 代码 · 共 800 行 · 第 1/2 页

C
800
字号
    // Put data into buffer    for (i = 0;  i < sg_len;  i++) {        data = (unsigned char *)sg_list[i].buf;        len = sg_list[i].len;#if DEBUG & 4        diag_printf(" sg buf %08x len %08x\n ", data, len);        dx = 0;#endif        while (len > 0) {#ifdef CYGHWR_NS_DP83902A_PLF_16BIT_DATA            cyg_uint16 tmp;            tmp = *data++ << 8;            len -= 2;            if (len >= 0)                tmp |= *data++;            DP_OUT_DATA(dp->data, tmp);#if DEBUG & 4            diag_printf(" %04x", tmp);            if (0 == (++dx % 8)) diag_printf("\n ");#endif#else#if DEBUG & 4            diag_printf(" %02x", *data);            if (0 == (++dx % 16)) diag_printf("\n ");#endif            DP_OUT_DATA(dp->data, *data++);            len--;#endif        }#if DEBUG & 4        diag_printf("\n");#endif    }    if (total_len < pkt_len) {#if DEBUG & 4        diag_printf("  + %d bytes of padding\n", pkt_len - total_len);#endif        // Padding to 802.3 length was required        for (i = total_len;  i < pkt_len;) {#ifdef CYGHWR_NS_DP83902A_PLF_16BIT_DATA            i += 2;#else            i++;#endif            DP_OUT_DATA(dp->data, 0);        }    }#ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_TX_DMA    // After last data write, delay for a bit before accessing the    // device again, or we may get random data corruption in the last    // datum (on some platforms).    CYGACC_CALL_IF_DELAY_US(1);#endif    // Wait for DMA to complete    do {        DP_IN(base, DP_ISR, isr);    } while ((isr & DP_ISR_RDC) == 0);    // Then disable DMA    DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START);    CR_DOWN();    // Start transmit if not already going    if (!dp->tx_started) {        if (start_page == dp->tx1) {            dp->tx_int = 1;  // Expecting interrupt from BUF1        } else {            dp->tx_int = 2;  // Expecting interrupt from BUF2        }        dp83902a_start_xmit(sc, start_page, pkt_len);    }}//// This function is called when a packet has been received.  It's job is// to prepare to unload the packet from the hardware.  Once the length of// the packet is known, the upper layer of the driver can be told.  When// the upper layer is ready to unload the packet, the internal function// 'dp83902a_recv' will be called to actually fetch it from the hardware.//static voiddp83902a_RxEvent(struct eth_drv_sc *sc){    struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *)sc->driver_private;    cyg_uint8 *base = dp->base;    unsigned char rsr;    unsigned char rcv_hdr[4];    int i, len, pkt, cur;    DEBUG_FUNCTION();    DP_IN(base, DP_RSR, rsr);    while (true) {        CR_UP();        // Read incoming packet header        DP_OUT(base, DP_CR, DP_CR_PAGE1 | DP_CR_NODMA | DP_CR_START);        DP_IN(base, DP_P1_CURP, cur);        DP_OUT(base, DP_P1_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START);        DP_IN(base, DP_BNDRY, pkt);        pkt += 1;        if (pkt == dp->rx_buf_end)            pkt = dp->rx_buf_start;        if (pkt == cur) {            CR_DOWN();            break;        }        DP_OUT(base, DP_RBCL, sizeof(rcv_hdr));        DP_OUT(base, DP_RBCH, 0);        DP_OUT(base, DP_RSAL, 0);        DP_OUT(base, DP_RSAH, pkt);        if (dp->rx_next == pkt) {            if (cur == dp->rx_buf_start)                DP_OUT(base, DP_BNDRY, dp->rx_buf_end-1);            else                DP_OUT(base, DP_BNDRY, cur-1); // Update pointer            CR_DOWN();            return;        }        dp->rx_next = pkt;        DP_OUT(base, DP_ISR, DP_ISR_RDC); // Clear end of DMA        DP_OUT(base, DP_CR, DP_CR_RDMA | DP_CR_START);#ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_RX_DMA	CYGACC_CALL_IF_DELAY_US(10);#endif        for (i = 0;  i < sizeof(rcv_hdr);) {#ifdef CYGHWR_NS_DP83902A_PLF_16BIT_DATA            cyg_uint16 tmp;            DP_IN_DATA(dp->data, tmp);            rcv_hdr[i++] = (tmp >> 8) & 0xff;            rcv_hdr[i++] = tmp & 0xff;#else            DP_IN_DATA(dp->data, rcv_hdr[i++]);#endif        }        CR_DOWN();#if DEBUG & 5        diag_printf("rx hdr %02x %02x %02x %02x\n",                    rcv_hdr[0], rcv_hdr[1], rcv_hdr[2], rcv_hdr[3]);#endif        len = ((rcv_hdr[3] << 8) | rcv_hdr[2]) - sizeof(rcv_hdr);        (sc->funs->eth_drv->recv)(sc, len);        if (rcv_hdr[1] == dp->rx_buf_start)            DP_OUT(base, DP_BNDRY, dp->rx_buf_end-1);        else             DP_OUT(base, DP_BNDRY, rcv_hdr[1]-1); // Update pointer    }}//// This function is called as a result of the "eth_drv_recv()" call above.// It's job is to actually fetch data for a packet from the hardware once// memory buffers have been allocated for the packet.  Note that the buffers// may come in pieces, using a scatter-gather list.  This allows for more// efficient processing in the upper layers of the stack.//static voiddp83902a_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len){    struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *)sc->driver_private;    cyg_uint8 *base = dp->base;    int i, mlen, len;    unsigned char *data;    cyg_uint8 saved_char = 0;    bool saved;#if DEBUG & 4    int dx;#endif    DEBUG_FUNCTION();    // Compute total packet length    len = 0;    for (i = 0;  i < sg_len;  i++) {        len += sg_list[i].len;    }#if DEBUG & 5    diag_printf("Rx packet %d length %d\n", dp->rx_next, len);#endif    CR_UP();    // Read incoming packet data    DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_NODMA | DP_CR_START);    DP_OUT(base, DP_RBCL, len & 0xFF);    DP_OUT(base, DP_RBCH, len >> 8);    DP_OUT(base, DP_RSAL, 4);           // Past header    DP_OUT(base, DP_RSAH, dp->rx_next);    DP_OUT(base, DP_ISR, DP_ISR_RDC); // Clear end of DMA    DP_OUT(base, DP_CR, DP_CR_RDMA | DP_CR_START);#ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_RX_DMA    CYGACC_CALL_IF_DELAY_US(10);#endif    saved = false;    for (i = 0;  i < sg_len;  i++) {        data = (unsigned char *)sg_list[i].buf;        if (data) {            mlen = sg_list[i].len;#if DEBUG & 4            diag_printf(" sg buf %08x len %08x \n", data, mlen);            dx = 0;#endif            while (0 < mlen) {                // Saved byte from previous loop?                if (saved) {                    *data++ = saved_char;                    mlen--;                    saved = false;                    continue;                }#ifdef CYGHWR_NS_DP83902A_PLF_16BIT_DATA                {                    cyg_uint16 tmp;                    DP_IN_DATA(dp->data, tmp);#if DEBUG & 4                    diag_printf(" %04x", tmp);                    if (0 == (++dx % 8)) diag_printf("\n ");#endif                    *data++ = (tmp >> 8) & 0xff;                    mlen--;                    if (0 == mlen) {                        saved_char = tmp & 0xff;                        saved = true;                    } else {                        *data++ = tmp & 0xff;                        mlen--;                    }                }#else                {                    cyg_uint8 tmp;                    DP_IN_DATA(dp->data, tmp);#if DEBUG & 4                    diag_printf(" %02x", tmp);                    if (0 == (++dx % 16)) diag_printf("\n ");#endif                    *data++ = tmp;;                    mlen--;                }#endif            }#if DEBUG & 4            diag_printf("\n");#endif        }    }    CR_DOWN();}static voiddp83902a_TxEvent(struct eth_drv_sc *sc){    struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *)sc->driver_private;    cyg_uint8 *base = dp->base;    unsigned char tsr;    unsigned long key;    DEBUG_FUNCTION();    DP_IN(base, DP_TSR, tsr);    if (dp->tx_int == 1) {        key = dp->tx1_key;        dp->tx1 = 0;    } else {        key = dp->tx2_key;        dp->tx2 = 0;    }    // Start next packet if one is ready    dp->tx_started = false;    if (dp->tx1) {        dp83902a_start_xmit(sc, dp->tx1, dp->tx1_len);        dp->tx_int = 1;    } else if (dp->tx2) {        dp83902a_start_xmit(sc, dp->tx2, dp->tx2_len);        dp->tx_int = 2;    } else {        dp->tx_int = 0;    }    // Tell higher level we sent this packet    (sc->funs->eth_drv->tx_done)(sc, key, 0);}// Read the tally counters to clear them.  Called in response to a CNT// interrupt.static voiddp83902a_ClearCounters(struct eth_drv_sc *sc){    struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *)sc->driver_private;    cyg_uint8 *base = dp->base;    cyg_uint8 cnt1, cnt2, cnt3;    DP_IN(base, DP_FER, cnt1);    DP_IN(base, DP_CER, cnt2);    DP_IN(base, DP_MISSED, cnt3);    DP_OUT(base, DP_ISR, DP_ISR_CNT);}// Deal with an overflow condition.  This code follows the procedure set// out in section 7.0 of the datasheet.static voiddp83902a_Overflow(struct eth_drv_sc *sc){    struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *)sc->driver_private;    cyg_uint8 *base = dp->base;    cyg_uint8 isr;    // Issue a stop command and wait 1.6ms for it to complete.    CR_UP();    DP_OUT(base, DP_CR, DP_CR_STOP | DP_CR_NODMA);    CYGACC_CALL_IF_DELAY_US(1600);    // Clear the remote byte counter registers.    DP_OUT(base, DP_RBCL, 0);    DP_OUT(base, DP_RBCH, 0);    // Enter loopback mode while we clear the buffer.    DP_OUT(base, DP_TCR, DP_TCR_LOCAL);     DP_OUT(base, DP_CR, DP_CR_START | DP_CR_NODMA);    CR_DOWN();    // Read in as many packets as we can and acknowledge any and receive    // interrupts.  Since the buffer has overflowed, a receive event of    // some kind will have occured.    dp83902a_RxEvent(sc);    DP_OUT(base, DP_ISR, DP_ISR_RxP|DP_ISR_RxE);    // Clear the overflow condition and leave loopback mode.    DP_OUT(base, DP_ISR, DP_ISR_OFLW);    DP_OUT(base, DP_TCR, DP_TCR_NORMAL);    // If a transmit command was issued, but no transmit event has occured,    // restart it here.    DP_IN(base, DP_ISR, isr);    if (dp->tx_started && !(isr & (DP_ISR_TxP|DP_ISR_TxE))) {        CR_UP();        DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_TXPKT | DP_CR_START);        CR_DOWN();    }}static voiddp83902a_poll(struct eth_drv_sc *sc){    struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *)sc->driver_private;    cyg_uint8 *base = dp->base;    unsigned char isr;//    DEBUG_FUNCTION();    CR_UP();    DP_OUT(base, DP_CR, DP_CR_NODMA | DP_CR_PAGE0 | DP_CR_START);    CR_DOWN();    DP_IN(base, DP_ISR, isr);    while (0 != isr) {        // The CNT interrupt triggers when the MSB of one of the error        // counters is set.  We don't much care about these counters, but        // we should read their values to reset them.        if (isr & DP_ISR_CNT) {            dp83902a_ClearCounters(sc);        }        // Check for overflow.  It's a special case, since there's a        // particular procedure that must be followed to get back into        // a running state.        if (isr & DP_ISR_OFLW) {            dp83902a_Overflow(sc);        } else {            // Other kinds of interrupts can be acknowledged simply by            // clearing the relevant bits of the ISR.  Do that now, then            // handle the interrupts we care about.            DP_OUT(base, DP_ISR, isr);      // Clear set bits            if (!dp->running) break;        // Is this necessary?            // Check for tx_started on TX event since these may happen            // spuriously it seems.            if (isr & (DP_ISR_TxP|DP_ISR_TxE) && dp->tx_started) {                dp83902a_TxEvent(sc);            }            if (isr & (DP_ISR_RxP|DP_ISR_RxE)) {                dp83902a_RxEvent(sc);            }        }        DP_IN(base, DP_ISR, isr);    }    CYGHWR_NS_DP83902A_PLF_INT_CLEAR(dp);}static intdp83902a_int_vector(struct eth_drv_sc *sc){    struct dp83902a_priv_data *dp = (struct dp83902a_priv_data *)sc->driver_private;    return dp->interrupt;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?