Package org.jnode.driver.net.bcm570x

Source Code of org.jnode.driver.net.bcm570x.BCM570xCore

/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.driver.net.bcm570x;

import java.security.PrivilegedExceptionAction;

import javax.naming.NameNotFoundException;

import org.jnode.driver.Device;
import org.jnode.driver.DriverException;
import org.jnode.driver.bus.pci.PCIBaseAddress;
import org.jnode.driver.bus.pci.PCIDevice;
import org.jnode.driver.bus.pci.PCIHeaderType0;
import org.jnode.driver.net.NetworkException;
import org.jnode.driver.net.ethernet.spi.Flags;
import org.jnode.driver.net.spi.AbstractDeviceCore;
import org.jnode.naming.InitialNaming;
import org.jnode.net.HardwareAddress;
import org.jnode.net.SocketBuffer;
import org.jnode.net.ethernet.EthernetAddress;
import org.jnode.net.ethernet.EthernetConstants;
import org.jnode.system.resource.IOResource;
import org.jnode.system.resource.IRQHandler;
import org.jnode.system.resource.IRQResource;
import org.jnode.system.resource.ResourceManager;
import org.jnode.system.resource.ResourceNotFreeException;
import org.jnode.system.resource.ResourceOwner;
import org.jnode.util.AccessControllerUtils;
import org.jnode.util.TimeoutException;

