if_cs8900a.c

来自「开放源码实时操作系统源码.」· C语言 代码 · 共 751 行 · 第 1/2 页

C
751
字号
                esa[3], esa[4], esa[5] );
#if !defined(CYGSEM_DEVS_ETH_CL_CS8900A_WRITE_EEPROM) || !defined(CS8900A_PROGRAM_EEPROM)
        diag_printf("*** PERMANENT EEPROM WRITE NOT ENABLED ***\n");
#endif
#endif // DEBUG

        // We can write the MAC address into the interface info,
        // and the chip registers no problem.
        for ( i = 0; i < sizeof(cpd->esa);  i++ )
            cpd->esa[i] = esa[i];
        for (i = 0;  i < sizeof(cpd->esa);  i += 2) {
            cyg_uint16 reg = cpd->esa[i] | (cpd->esa[i+1] << 8);
            put_reg(cpd->base, PP_IA+i, reg );
        }
#if defined(CYGSEM_DEVS_ETH_CL_CS8900A_WRITE_EEPROM) && defined(CS8900A_PROGRAM_EEPROM)
        if (CS8900A_PROGRAM_EEPROM(cpd))
            return 1;
        else 
            return 0;
#elif defined(CYGSEM_DEVS_ETH_CL_CS8900A_WRITE_EEPROM) && !defined(CS8900A_PROGRAM_EEPROM)
        /* WRITE_EEPROM requested, but no PROGRAM_EEPROM provided */
        return 1;
#else /* !CYGSEM_DEVS_ETH_CL_CS8900A_WRITE_EEPROM - No need to write EEPROM */
        return 0;
#endif

#ifdef ETH_DRV_GET_MAC_ADDRESS
    case ETH_DRV_GET_MAC_ADDRESS:
        // Extract the MAC address that is in the chip, and tell the
        // system about it.
        for (i = 0;  i < sizeof(cpd->esa);  i += 2) {
            unsigned short z = get_reg(cpd->base, PP_IA+i/2 );
            esa[i] =   (unsigned char)(0xff & z);
            esa[i+1] = (unsigned char)(0xff & (z >> 8));
        }
        return 0;
#endif
    case ETH_DRV_SET_MC_LIST:
    case ETH_DRV_SET_MC_ALL:
        // Note: this code always accepts all multicast addresses if any
        // are desired.  It would be possible to accept a subset by adjusting
        // the Logical Address Filter (LAF), but that would require scanning
        // this list and building a suitable mask.
        if (mc_list->len) {
            cpd->rxmode |= PP_RxCTL_Multicast;
        } else {
            cpd->rxmode &= ~PP_RxCTL_Multicast;
        }
        put_reg(base, PP_RxCTL, cpd->rxmode);  // When is it safe to do this?
        return 0;
    default:
        return 1;
        break;
    }
}

// This routine is called to see if it is possible to send another packet.
// It will return non-zero if a transmit is possible, zero otherwise.
static int
cs8900a_can_send(struct eth_drv_sc *sc)
{
    cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
    cyg_addrword_t base = cpd->base;
    cyg_uint16 stat;

    stat = get_reg(base, PP_LineStat);
    if ((stat & PP_LineStat_LinkOK) == 0) {
        return false;  // Link not connected
    }
#ifdef CYGPKG_KERNEL
    // Horrible hack!
    if (cpd->txbusy) {
        cyg_tick_count_t now = cyg_current_time();
        if ((now - cpd->txstart) > 25) {
            // 250ms is more than enough to transmit one frame
#if DEBUG & 1
            diag_printf("CS8900: Tx interrupt lost\n");
#endif
            cpd->txbusy = false;
            // Free up the buffer (with error indication)
            (sc->funs->eth_drv->tx_done)(sc, cpd->txkey, 1);
        }
    }
#endif
    return (cpd->txbusy == false);
}

