📄 if_dp83902a.c
字号:
#if DEBUG & 5
diag_printf("TX prep page %d len %d\n", start_page, pkt_len);
#endif
DP_OUT(base, DP_ISR, DP_ISR_RDC); // Clear end of DMA
// Set these first, following the small print of the manual
DP_OUT(base, DP_RBCL, 1);
DP_OUT(base, DP_CR, DP_CR_PAGE0 | DP_CR_RDMA | DP_CR_START);
#ifdef CYGHWR_NS_DP83902A_PLF_BROKEN_TX_DMA
// Stall for a bit before continuing to work around random data
// corruption problems on some platforms.
CYGACC_CALL_IF_DELAY_US(1);
#endif
// Send data to device buffer(s)
DP_OUT(base, DP_RSAL, 0);
DP_OUT(base, DP_RSAH, start_page);
DP_OUT(base, DP_RBCL, pkt_len & 0xFF);
DP_OUT(base, DP_RBCH, pkt_len >> 8);
DP_OUT(base, DP_CR, DP_CR_WDMA | DP_CR_START);
// 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 void
dp83902a_RxEvent(struct eth_drv_sc *sc, int stat)
{
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 == cur) {
CR_DOWN();
break;
}
if (pkt == DP_RX_STOP) pkt = DP_RX_START;
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) {
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);
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);
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 void
dp83902a_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);
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 void
dp83902a_TxEvent(struct eth_drv_sc *sc, int stat)
{
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);
}
static void
dp83902a_BufEvent(struct eth_drv_sc *sc, int stat)
{
// What to do if the receive buffers overflow?
if (stat & DP_ISR_OFLW) {
// Note: [so far] it seems safe to just ignore this condition
// The Linux driver goes through extraordinary pains to handle
// it, including totally shutting down the chip and restarting.
}
}
static void
dp83902a_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) {
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, isr);
}
if (isr & (DP_ISR_RxP|DP_ISR_RxE)) {
dp83902a_RxEvent(sc, isr);
}
if (isr & (DP_ISR_OFLW|DP_ISR_CNT)) {
dp83902a_BufEvent(sc, isr);
}
DP_IN(base, DP_ISR, isr);
}
CYGHWR_NS_DP83902A_PLF_INT_CLEAR(dp);
}
static int
dp83902a_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 + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -