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 + -
显示快捷键?