// This routine is called to send data to the hardware.
static void 
cs8900a_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, 
            int total_len, unsigned long key)
{
    cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
    cyg_addrword_t base = cpd->base;
    int i;
    int len;
    cyg_uint8 *data;
    cyg_uint16 saved_data = 0, *sdata;
    cyg_uint16 stat;
    bool odd_byte = false;

    // Mark xmitter busy
    cpd->txbusy = true;
    cpd->txkey = key;
#ifdef CYGPKG_KERNEL
    cpd->txstart = cyg_current_time();
#endif

    // Start the xmit sequence
#ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
    total_len = CYG_SWAP16(total_len);
#endif
        
    // The hardware indicates that there are options as to when the actual
    // packet transmission will start wrt moving of data into the transmit
    // buffer.  However, impirical results seem to indicate that if the
    // packet is large and transmission is allowed to start before the
    // entire packet has been pushed into the buffer, the hardware gets
    // confused and the packet is lost, along with a "lost" Tx interrupt.
    // This may be a case of the copy loop below being interrupted, e.g.
    // a system timer interrupt, and the hardware getting unhappy that 
    // not all of the data was provided before the transmission should
    // have completed (i.e. buffer underrun).
    // For now, the solution is to not allow this overlap.
    //HAL_WRITE_UINT16(cpd->base+CS8900A_TxCMD, PP_TxCmd_TxStart_5)

    // Start only when all data sent to chip
    HAL_WRITE_UINT16(cpd->base+CS8900A_TxCMD, PP_TxCmd_TxStart_Full);

    HAL_WRITE_UINT16(cpd->base+CS8900A_TxLEN, total_len);
    // Wait for controller ready signal
    {
        // add timeout per cs8900a bugzilla report 1000281 */
        int timeout = 1000;

        do {
            stat = get_reg(base, PP_BusStat);
#if DEBUG & 1
            if( stat & PP_BusStat_TxBid )
                diag_printf( "cs8900a_send: Bid error!\n" );
#endif
        } while (!(stat & PP_BusStat_TxRDY) && --timeout);

        if( !timeout ) {
            // we might as well just return, since if we write the data it will
            // just get thrown away
            return;
        }
    }

    // Put data into buffer
    for (i = 0;  i < sg_len;  i++) {
        data = (cyg_uint8 *)sg_list[i].buf;
        len = sg_list[i].len;

        if (len > 0) {
            /* Finish the last word. */
            if (odd_byte) {
// This new byte must get on the bus _after_ the last saved odd byte, it therefore
// belongs in the MSB of the CS8900a
#ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED                            
                saved_data |= *data++;
#else
                saved_data |= ((cyg_uint16)*data++) << 8;
#endif
                HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, saved_data);
                len--;
                odd_byte = false;
            }
            if (((CYG_ADDRESS)data & 0x1) == 0) {
                /* Aligned on 16-bit boundary, so output contiguous words. */
                sdata = (cyg_uint16 *)data;
                while (len > 1) {
					// Make sure data get on the bus in Big Endian format
#if((CYG_BYTEORDER == CYG_MSBFIRST) && defined(CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED) || \
    (CYG_BYTEORDER == CYG_LSBFIRST) && !defined(CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED ))
                    HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, *sdata++);
#else
                    HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, CYG_SWAP16(*sdata++));
#endif
                    len -= sizeof(cyg_uint16);
                }
                data = (cyg_uint8 *)sdata;
            } else {
                /* Not 16-bit aligned, so byte copy */
                while (len > 1) {
                    saved_data = (cyg_uint16)*data++;   // reuse saved_data
					// Make sure data get on the bus in Big Endian format, the first byte belongs in the
					// LSB of the CS8900A
#ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
                    saved_data =  ((cyg_uint16)*data++) | (saved_data << 8);
#else
                    saved_data |= ((cyg_uint16)*data++) << 8;
#endif
                    HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, saved_data);
                    len -= sizeof(cyg_uint16);
                }
            }
            /* Save last byte, if necessary. */
            if (len == 1) {
                saved_data = (cyg_uint16)*data;
// This _last_ byte must get on the bus _first_, it therefore belongs in the LSB of
// the CS8900a
#ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
                saved_data = (saved_data << 8);
#endif
                odd_byte = true;
            }
        }
    }
    if (odd_byte) {
        HAL_WRITE_UINT16(cpd->base+CS8900A_RTDATA, saved_data);
    }
}