/**
* @author Martin Husted Hartvig (hagar@jnode.org)
*/
public class BCM570xCore extends AbstractDeviceCore implements BCM570xConstants, IRQHandler,
        EthernetConstants {
    /**
     * Start of IO address space
     */
    private final int iobase;
    /**
     * IO address space
     */
    private final IOResource io;
    /**
     * IRQ
     */
    private final IRQResource irq;
    /**
     * My ethernet address
     */
    private EthernetAddress hwAddress = null;
    /**
     * flags needed to setup device
     */
    private final BCM570xFlags flags;
    /**
     * main driver this belongs to
     */
    private final BCM570xDriver driver;
    /** The receive buffer ring */
    // private final RTL8139RxRing rxRing = null;
    /** The transmit buffer */
    // private final RTL8139TxBuffer[] txBuffers = new RTL8139TxBuffer[4];
    /**
     * Is a transmission active?
     */
    // private boolean tx_active;
    private int txIndex;
    private int txAborted;
    private int txNumberOfPackets;
    private int txPending;

    /**
     * Create a new instance
     *
     * @param flags
     */
    public BCM570xCore(BCM570xDriver driver, ResourceOwner owner, PCIDevice device, Flags flags)
        throws DriverException, ResourceNotFreeException {
        if (!(flags instanceof BCM570xFlags))
            throw new DriverException("Wrong flags to the BCM570x driver");

        this.driver = driver;
        log.info(driver);
        this.flags = (BCM570xFlags) flags;

        final int irq = getIRQ(device, this.flags);
        log.info("BCM570x driver irq " + irq);

        // Get the start of the IO address space
        this.iobase = getIOBase(device, this.flags);

        final int iolength = getIOLength(device, this.flags);
        log.info("BCM570x driver iobase " + iobase + " irq " + irq);
        log.debug("BCM570x driver iobase " + iobase + " irq " + irq);

        final ResourceManager rm;

        try {
            rm = InitialNaming.lookup(ResourceManager.NAME);
        } catch (NameNotFoundException ex) {
            throw new DriverException("Cannot find ResourceManager");
        }

        this.irq = rm.claimIRQ(owner, irq, this, true);

        try {
            io = claimPorts(rm, owner, iobase, iolength);
        } catch (ResourceNotFreeException ex) {
            this.irq.release();
            throw ex;
        }

        /*
         * this.rxRing = new RTL8139RxRing(RX_FRAMES, rm);
         *
         * for (int i = 0; i < txBuffers.length; i++) { txBuffers[i] = new
         * RTL8139TxBuffer(rm); setReg32(REG_TX_ADDR0 + (4 * i),
         * txBuffers[i].getFirstDPDAddress().toInt()); }
         *
         * powerUpDevice(); reset();
         *
         * byte[] adr1 = i2bsLoHi(getReg32(REG_MAC0)); byte[] adr2 =
         * i2bsLoHi(getReg32(REG_MAC0 + 4));
         *
         * final byte[] hwAddrArr = new byte[ETH_ALEN];
         *
         * hwAddrArr[0] = adr1[0]; hwAddrArr[1] = adr1[1]; hwAddrArr[2] =
         * adr1[2]; hwAddrArr[3] = adr1[3]; hwAddrArr[4] = adr2[0]; hwAddrArr[5] =
         * adr2[1];
         *
         * this.hwAddress = new EthernetAddress(hwAddrArr, 0);
         *  // disable multicast setReg32(REG_MAR0, 0); setReg32(REG_MAR0 + 4,
         * 0);
         *
         * log.debug("Found " + flags.getName() + " IRQ=" + irq + ", IOBase=0x" +
         * NumberUtils.hex(iobase) + ", MAC Address=" + hwAddress);
         */
    }

    @SuppressWarnings("unused")
    private void powerUpDevice() {
        setReg8(REG_CFG9346, CFG9346_WE);
        setReg8(REG_CONFIG1, 0);
        setReg8(REG_CFG9346, 0);
    }

    private void reset() {
        // FIXME ... what is with all the fixed delays???
        txIndex = 0;
        int i;
        setReg8(REG_CHIPCMD, CMD_RESET);
        try {
            Thread.sleep(200);
        } catch (InterruptedException ex) {
            // ignore
        }

        for (i = 0; i < REPEAT_TIMEOUT_COUNT; i++) {
            try {
                Thread.sleep(GENERIC_WAIT_TIME);
            } catch (InterruptedException ex) {
                // ignore
            }

            if ((getReg8(REG_CHIPCMD) & CMD_RESET) == 0)
                break;
        }

        if (i == REPEAT_TIMEOUT_COUNT)
            log.debug("Ethernet card: Chip Reset incomplete");

        setReg16(BMCR, BMCR_RESET);

        try {
            Thread.sleep(200);
        } catch (InterruptedException ex) {
            // ignore
        }

        for (i = 0; i < REPEAT_TIMEOUT_COUNT; i++) {
            try {
                Thread.sleep(GENERIC_WAIT_TIME);
            } catch (InterruptedException ex) {
                // ignore
            }

            if ((getReg16(BMCR) & BMCR_RESET) == 0)
                break;
        }

        if (i == REPEAT_TIMEOUT_COUNT)
            log.debug("Ethernet card: BMCR Reset incomplete");

        // Autoload from the eeprom
        setReg8(REG_CFG9346, CFG9346_AUTOLOAD);

        try {
            Thread.sleep(200);
        } catch (InterruptedException ex) {
            // ignore
        }

        for (i = 0; i < REPEAT_TIMEOUT_COUNT; i++) {
            try {
                Thread.sleep(GENERIC_WAIT_TIME);
            } catch (InterruptedException ex) {
                // ignore
            }

            if ((getReg8(REG_CFG9346) & 0xc0) == 0)
                break;
        }
        if (i == REPEAT_TIMEOUT_COUNT)
            log.debug("Ethernet card: Autoload incomplete");
    }

    @SuppressWarnings("unused")
    private byte[] i2bsLoHi(int _i) {
        int shiftL = 24, shiftH = shiftL;

        byte[] bs = new byte[4];

        for (int i = 0; i < 4; i++) {
            bs[i] = (byte) ((_i << shiftH) >>> shiftL);
            shiftH = shiftH - 8;
        }
        return bs;
    }

    private void autoNegotiate() {

        // boolean fullDuplex = false;

        // start auto negotiating
        setReg16(REG_INTR_MASK, INTR_MASK);

        int status = getReg16(REG_INTR_STATUS);

        if ((status & INTR_LNKCHG) != 0) {
            log.debug("AN: link changed! " + Integer.toHexString(status));

            setReg16(REG_INTR_STATUS, status);
        }

        setReg8(REG_CFG9346, 0xC0);
        setReg16(BMCR, 0x1200);

        status = getReg16(BMSR);

        int bogusCount = 0;
        while ((status & 0x30) == 0) {

            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                // ignore
            }

            bogusCount++;
            if (bogusCount >= AUTO_NEGOTIATE_TIMEOUT) {
                log.debug("Bogus count: autonegotiating taking too long: " +
                        Integer.toHexString(status));
                break;
            }
            status = getReg16(BMSR);
        }

        log.debug("autonegotiating status: " + Integer.toHexString(status));

        if ((status & 0x20) != 0)
            log.debug("autonegotiating complete");
        if ((status & 0x10) != 0)
            log.debug("remote fault detected");
        if ((status & 0x4) != 0)
            log.debug("link valid");

        /* int lpar = */
        getReg16(NWAY_LPAR);

        log.debug("MSR: " + Integer.toHexString(getReg8(MSR)) + " BMCR: " +
                Integer.toHexString(getReg16(BMCR)) + " LPAR: " +
                Integer.toHexString(getReg16(NWAY_LPAR)));

        // if (lpar == 0xffff) {
        // } else if (((lpar & 0x0100) == 0x0100) || ((lpar & 0x00C0) ==
        // 0x0040)) {
        // fullDuplex = true;
        // }

        // if (fullDuplex) rtl8139.write8(REG_CONFIG1, 0x60); // check
        // else rtl8139.write8(REG_CONFIG1, 0x20);

        setReg8(REG_CFG9346, 0x00);

        // if (fullDuplex) System.out.print("AutoNegotiation: Full Duplex ");
        // else System.out.print("AutoNegotiation: Half Duplex ");

        // if ((lpar & 0x0180) != 0) System.out.println("100 Mbps Mode");
        // else System.out.println("10 Mbps Mode");

        return;
    }

    /**
     * Gets the hardware address of this device
     */
    public HardwareAddress getHwAddress() {
        return hwAddress;
    }

    /**
     * Initialize the device
     */
    public void initialize() {
        // reset the device
        reset();

        // initialize our buffer
        // rxRing.initialize();

        // setReg32(REG_RX_BUF, rxRing.getFirstUPDAddress().toInt());

        autoNegotiate();

        // enable tx/rx
        enableTxRx();

        // setup tx configuration
        setReg32(REG_TX_CONFIG, txConfig);
        // setup rx configuration
        setReg32(REG_RX_CONFIG, rxConfig);

        // unlock config registers
        setReg8(REG_CFG9346, 0xc0);

        // setups up LED pin defs, vital product data, and power management
        int reg = getReg8(REG_CONFIG1);

        // disable power management
        reg &= 0xfe;
        reg |= 0xc0;
        setReg8(REG_CONFIG1, reg);

        // setup config3
        setReg8(REG_CONFIG3, 0x40);
        // Setup config4
        setReg8(REG_CONFIG4, 0x2);
        setReg8(REG_CFG9346, 0);

        // clear rx missed
        setReg16(REG_RX_MISSED, 0x00000000);

        // enable tx/rx
        enableTxRx();
        // Enable interrupts
        setReg16(REG_INTR_MASK, INTR_MASK);
    }

    private void enableTxRx() {
        setReg8(REG_CHIPCMD, CMD_TX_ENABLE | CMD_RX_ENABLE);
    }

    /**
     * Disable the device
     */
    public void disable() {
        reset();
    }

    /**
     * Release all resources
     */
    public void release() {
        io.release();
        irq.release();
    }

    /**
     * Transmit the given buffer
     *
     * @param buf
     * @param timeout
     * @throws InterruptedException
     * @throws org.jnode.util.TimeoutException
     *
     */
    public void transmit(SocketBuffer buf, HardwareAddress destination, long timeout)
        throws InterruptedException, TimeoutException {
        // Set the source address
        hwAddress.writeTo(buf, 6);
        // tx_active = true;

        txNumberOfPackets++;
        // Set the address of the txBuffer

        setReg32(REG_CFG9346, CFG9346_WE);

        // this should be a bug fix, but looking (in Ethereal) at the packes
        // send
        // indicate on my card that this is not true
        // Martin
        /*
         * if (txIndex == 0) { txBuffers[0].initialize(buf);
         * setReg32(REG_TX_STATUS0, txFlag | buf.getSize());
         *
         * txBuffers[1].initialize(buf); setReg32(REG_TX_STATUS0 + 4, txFlag |
         * buf.getSize());
         *
         * txIndex = 2; } else {
         */
        // txBuffers[txIndex].initialize(buf);
        setReg32(REG_TX_STATUS0 + 4 * txIndex, txFlag | buf.getSize());

        // Point to the next empty descriptor
        txIndex++;
        txIndex &= 3;
        // }

        setReg32(REG_CFG9346, CFG9346_NORMAL);
    }

    /**
     * Handle a given hardware interrupt. This method is called from the kernel
     * with interrupts disabled. So keep and handling here as short as possible!
     */
    public void handleInterrupt(int irq) {
        int bogusCount = 20;
        // Keep processing interrupts until there are none to process
        boolean linkChanged = false;

        while (true) {
            int status = getReg16(REG_INTR_STATUS);

            if (status == 0xffff) {
                break;
            }

            // See if anything needs servicing
            if ((status & (INTR_RX_OK | INTR_RX_ERR | INTR_TX_OK | INTR_TX_ERR |
                    INTR_RX_BUF_OVRFLO | INTR_RX_FIFO_OVRFLO | INTR_TIMEOUT | INTR_SYS_ERR |
                    INTR_RX_UNDERRUN | INTR_LEN_CHG)) == 0) {
                break;
            }

            if ((status & INTR_RX_UNDERRUN) == INTR_RX_UNDERRUN) {
                linkChanged = (getReg16(REG_CSCR) & CSCR_LINKCHANGE) == CSCR_LINKCHANGE;
                if (linkChanged) {
                    log.debug("Link changed");
                }
            }

            if ((status & INTR_RX_FIFO_OVRFLO) == INTR_RX_FIFO_OVRFLO) {
                setReg16(REG_INTR_STATUS, status | INTR_RX_BUF_OVRFLO);
            } else {
                setReg16(REG_INTR_STATUS, status);
            }

            // Process tx interrupts
            if ((status & (INTR_TX_OK | INTR_TX_ERR)) != 0) {
                txProcess(status);
            }

            // Process rx interrupts
            if ((status & (INTR_RX_OK | INTR_RX_FIFO_OVRFLO | INTR_RX_BUF_OVRFLO)) != 0) {
                rxProcess(status);
            }

            // Process the other errors
            if ((status & (INTR_RX_ERR | INTR_TX_ERR | INTR_RX_BUF_OVRFLO | INTR_RX_FIFO_OVRFLO |
                    INTR_TIMEOUT | INTR_SYS_ERR | INTR_RX_UNDERRUN)) != 0) {
                errorInterrupt(status);

                // log.debug(" error Interupt "+status);
            }
            if (--bogusCount < 0) {
                setReg16(REG_INTR_STATUS, 0xffff);

                break;
            }
        }
    }

    private void errorInterrupt(int status) {
        // Tally the missed packets and zero the counter
        setReg32(REG_RX_MISSED, 0);

        if ((status & (INTR_RX_UNDERRUN | INTR_RX_BUF_OVRFLO | INTR_RX_FIFO_OVRFLO | INTR_RX_ERR)) != 0) {
            // rxErrors++;
        }
        if ((status & INTR_RX_FIFO_OVRFLO) != 0) {
            // rxFifoOverflow++;
        }
        if ((status & INTR_RX_UNDERRUN) != 0) {
            // rxUnderRun++;
        }
        if ((status & INTR_RX_BUF_OVRFLO) != 0) {
            // rxBufferOverflow++;
            // rxBufferOverflow++;
            // following is needed to clear this interrupt
            // rxIndex = getReg16(REG_RX_BUF_CNT) % RX_BUF_SIZE;

            /*
             * rxRing.setIndex(getReg16(REG_RX_BUF_CNT) % RX_BUF_SIZE);
             * setReg16(REG_RX_BUF_PTR, rxRing.getIndex() - 16);
             */

        }
        if ((status & INTR_SYS_ERR) != 0) {
            // ???
        }
        if ((status & INTR_TIMEOUT) != 0) {
            // timeoutError++;
        }
    }

    private void rxProcess(int status) {
        // Read all packets
        setReg32(REG_CFG9346, CFG9346_WE);

        while ((getReg8(REG_CHIPCMD) & CMD_BUFFER_EMPTY) == 0) {
            final int pktStatus = 0; // rxRing.getPktStatus();
            final int pktLen = (pktStatus >> 16);

            if (pktLen == 0xfff0) {
                break;
            }

            if ((pktStatus & (RX_ISE | RX_RUNT | RX_LONG | RX_CRC | RX_FAE)) != 0) {
                setReg8(REG_CHIPCMD, CMD_TX_ENABLE);

                // set up rx mode/configuration
                setReg32(REG_RX_CONFIG, rxConfig);
                // rxRing.setIndex(getReg16(CBR));
                setReg16(CAPR, CBR);
                enableTxRx();
                setReg32(REG_RX_CONFIG, rxConfig);

                // Enable interrupts
                setReg16(REG_INTR_MASK, INTR_MASK);
                return;
            } else {
                final SocketBuffer skbuf = null; // rxRing.getPacket(pktLen);

                try {
                   
                    if (skbuf != null && skbuf.getSize() > 0) {
                        driver.onReceive(skbuf);
                    }
                } catch (NetworkException e) {
                    e.printStackTrace();
                } finally {
                    // FIXME
                }
            }
            // setReg16(CAPR, rxRing.getIndex() - 16);
        }
        setReg32(REG_CFG9346, CFG9346_NORMAL);
    }

    public void txProcess(int status) {
        while (txNumberOfPackets > 0) {
            int txStatus = getReg32(REG_TX_STATUS0 + (txPending * 4));
            // Make sure something has been transmitted
            if ((txStatus & (TX_TUN | TX_TOK | TX_TABT)) == 0) {
                return;
            }
            if ((txStatus & (TX_OWC | TX_TABT)) != 0) {
                // txErrors++;
                if ((txStatus & TX_TABT) != 0) {
                    txAborted++;
                    // Setting clear abort bit will make the 8139
                    // retransmit the aborted packet
                    setReg32(REG_TX_CONFIG, txConfig | TCR_CLRABT);
                    return;
                }
                if ((txStatus & TX_CRS) != 0) {
                    // txCarrierErrors++;
                }
                if ((txStatus & TX_OWC) != 0) {
                    // System.out.println("TX window error");
                    // txWindowErrors++;
                }
            } else {
                if ((txStatus & TX_TUN) != 0) {
                    // txFifoErrors++;
                }
                // txCollisions += ((txStatus & TX_NCC) >> 24);
                // txBytes += txStatus & 0x7ff;
                // txPackets++;
            }
            // txPendingQueue[txPending].free();
            txNumberOfPackets--;
            txPending++;
            txPending &= 3;
        }
    }

    /**
     * Gets the first IO-Address used by the given device
     *
     * @param device
     * @param flags
     */
    protected int getIOBase(Device device, Flags flags) throws DriverException {
        final PCIHeaderType0 config = ((PCIDevice) device).getConfig().asHeaderType0();
        final PCIBaseAddress[] addrs = config.getBaseAddresses();

        if (addrs.length < 1) {
            throw new DriverException("Cannot find iobase: not base addresses");
        }
        if (!addrs[0].isIOSpace()) {
            throw new DriverException("Cannot find iobase: first address is not I/O");
        }
        return addrs[0].getIOBase();
    }

    /**
     * Gets the number of IO-Addresses used by the given device
     *
     * @param device
     * @param flags
     */
    protected int getIOLength(Device device, Flags flags) throws DriverException {
        final PCIHeaderType0 config = ((PCIDevice) device).getConfig().asHeaderType0();
        final PCIBaseAddress[] addrs = config.getBaseAddresses();

        if (addrs.length < 1) {
            throw new DriverException("Cannot find iobase: not base addresses");
        }

        if (!addrs[0].isIOSpace()) {
            throw new DriverException("Cannot find iobase: first address is not I/O");
        }

        return addrs[0].getSize();
    }

    /**
     * Gets the IRQ used by the given device
     *
     * @param device
     * @param flags
     */
    protected int getIRQ(Device device, Flags flags) throws DriverException {
        final PCIHeaderType0 config = ((PCIDevice) device).getConfig().asHeaderType0();
        return config.getInterruptLine();
    }

    /**
     * Reads a 8-bit NIC register
     *
     * @param reg
     */
    protected final int getReg8(int reg) {
        return io.inPortByte(iobase + reg);
    }

    /**
     * Reads a 16-bit NIC register
     *
     * @param reg
     */
    protected final int getReg16(int reg) {
        return io.inPortWord(iobase + reg);
    }

    /**
     * Reads a 32-bit NIC register
     *
     * @param reg
     */
    protected final int getReg32(int reg) {
        return io.inPortDword(iobase + reg);
    }

    /**
     * Writes a 8-bit NIC register
     *
     * @param reg
     * @param value
     */
    protected final void setReg8(int reg, int value) {
        io.outPortByte(iobase + reg, value);
    }

    /**
     * Writes a 16-bit NIC register
     *
     * @param reg
     * @param value
     */
    protected final void setReg16(int reg, int value) {
        io.outPortWord(iobase + reg, value);
    }

    /**
     * Writes a 32-bit NIC register
     *
     * @param reg
     * @param value
     */
    public final void setReg32(int reg, int value) {
        io.outPortDword(iobase + reg, value);
    }

    private IOResource claimPorts(final ResourceManager rm, final ResourceOwner owner,
            final int low, final int length) throws ResourceNotFreeException, DriverException {
        try {
            return AccessControllerUtils.doPrivileged(new PrivilegedExceptionAction<IOResource>() {
                public IOResource run() throws ResourceNotFreeException {
                    return rm.claimIOResource(owner, low, length);
                }
            });
        } catch (ResourceNotFreeException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new DriverException("Unknown exception", ex);
        }
    }
}
TOP

Related Classes of org.jnode.driver.net.bcm570x.BCM570xCore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.