if_lan91cxx.c

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

C
1,486
字号
                0xff & (stat >> 8), cpd->rxpacket );
#endif
    // There is an Rx Packet ready
    cpd->rxpacket = 0xff & (stat >> 8);

    // Read status and (word) length
    put_reg(sc, LAN91CXX_POINTER, (LAN91CXX_POINTER_RCV | LAN91CXX_POINTER_READ |
                                 LAN91CXX_POINTER_AUTO_INCR | 0x0000));
    get_data_init(sc);
    
    stat = get_data_short(sc);
    len = get_data_short(sc);
    len = len - 6;     // minus header/footer words

#ifdef KEEP_STATISTICS
    if ( stat & LAN91CXX_RX_STATUS_ALIGNERR ) INCR_STAT( rx_align_errors );
    //if ( stat & LAN91CXX_RX_STATUS_BCAST    ) INCR_STAT(  );
    if ( stat & LAN91CXX_RX_STATUS_BADCRC   ) INCR_STAT( rx_crc_errors );
    if ( stat & LAN91CXX_RX_STATUS_TOOLONG  ) INCR_STAT( rx_too_long_frames );
    if ( stat & LAN91CXX_RX_STATUS_TOOSHORT ) INCR_STAT( rx_short_frames );
    //if ( stat & LAN91CXX_RX_STATUS_MCAST    ) INCR_STAT(  );
#endif // KEEP_STATISTICS

    if ((stat & LAN91CXX_RX_STATUS_BAD) == 0) {
        INCR_STAT( rx_good );
        // Then it's OK

        if( LAN91CXX_RX_STATUS_IS_ODD(cpd,stat) )
            len++;

#if DEBUG & 1
        db_printf("RxEvent good rx - stat: 0x%04x, len: 0x%04x\n", stat, len);
#endif
        // Check for bogusly short packets; can happen in promisc mode:
        // Asserted against and checked by upper layer driver.
#ifdef CYGPKG_NET
        if ( len > sizeof( struct ether_header ) )
            // then it is acceptable; offer the data to the network stack
#endif
        (sc->funs->eth_drv->recv)(sc, len);

        return;
    }

    // Not OK for one reason or another...
#if DEBUG & 1
    db_printf("RxEvent - bad rx: stat: 0x%04x, len: 0x%04x\n", stat, len);
    db_printf("PHY %2d: %04x\n",0,lan91cxx_read_phy( sc, 0, 0));
    db_printf("PHY %2d: %04x\n",1,lan91cxx_read_phy( sc, 0, 1));
    db_printf("PHY %2d: %04x\n",2,lan91cxx_read_phy( sc, 0, 2));
    db_printf("PHY %2d: %04x\n",3,lan91cxx_read_phy( sc, 0, 3));
    db_printf("PHY %2d: %04x\n",4,lan91cxx_read_phy( sc, 0, 4));
    db_printf("PHY %2d: %04x\n",5,lan91cxx_read_phy( sc, 0, 5));
    db_printf("PHY %2d: %04x\n",16,lan91cxx_read_phy( sc, 0, 16));
    db_printf("PHY %2d: %04x\n",17,lan91cxx_read_phy( sc, 0, 17));
    db_printf("PHY %2d: %04x\n",18,lan91cxx_read_phy( sc, 0, 18));
    db_printf("PHY %2d: %04x\n",19,lan91cxx_read_phy( sc, 0, 19));
    db_printf("PHY %2d: %04x\n",20,lan91cxx_read_phy( sc, 0, 20));
#endif

    // Free packet
    put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_remrel_rx_frame);
}