// 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
// 'cs8900a_recv' will be called to actually fetch it from the hardware.
static void
cs8900a_RxEvent(struct eth_drv_sc *sc, int stat)
{
    cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
    cyg_addrword_t base = cpd->base;
    cyg_uint16 len;

    if(stat & PP_RxCFG_RxOK) {
        // Only start reading a message if one has been received
        HAL_READ_UINT16(base+CS8900A_RTDATA, stat);
        HAL_READ_UINT16(base+CS8900A_RTDATA, len);

#ifdef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED
        len = CYG_SWAP16(len);
#endif

        CYG_ASSERT(len > 0, "Zero length ethernet frame received");
        
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
        if (cyg_io_eth_net_debug) {
            diag_printf("RxEvent - stat: %x, len: %d\n", stat, len);
        }
#endif
        (sc->funs->eth_drv->recv)(sc, len);
    }
}

// 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
cs8900a_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
{
    cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
    cyg_addrword_t base = cpd->base;
    int i, mlen;
    cyg_uint16 *data, val;
    cyg_uint8 *cp, cval;

    for (i = 0;  i < sg_len;  i++) {
        data = (cyg_uint16 *)sg_list[i].buf;
        mlen = sg_list[i].len;
        while (mlen >= sizeof(*data)) {
            HAL_READ_UINT16(base+CS8900A_RTDATA, val);
            if (data) {
#if((CYG_BYTEORDER == CYG_MSBFIRST) && defined(CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED) || \
    (CYG_BYTEORDER == CYG_LSBFIRST) && !defined(CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED ))
                *data++ = val;
#else
                *data++ = CYG_SWAP16(val);
#endif
            }
            mlen -= sizeof(*data);
        }
        if (mlen) {
            HAL_READ_UINT16(base+CS8900A_RTDATA, val);
#ifndef CYGIMP_DEVS_ETH_CL_CS8900A_DATABUS_BYTE_SWAPPED 
            // last odd byte will be in the LSB
            cval = (cyg_uint8)(val);
#elif(CYG_BYTEORDER == CYG_MSBFIRST)
            // last odd byte will be in the MSB
            cval = (cyg_uint8)(val >> 8);
#endif
            cval &= 0xff;
            if ((cp = (cyg_uint8 *)data) != 0) {
                *cp = cval;
            }
        }
    }
}

static void
cs8900a_TxEvent(struct eth_drv_sc *sc, int stat)
{
    cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
    cyg_addrword_t base = cpd->base;

    stat = get_reg(base, PP_TER);
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
    if (cyg_io_eth_net_debug) {
        diag_printf("Tx event: %x\n", stat);
    }
#endif
    cpd->txbusy = false;
    (sc->funs->eth_drv->tx_done)(sc, cpd->txkey, 0);
}

static void
cs8900a_BufEvent(struct eth_drv_sc *sc, int stat)
{
    if (stat & PP_BufCFG_RxMiss) {
    }
    if (stat & PP_BufCFG_TxUE) {
    }
}

static void
cs8900a_poll(struct eth_drv_sc *sc)
{
    cyg_uint16 event;
    cs8900a_priv_data_t *cpd = (cs8900a_priv_data_t *)sc->driver_private;
    cyg_addrword_t base = cpd->base;

    HAL_READ_UINT16(base+CS8900A_ISQ, event);
    while (event != 0) {
        switch (event & ISQ_EventMask) {
        case ISQ_RxEvent:
            cs8900a_RxEvent(sc, event);
            break;
        case ISQ_TxEvent:
            cs8900a_TxEvent(sc, event);
            break;
        case ISQ_BufEvent:
            cs8900a_BufEvent(sc, event);
            break;
        case ISQ_RxMissEvent:
            // Receive miss counter has overflowed
            break;
        case ISQ_TxColEvent:
            // Transmit collision counter has overflowed
            break;
        default:
#if DEBUG & 1
            diag_printf("%s: Unknown event: %x\n", __FUNCTION__, event);
#endif
            break;
        }
        HAL_READ_UINT16(base+CS8900A_ISQ, event);
    }

    CYGHWR_CL_CS8900A_PLF_INT_CLEAR(cpd);
}

#ifdef CYGSEM_DEVS_ETH_CL_CS8900A_NOINTS
void
cs8900a_fake_int(cyg_addrword_t param)
{
    struct eth_drv_sc *sc = (struct eth_drv_sc *) param;
    int s;

#if DEBUG & 1
    diag_printf("cs8900a_fake_int()\n");
#endif

    while (true) {
        cyg_thread_delay(5);
        s = splnet();
        cs8900a_poll(sc);
        splx(s);
    }
}
#endif

⌨️ 快捷键说明

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