/* * $Id$ * * Copyright (C) 2003-2015 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._3c90x; import java.util.ArrayList; import java.util.Collection; 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.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.NumberUtils; import org.jnode.util.TimeoutException; /** * @author epr */ public class _3c90xCore extends AbstractDeviceCore implements _3c90xConstants, 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; /** * The driver i'm a part of */ private final _3c90xDriver driver; /** * My flags */ private final _3c90xFlags flags; /** * Is a transmission active? */ private boolean tx_active; /** * Active register window */ private int reg_window = 0xff; /** * The receive buffer ring */ private final _3c90xRxRing rxRing; /** * The transmit buffer */ private final _3c90xTxBuffer txBuffer; /** * Is the device a B revision */ private final boolean Brev; /** * Create a new instance * * @param flags */ public _3c90xCore(_3c90xDriver driver, ResourceOwner owner, PCIDevice device, _3c90xFlags flags) throws DriverException, ResourceNotFreeException { final int irq = getIRQ(device, flags); this.driver = driver; this.flags = flags; this.tx_active = false; // Get the start of the IO address space this.iobase = getIOBase(device, flags); final int iolength = getIOLength(device, flags); 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 = rm.claimIOResource(owner, iobase, iolength); } catch (ResourceNotFreeException ex) { this.irq.release(); throw ex; } this.rxRing = new _3c90xRxRing(RX_FRAMES, rm); this.txBuffer = new _3c90xTxBuffer(rm); // Reset the device reset(); // Determine Brev flag switch (readEEProm(0x03)) { case 0x9000:/** 10 Base TPO **/ case 0x9001:/** 10/100 T4 **/ case 0x9050:/** 10/100 TPO **/ case 0x9051:/** 10 Base Combo **/ //case 0x9200: /** 3Com905C-TXM **/ Brev = false; break; case 0x9004:/** 10 Base TPO **/ case 0x9005:/** 10 Base Combo **/ case 0x9006:/** 10 Base TPO and Base2 **/ case 0x900A:/** 10 Base FL **/ case 0x9055:/** 10/100 TPO **/ case 0x9056:/** 10/100 T4 **/ case 0x905A:/** 10 Base FX **/ default: Brev = true; break; } // Read the eeprom final int[] eeprom = new int[0x21]; for (int i = 0; i < 0x17; i++) { eeprom[i] = readEEProm(i); //Syslog.debug("eeprom[" + NumberUtils.hex(i, 2) + "]=" + NumberUtils.hex(eeprom[i], 4)); } final byte[] hwAddrArr = new byte[ETH_ALEN]; hwAddrArr[0] = (byte) (eeprom[0x0a] >> 8); hwAddrArr[1] = (byte) (eeprom[0x0a] & 0xFF); hwAddrArr[2] = (byte) (eeprom[0x0b] >> 8); hwAddrArr[3] = (byte) (eeprom[0x0b] & 0xFF); hwAddrArr[4] = (byte) (eeprom[0x0c] >> 8); hwAddrArr[5] = (byte) (eeprom[0x0c] & 0xFF); this.hwAddress = new EthernetAddress(hwAddrArr, 0); log.debug("Found " + flags.getName() + " IRQ=" + irq + ", IOBase=0x" + NumberUtils.hex(iobase) + ", MAC Address=" + hwAddress); } /** * Gets the ethernet address of this device */ public HardwareAddress getHwAddress() { return hwAddress; } /** * Initialize the device */ public synchronized void initialize() { // First reset the device reset(); // Now initialize our buffers rxRing.initialize(); /** Program the MAC address into the station address registers **/ setWindow(winAddressing2); final int a0 = ((hwAddress.get(1) & 0xFF) << 8) | (hwAddress.get(0) & 0xFF); final int a1 = ((hwAddress.get(3) & 0xFF) << 8) | (hwAddress.get(2) & 0xFF); final int a2 = ((hwAddress.get(5) & 0xFF) << 8) | (hwAddress.get(4) & 0xFF); setReg16(regStationAddress_2_3w + 0, a0); setReg16(regStationAddress_2_3w + 2, a1); setReg16(regStationAddress_2_3w + 4, a2); setReg16(regStationMask_2_3w + 0, 0); setReg16(regStationMask_2_3w + 2, 0); setReg16(regStationMask_2_3w + 4, 0); // Determine the link type final ArrayList<String> connectors = new ArrayList<String>(); final int linktype = determineLinkType(connectors); log.debug("Found connectors " + connectors); /** enable DC converter for 10-Base-T **/ if (linktype == 0x0003) { issueCommand(cmdEnableDcConverter, 0, 0); } /** Set the link to the type we just determined. **/ setWindow(winTxRxOptions3); int cfg = getReg32(regInternalConfig_3_l); cfg &= ~(0xF << 20); cfg |= (linktype << 20); setReg32(regInternalConfig_3_l, cfg); //log.debug("Setting linktype to 0x" + NumberUtils.hex(linktype)); /** Now that we set the xcvr type, reset the Tx and Rx, re-enable. **/ issueCommand(cmdTxReset, 0x00, 10); while ((getReg16(regCommandIntStatus_w) & INT_CMDINPROGRESS) != 0) { /* loop */ } if (!Brev) { setReg8(regTxFreeThresh_b, 0x01); } issueCommand(cmdTxEnable, 0, 0); /** ** reset of the receiver on B-revision cards re-negotiates the link ** takes several seconds (a computer eternity) **/ if (Brev) { issueCommand(cmdRxReset, 0x04, 10); } else { issueCommand(cmdRxReset, 0x00, 10); } while ((getReg16(regCommandIntStatus_w) & INT_CMDINPROGRESS) != 0) { /* loop */ } /** Set the RX filter = receive only individual pkts & bcast. **/ issueCommand(cmdSetRxFilter, 0x01 + 0x04, 0); //issueCommand(cmdSetRxFilter, 0x1F, 0); issueCommand(cmdRxEnable, 0, 0); setReg32(regUpListPtr_l, rxRing.getFirstUPDAddress().toInt()); /** ** set Indication and Interrupt flags , acknowledge any IRQ's **/ final int intMask = INT_HOSTERROR | INT_TXCOMPLETE | INT_RXCOMPLETE | INT_UPDATESTATS | INT_LINKEVENT | INT_UPCOMPLETE | INT_INTREQUESTED; issueCommand(cmdSetInterruptEnable, /*0x7FF*/intMask, 0); issueCommand(cmdSetIndicationEnable, 0x7FF, 0); issueCommand(cmdAcknowledgeInterrupt, 0x661, 0); log.debug("initialize done"); } /** * Disable the device */ public synchronized void disable() { reset(); } /** * Release all resources */ public void release() { io.release(); log.debug("irq.release"); irq.release(); log.debug("end of release"); } /** * Transmit the given buffer * * @param buf * @param timeout * @throws InterruptedException * @throws TimeoutException */ public synchronized void transmit(SocketBuffer buf, HardwareAddress destination, long timeout) throws InterruptedException, TimeoutException { // Set the source address hwAddress.writeTo(buf, 6); //final int txStatus = getReg8(regTxStatus_b); //log.debug("Waiting for transmit txStatus=0x" + NumberUtils.hex(txStatus, 2)); // Wait until we can start transmitting final long start = System.currentTimeMillis(); while (tx_active) { final long now = System.currentTimeMillis(); if (now - start > timeout) { throw new TimeoutException("Timeout in claiming transmitter"); } wait(timeout); } tx_active = true; //log.debug("Going for transmit"); txBuffer.initialize(buf); // Stall the download engine issueCommand(cmdStallCtl, 2, 1); // Set the address of the txBuffer setReg32(regDnListPtr_l, txBuffer.getFirstDPDAddress().toInt()); // UnStall the download engine issueCommand(cmdStallCtl, 3, 1); //log.debug("Leaving transmit txStatus=0x" + NumberUtils.hex(getReg8(regTxStatus_b), 2)); } /** * @see org.jnode.system.resource.IRQHandler#handleInterrupt(int) */ public synchronized void handleInterrupt(int irq) { int intStatus = getReg16(regCommandIntStatus_w); int loops = 0; while ((intStatus & ~INT_WINDOWNUMBER) != 0) { //log.debug("IntStatus flags on " + flags.getName() + ": 0x" + NumberUtils.hex(intStatus, 4)); loops++; if (loops > MAX_SERVICE) { log.error("Too much work in intterupt, IntStatus=0x" + NumberUtils.hex(intStatus)); //issueCommand(cmdAcknowledgeInterrupt, intStatus & ~INT_WINDOWNUMBER, 0); return; } if ((intStatus & INT_TXCOMPLETE) != 0) { //log.debug("TxComplete on " + flags.getName()); processTxComplete(); } else if ((intStatus & INT_UPDATESTATS) != 0) { log.debug("UpdateStats on " + flags.getName()); processUpdateStats(); } else if ((intStatus & INT_LINKEVENT) != 0) { //log.debug("LinkEvent on " + flags.getName()); processLinkEvent(); } else if ((intStatus & INT_UPCOMPLETE) != 0) { //log.debug("UpComplete on " + flags.getName()); processUpComplete(); } else if ((intStatus & INT_INTERRUPTLATCH) != 0) { issueCommand(cmdAcknowledgeInterrupt, INT_INTERRUPTLATCH, 0); } else { log.debug("Unknown IntStatus flags set on " + flags.getName() + ": IntStatus=0x" + NumberUtils.hex(intStatus, 4)); issueCommand(cmdAcknowledgeInterrupt, intStatus & ~INT_WINDOWNUMBER, 0); } intStatus = getReg16(regCommandIntStatus_w); } //issueCommand(cmdAcknowledgeInterrupt, INT_INTERRUPTLATCH, 0); //log.debug("Done IRQ on " + flags.getName() + ": 0x" + NumberUtils.hex(intStatus, 4)); } /** * Process a TxComplete interrupt */ private final void processTxComplete() { tx_active = false; notifyAll(); setReg8(regTxStatus_b, 0xFF); // Ack TxComplete, by writing any value } /** * Process an UpdateStats interrupt */ private final void processUpdateStats() { // TODO Implement update stats } /** * Process a LinkEvent interrupt */ private final void processLinkEvent() { // Read IntStatusAuto ack. the interrupt //getReg16(regIntStatusAuto_w); issueCommand(cmdAcknowledgeInterrupt, 0x02, 0); //issueCommand(cmdRxEnable, 0, 0); } /** * Process an UpComplete interrupt */ private final void processUpComplete() { // Read all packets final int nrFrames = rxRing.getNrFrames(); // Stall uploading first issueCommand(cmdStallCtl, 0x00, 1); for (int i = 0; i < nrFrames; i++) { final int pktStatus = rxRing.getPktStatus(i); if (pktStatus != 0) { //log.debug("PktStatus[" + NumberUtils.hex(i, 2) + "]=0x" + NumberUtils.hex(pktStatus)); if ((pktStatus & upComplete) != 0) { final SocketBuffer skbuf = rxRing.getPacket(i); try { //log.debug("Read packet at index 0x" + NumberUtils.hex(i)); driver.onReceive(skbuf); } catch (NetworkException ex) { log.debug("Error in onReceive", ex); } finally { rxRing.setPktStatus(i, 0); } } } } // UnStall uploading issueCommand(cmdStallCtl, 0x01, 0); // Ack interrupt issueCommand(cmdAcknowledgeInterrupt, INT_UPCOMPLETE, 0); } /** * Reset this device */ private void reset() { issueCommand(cmdGlobalReset, 0xff, 10); /** global reset command resets station mask, non-B revision cards ** require explicit reset of values **/ setWindow(winAddressing2); setReg16(regStationAddress_2_3w + 0, 0); setReg16(regStationAddress_2_3w + 2, 0); setReg16(regStationAddress_2_3w + 4, 0); /** Issue transmit reset, wait for command completion **/ issueCommand(cmdTxReset, 0, 10); issueCommand(cmdRxReset, 0, 10); issueCommand(cmdSetInterruptEnable, 0, 0); /** enable rxComplete and txComplete **/ issueCommand(cmdSetIndicationEnable, 0x0014, 0); /** acknowledge any pending status flags **/ issueCommand(cmdAcknowledgeInterrupt, 0x661, 0); } /** * Gets the first IO-Address used by the given device * * @param device * @param flags */ protected final int getIOBase(Device device, _3c90xFlags 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, _3c90xFlags 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, _3c90xFlags flags) throws DriverException { final PCIHeaderType0 config = ((PCIDevice) device).getConfig().asHeaderType0(); return config.getInterruptLine(); } /** * Determine which connectors are physically available on the device. * Determine the link type based on that. * * @param connectors */ private final int determineLinkType(Collection<String> connectors) { /** Read the media options register, print a message and set default ** xcvr. ** ** Uses Media Option command on B revision, Reset Option on non-B ** revision cards -- same register address **/ setWindow(winTxRxOptions3); int mopt = getReg16(regResetMediaOptions_3_w); /* mask out VCO bit that is defined as 10baseFL bit on B-rev cards */ if (!Brev) { mopt &= 0x7F; } //log.debug("mopt=0x" + NumberUtils.hex(mopt)); int linktype = 0x0008; if ((mopt & 0x01) != 0) { connectors.add("100Base-T4"); linktype = 0x0006; } if ((mopt & 0x04) != 0) { connectors.add("100Base-FX"); linktype = 0x0005; } if ((mopt & 0x10) != 0) { connectors.add("10Base-2"); linktype = 0x0003; } if ((mopt & 0x20) != 0) { connectors.add("AUI"); linktype = 0x0001; } if ((mopt & 0x40) != 0) { connectors.add("MII"); linktype = 0x0006; } if ((mopt & 0xA) == 0xA) { connectors.add("10Base-T / 100Base-TX"); linktype = 0x0008; } else if ((mopt & 0xA) == 0x2) { connectors.add("100Base-TX"); linktype = 0x0008; } else if ((mopt & 0xA) == 0x8) { connectors.add("10Base-T"); linktype = 0x0008; } return linktype; } /** * Execute a command on the NIC * * @param command * @param param */ private final void issueCommand(int command, int param, long sleep) { // Calculate the complete command + param value final int v = (command << 11) | (param & 0x7FF); // Wait for the previous command to complete while ((getReg16(regCommandIntStatus_w) & INT_CMDINPROGRESS) != 0) { /* loop */ } // Send the command setReg16(regCommandIntStatus_w, v); if (sleep > 0) { // Sleep for long commands try { Thread.sleep(sleep); } catch (InterruptedException ex) { // Ignore } } // Wait for the command to complete int loops = 0; while ((getReg16(regCommandIntStatus_w) & INT_CMDINPROGRESS) != 0) { /* loop */ loops++; } //log.debug("Loops=" + loops + ", cmd=0x" + NumberUtils.hex(v)); } /** * Read data from the serial eeprom * * @param address */ private int readEEProm(int address) { /** Select correct window **/ setWindow(winEepromBios0); /** Make sure the eeprom isn't busy **/ while (((1 << 15) & getReg16(regEepromCommand_0_w)) != 0) { /* Loop */ } /** Read the value. **/ setReg16(regEepromCommand_0_w, address + ((0x02) << 6)); try { Thread.sleep(2); } catch (InterruptedException ex) { // Ignore } while (((1 << 15) & getReg16(regEepromCommand_0_w)) != 0) { /* Loop */ } return getReg16(regEepromData_0_w) & 0xFFFF; } /** * Sets the current register window * * @param w */ private final void setWindow(int w) { if (reg_window != w) { issueCommand(cmdSelectRegisterWindow, w, 0); reg_window = w; } } /** * Reads a 8-bit NIC register * @param reg */ /*private final int getReg8(int reg) { return io.inPortByte(iobase + reg); }*/ /** * Reads a 16-bit NIC register * * @param reg */ private final int getReg16(int reg) { return io.inPortWord(iobase + reg); } /** * Reads a 32-bit NIC register * * @param reg */ private final int getReg32(int reg) { return io.inPortDword(iobase + reg); } /** * Writes a 8-bit NIC register * * @param reg * @param value */ private final void setReg8(int reg, int value) { io.outPortByte(iobase + reg, value); } /** * Writes a 16-bit NIC register * * @param reg * @param value */ private final void setReg16(int reg, int value) { io.outPortWord(iobase + reg, value); } /** * Writes a 32-bit NIC register * * @param reg * @param value */ private final void setReg32(int reg, int value) { io.outPortDword(iobase + reg, value); } }