//
// This function is called as a result of the "eth_drv_recv()" call above.
// Its 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
lan91cxx_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
{
#if (4 & DEBUG) || defined(CYGPKG_INFRA_DEBUG) || \
    defined(KEEP_STATISTICS) || defined(LAN91CXX_IS_LAN91C111)
    struct lan91cxx_priv_data *cpd = 
        (struct lan91cxx_priv_data *)sc->driver_private;
#endif
    int i;
    short mlen=0, plen;
    cyg_uint8 *data=NULL;
    short val;
    unsigned char *cp, cval;

    DEBUG_FUNCTION();

    INCR_STAT( rx_deliver );

    put_reg(sc, LAN91CXX_POINTER, (LAN91CXX_POINTER_RCV | LAN91CXX_POINTER_READ |
                                 LAN91CXX_POINTER_AUTO_INCR));
    get_data_init(sc);
    
    val = get_data_short(sc);
    plen = get_data_short(sc);
    plen = plen - 6;

    for (i = 0;  i < sg_len;  i++) {
        int clen;
        data = (cyg_uint8 *)sg_list[i].buf;
        mlen = sg_list[i].len;

        clen = mlen;
        if( clen > plen )
            clen = plen;

#if DEBUG & 1
        db_printf("%s : mlen 0x%04x plen 0x%04x clen 0x%04x\n", __FUNCTION__, mlen, plen, clen);
#endif
        mlen -= clen;
        plen -= clen;
        
        if (data) {
            while( clen > 0 ) {
                *data++ = get_data_byte(sc);
                clen--;
            }
        }
        else { // must actively discard ie. read it from the chip anyway.
#if DEBUG & 1            
            db_printf("lan91cxx_recv: No data!!!!!\n");
#endif
            while( clen > 0 ) {            
                (void)get_data_byte(sc);
                clen--;                
            }
        }
#if DEBUG & 1
        diag_dump_buf( sg_list[i].buf, sg_list[i].len > 64 ? 64 : sg_list[i].len );
#endif
    }
    val = get_data_short(sc); // Read control word (and potential data) unconditionally

    cp = (unsigned char *)data;

    CYG_ASSERT(val & LAN91CXX_CONTROLBYTE_RX, 
               "Controlbyte is not for Rx");
    CYG_ASSERT( (1 == mlen) == (0 != LAN91CXX_CONTROLBYTE_IS_ODD(cpd,val)), 
                "Controlbyte does not match");
    if (data && (1 == mlen) && LAN91CXX_CONTROLBYTE_IS_ODD(cpd,val) ) {
        cval = val & 0x00ff;    // last byte contains data
        *cp = cval;
    }

    val = get_reg(sc, LAN91CXX_FIFO_PORTS);
#if DEBUG & 4
    if ( 0x8000 & val ) // Then the Rx FIFO is empty
        db_printf("#####Rx packet NOT freed, stat is 0x%04x (expected 0x%04x)\n",
                    val, cpd->rxpacket);
    else
        db_printf("#####Rx packet freed 0x%04x (expected 0x%04x)\n",
                    0xff & (val >> 8), cpd->rxpacket );
#endif
    CYG_ASSERT( (0xff & (val >> 8)) == cpd->rxpacket, "Unexpected rx packet" );

    // Free packet
    put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_remrel_rx_frame);
}


static void
lan91cxx_poll(struct eth_drv_sc *sc)
{
    unsigned short event;
    struct lan91cxx_priv_data *cpd = 
        (struct lan91cxx_priv_data *)sc->driver_private;

//    DEBUG_FUNCTION();
    while (1) {
        cyg_drv_interrupt_acknowledge(cpd->interrupt);
        // Get the (unmasked) requests
        event = get_reg(sc, LAN91CXX_INTERRUPT);
        event = event & (event >> 8) & 0xff;

        if (0 == event)
            break;
#if 0
        if (event & LAN91CXX_INTERRUPT_ERCV_INT) {
            // Early receive interrupt
            db_printf("Early receive interrupt\n");
        }
        else if (event & LAN91CXX_INTERRUPT_EPH_INT) {
            // ethernet protocol handler failures
            db_printf("Ethernet protocol handler failures\n");
        }
        else if (event & LAN91CXX_INTERRUPT_RX_OVRN_INT) {
            // receive overrun
            db_printf("Receive overrun\n");
        }
        else if (event & LAN91CXX_INTERRUPT_ALLOC_INT) {
            // allocation interrupt
            db_printf("Allocation interrupt\n");
        }
        else
#endif
        if (event & LAN91CXX_INTERRUPT_TX_SET) {
            lan91cxx_TxEvent(sc, event);
        }
        if (event & LAN91CXX_INTERRUPT_RCV_INT) {
            lan91cxx_RxEvent(sc);
        }
        if (event & ~(LAN91CXX_INTERRUPT_TX_SET | LAN91CXX_INTERRUPT_RCV_INT))
            db_printf("%s: Unknown interrupt: 0x%04x\n",
			__FUNCTION__, event);
    }
}

#ifdef LAN91CXX_IS_LAN91C111

static cyg_uint16
lan91cxx_read_phy(struct eth_drv_sc *sc, cyg_uint8 phyaddr, cyg_uint8 phyreg)
{
    int i, mask, input_idx, clk_idx = 0;
    cyg_uint16 mii_reg, value;
    cyg_uint8 bits[64];

    // 32 consecutive ones on MDO to establish sync
    for (i = 0; i < 32; ++i)
	bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;

    // Start code <01>
    bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
    bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;

    // Read command <10>
    bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
    bits[clk_idx++] = LAN91CXX_MGMT_MDOE;

    // Output the PHY address, msb first
    for (mask = 0x10; mask; mask >>= 1) {
	if (phyaddr & mask)
	    bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
	else
	    bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
    }

    // Output the phy register number, msb first
    for (mask = 0x10; mask; mask >>= 1) {
	if (phyreg & mask)
	    bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
	else
	    bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
    }

    // Tristate and turnaround (1 bit times)
    bits[clk_idx++] = 0;

    // Input starts at this bit time
    input_idx = clk_idx;

    // Will input 16 bits
    for (i = 0; i < 16; ++i)
	bits[clk_idx++] = 0;

    // Final clock bit
    bits[clk_idx++] = 0;

    // Get the current MII register value
    mii_reg = get_reg(sc, LAN91CXX_MGMT);

    // Turn off all MII Interface bits
    mii_reg &= ~(LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MCLK | 
		 LAN91CXX_MGMT_MDI | LAN91CXX_MGMT_MDO);

    // Clock all 64 cycles
    for (i = 0; i < sizeof(bits); ++i) {
	// Clock Low - output data
	put_reg(sc, LAN91CXX_MGMT, mii_reg | bits[i]);
	CYGACC_CALL_IF_DELAY_US(50);

	// Clock Hi - input data
	put_reg(sc, LAN91CXX_MGMT, mii_reg | bits[i] | LAN91CXX_MGMT_MCLK);
	CYGACC_CALL_IF_DELAY_US(50);

	bits[i] |= get_reg(sc, LAN91CXX_MGMT) & LAN91CXX_MGMT_MDI;
    }

    // Return to idle state
    put_reg(sc, LAN91CXX_MGMT, mii_reg);
    CYGACC_CALL_IF_DELAY_US(50);

    // Recover input data
    for (value = 0, i = 0; i < 16; ++i) {
	value <<= 1;
	if (bits[input_idx++] & LAN91CXX_MGMT_MDI)
	    value |= 1;
    }
    return value;
}

static void
lan91cxx_write_phy(struct eth_drv_sc *sc, cyg_uint8 phyaddr,
		   cyg_uint8 phyreg, cyg_uint16 value)
{
    int i, mask, clk_idx = 0;
    cyg_uint16 mii_reg;
    cyg_uint8 bits[65];

    // 32 consecutive ones on MDO to establish sync
    for (i = 0; i < 32; ++i)
	bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;

    // Start code <01>
    bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
    bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;

    // Write command <01>
    bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
    bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;

    // Output the PHY address, msb first
    for (mask = 0x10; mask; mask >>= 1) {
	if (phyaddr & mask)
	    bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
	else
	    bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
    }

    // Output the phy register number, msb first
    for (mask = 0x10; mask; mask >>= 1) {
	if (phyreg & mask)
	    bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
	else
	    bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
    }

    // Tristate and turnaround (2 bit times)
    bits[clk_idx++] = 0;
    bits[clk_idx++] = 0;

    // Write out 16 bits of data, msb first
    for (mask = 0x8000; mask; mask >>= 1) {
	if (value & mask)
	    bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO;
	else
	    bits[clk_idx++] = LAN91CXX_MGMT_MDOE;
    }

    // Final clock bit (tristate)
    bits[clk_idx++] = 0;

    // Get the current MII register value
    mii_reg = get_reg(sc, LAN91CXX_MGMT);

    // Turn off all MII Interface bits
    mii_reg &= ~(LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MCLK | 
		 LAN91CXX_MGMT_MDI | LAN91CXX_MGMT_MDO);

    // Clock all cycles
    for (i = 0; i < sizeof(bits); ++i) {
	// Clock Low - output data
	put_reg(sc, LAN91CXX_MGMT, mii_reg | bits[i]);
	CYGACC_CALL_IF_DELAY_US(50);

	// Clock Hi - input data
	put_reg(sc, LAN91CXX_MGMT, mii_reg | bits[i] | LAN91CXX_MGMT_MCLK);
	CYGACC_CALL_IF_DELAY_US(50);

//	bits[i] |= get_reg(sc, LAN91CXX_MGMT) & LAN91CXX_MGMT_MDI;
    }

    // Return to idle state
    put_reg(sc, LAN91CXX_MGMT, mii_reg);
    CYGACC_CALL_IF_DELAY_US(50);
}
#endif // LAN91CXX_IS_LAN91C111

// EOF if_lan91cxx.c

⌨️ 快捷键说明

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