/* * $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.via_rhine; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; 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.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.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; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.CFGD_CFDX; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.CR1_SFRST; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.CR1_TDMD1; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.CR_DPOLL; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.CR_FDX; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.CR_RXON; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.CR_STOP; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.CR_STRT; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.CR_TXON; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.DEFAULT_INTR; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IMRShadow; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrEnable; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrLinkChange; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrPCIErr; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrRxDone; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrRxDropped; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrRxEarly; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrRxEmpty; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrRxErr; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrRxNoBuf; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrRxOverflow; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrRxWakeUp; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrStatsMax; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrStatus; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrStatus2; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrTxAborted; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrTxDescRace; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrTxDone; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrTxErrSummary; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrTxError; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.IntrTxUnderrun; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.MIISR_SPEED; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.RX_RING_SIZE; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.TX_RING_SIZE; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byBCR0; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byBCR1; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byCFGD; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byCR0; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byCR1; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byEECSR; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byIMR0; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byMAR0; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byMAR4; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byMIIAD; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byMIICR; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byPAR0; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byRCR; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.byTCR; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.dwCurrentRxDescAddr; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.dwCurrentTxDescAddr; import static org.jnode.driver.net.via_rhine.ViaRhineConstants.wMIIDATA; import static org.jnode.net.ethernet.EthernetConstants.ETH_ALEN; /** * @author Levente S\u00e1ntha */ class ViaRhineCore extends AbstractDeviceCore implements IRQHandler { private final int ioBase; private final IOResource io; private final IRQResource irq; private EthernetAddress hwAddress; private ViaRhineDriver driver; private ViaRhineRxRing rxRing; private ViaRhineTxRing txRing; /* // temporary Rx buffers. int chip_id; int chip_revision; unsigned int dirty_rx, dirty_tx; // The saved address of a sent-in-place packet/buffer, for skfree(). struct sk_buff *tx_skbuff[TX_RING_SIZE]; unsigned char mc_filter[8]; // Current multicast filter. char phys[4]; // MII device addresses. */ //ViaRhineRxDescriptor[] rx_ring = new ViaRhineRxDescriptor[RX_RING_SIZE]; //ViaRhineTxDescriptor[] tx_ring = new ViaRhineTxDescriptor[TX_RING_SIZE]; byte[] rx_buffs = new byte[RX_RING_SIZE]; byte[] tx_buffs = new byte[TX_RING_SIZE]; int chip_id; int chip_revision; short ioaddr; int cur_rx, cur_tx; // The next free and used entries int dirty_rx, dirty_tx; // The saved address of a sent-in-place packet/buffer, for skfree(). SocketBuffer[] tx_skbuff = new SocketBuffer[TX_RING_SIZE]; char[] mc_filter = new char[8]; // Current multicast filter. char[] phys = new char[4]; // MII device addresses. int tx_full = 1; // The Tx queue is full. int full_duplex = 1; // Full-duplex operation requested. int default_port = 4; // Last dev->if_port value. int media2 = 4; // Secondary monitored media port. int medialock = 1; // Don't sense media type. int mediasense = 1; // Media sensing in progress. public ViaRhineCore(ViaRhineDriver driver, Device device, ResourceOwner owner, Flags flags) throws DriverException, ResourceNotFreeException { this.driver = driver; final int irq_nr = getIRQ(device, flags); PCIBaseAddress addr = getIOBaseAddress(device, flags); this.ioBase = addr.getIOBase(); int io_length = addr.getSize(); final ResourceManager rm; try { rm = InitialNaming.lookup(ResourceManager.NAME); } catch (NameNotFoundException ex) { throw new DriverException("Cannot find ResourceManager"); } this.irq = rm.claimIRQ(owner, irq_nr, this, true); try { io = rm.claimIOResource(owner, ioBase, io_length); } catch (ResourceNotFreeException ex) { this.irq.release(); throw ex; } final byte[] hwAddrArr = new byte[ETH_ALEN]; for (int i = 0; i < ETH_ALEN; i++) hwAddrArr[i] = (byte) getReg8(byPAR0 + i); this.hwAddress = new EthernetAddress(hwAddrArr, 0); log.debug("Found " + flags.getName() + " IRQ = " + irq.getIRQ() + ", IO Base = 0x" + NumberUtils.hex(ioBase) + ", IO Length = " + io_length + ", MAC Address = " + hwAddress); } protected PCIBaseAddress getIOBaseAddress(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: no base address"); } if (!addrs[0].isIOSpace()) { throw new DriverException("Cannot find iobase: first address is not I/O"); } return addrs[0]; } /** * 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(); } public void handleInterrupt(int irq) { log.debug("handleInterrupt()"); printIntrStatus(); setIRQEnabled(false); int intr_status = getIntrStatus(); if ((intr_status & (IntrRxDone | IntrRxNoBuf | IntrRxOverflow | IntrRxDropped | IntrRxEarly | IntrRxEmpty | IntrRxErr | IntrRxWakeUp)) != 0) { /* Acknowledge all of the current interrupt sources ASAP. */ //outw(DEFAULT_INTR & ~IntrRxDone, nic->ioaddr + IntrStatus); //IOSYNC; try { Thread.sleep(50); if (!rxRing.currentDesc().isOwnBit()) { SocketBuffer packet = rxRing.currentDesc().getPacket(); driver.onReceive(packet); log.debug("New packet"); log.debug(packet.getLinkLayerHeader().getSourceAddress()); log.debug(packet.getLinkLayerHeader().getDestinationAddress()); log.debug('\n' + hexDump(packet.toByteArray()) + '\n'); rxRing.currentDesc().setOwnBit(); rxRing.next(); } } catch (Exception e) { log.error("error in irq handler", e); } //setReg16(IntrStatus, DEFAULT_INTR & ~IntrRxDone); setReg16(IntrStatus, DEFAULT_INTR); } if ((intr_status & (IntrTxDone | IntrTxAborted | IntrTxDescRace | IntrTxError | IntrTxErrSummary | IntrTxUnderrun)) != 0) { try { if ((intr_status & IntrTxError) != 0) { reset(); return; } Thread.sleep(50); } catch (Exception e) { log.error("error in irq handler", e); } setReg16(IntrStatus, DEFAULT_INTR | my_INTR); } setIRQEnabled(true); } private static final int my_INTR = IntrTxDone | IntrTxError | IntrTxUnderrun; private void printIntrStatus() { int intr_status = getIntrStatus(); log.debug("Interrupt status word: 0x" + NumberUtils.hex(intr_status)); if ((intr_status & IntrRxDone) != 0) log.debug("Interrupt status: " + "IntrRxDone"); if ((intr_status & IntrRxErr) != 0) log.debug("Interrupt status: " + "IntrRxErr"); if ((intr_status & IntrRxEmpty) != 0) log.debug("Interrupt status: " + "IntrRxEmpty"); if ((intr_status & IntrTxDone) != 0) log.debug("Interrupt status: " + "IntrTxDone"); if ((intr_status & IntrTxError) != 0) log.debug("Interrupt status: " + "IntrTxError"); if ((intr_status & IntrTxUnderrun) != 0) log.debug("Interrupt status: " + "IntrTxUnderrun"); if ((intr_status & IntrPCIErr) != 0) log.debug("Interrupt status: " + "IntrPCIErr"); if ((intr_status & IntrStatsMax) != 0) log.debug("Interrupt status: " + "IntrStatsMax"); if ((intr_status & IntrRxEarly) != 0) log.debug("Interrupt status: " + "IntrRxEarly"); if ((intr_status & IntrRxOverflow) != 0) log.debug("Interrupt status: " + "IntrRxOverflow"); if ((intr_status & IntrRxDropped) != 0) log.debug("Interrupt status: " + "IntrRxDropped"); if ((intr_status & IntrRxNoBuf) != 0) log.debug("Interrupt status: " + "IntrRxNoBuf"); if ((intr_status & IntrTxAborted) != 0) log.debug("Interrupt status: " + "IntrTxAborted"); if ((intr_status & IntrLinkChange) != 0) log.debug("Interrupt status: " + "IntrLinkChange"); if ((intr_status & IntrRxWakeUp) != 0) log.debug("Interrupt status: " + "IntrRxWakeUp"); if ((intr_status & IntrTxDescRace) != 0) log.debug("Interrupt status: " + "IntrTxDescRace"); } private void setIRQEnabled(boolean enable) { int intr_status = getIntrStatus(); if (enable) intr_status = intr_status | DEFAULT_INTR | my_INTR; else intr_status = (intr_status & ~(DEFAULT_INTR | my_INTR)); setReg16(IntrEnable, intr_status); } private int getIntrStatus() { int intr_status = getReg16(IntrStatus); /* On Rhine-II, Bit 3 indicates Tx descriptor write-back race. */ /* added comment by guard */ /* For supporting VT6107, please use revision id to recognize different chips in driver */ // if (tp->chip_id == 0x3065) //if( tp->chip_revision < 0x80 && tp->chip_revision >=0x40 ) intr_status |= getReg8(IntrStatus2) << 16; return intr_status; } public HardwareAddress getHwAddress() { return hwAddress; } public void initialize() throws DriverException { log.debug("initialize()"); probe(); reset(); } public void disable() { log.debug("disable()"); // merge reset and disable reset(); // Switch to loopback mode to avoid hardware races. setReg8(byTCR, 0x60 | 0x01); // Stop the chip's Tx and Rx processes. setReg16(byCR0, CR_STOP); } private void reset() { /* software reset */ setReg8(byCR1, CR1_SFRST); MIIDelay(); //init ring initRing(); /*write TD RD Descriptor to MAC */ setReg32(dwCurrentRxDescAddr, rxRing.ringAddr); setReg32(dwCurrentTxDescAddr, txRing.ringAddr); /* close IMR */ setReg16(byIMR0, 0x0000); /* Setup Multicast */ //set_rx_mode(nic); setRxMode(); /* set TCR RCR threshold to store and forward*/ //outb (0x3e, byBCR0); //outb (0x38, byBCR1); //outb (0x2c, byRCR); //outb (0x60, byTCR); setReg8(byBCR0, 0x3e); setReg8(byBCR1, 0x38); setReg8(byRCR, 0x2c); setReg8(byTCR, 0x60); /* Set Fulldupex */ int FDXFlag = queryAuto(); if (FDXFlag == 1) { setReg8(byCFGD, CFGD_CFDX); setReg16(byCR0, CR_FDX); } /* KICK NIC to WORK */ //CRbak = inw (byCR0); //CRbak = CRbak & 0xFFFB; /* not CR_STOP */ //outw ((CRbak | CR_STRT | CR_TXON | CR_RXON | CR_DPOLL), byCR0); int cr = getReg8(byCR0); cr = cr & 0xFFFB; setReg16(byCR0, cr | CR_STRT | CR_TXON | CR_RXON | CR_DPOLL); /* disable all known interrupt */ //outw (0, byIMR0); //setReg16(byIMR0, 0); //-------------------- //outw (IMRShadow, byIMR0); setReg16(byIMR0, IMRShadow); } private void setRxMode() { /* ! IFF_PROMISC */ //outl(0xffffffff, byMAR0); //outl(0xffffffff, byMAR4); //rx_mode = 0x0C; //outb(0x60 /* thresh */ | rx_mode, byRCR ); setReg32(byMAR0, 0xffffffff); setReg32(byMAR4, 0xffffffff); int rx_mode = 0x0C; setReg8(byRCR, 0x60 | rx_mode); } private void reloadEEPROM() { setReg8(byEECSR, 0x20); /* Typically 2 cycles to reload. */ for (int i = 0; i < 150; i++) if ((getReg8(byEECSR) & 0x20) == 0) break; } void initRing() { try { final ResourceManager rm = InitialNaming.lookup(ResourceManager.NAME); rxRing = new ViaRhineRxRing(rm); log.debug("Rx ring initialised"); txRing = new ViaRhineTxRing(rm); log.debug("Tx ring initialised"); } catch (NameNotFoundException ex) { throw new RuntimeException("Cannot find ResourceManager"); } } private int queryAuto() { int byMIIIndex; int MIIReturn; int advertising, mii_reg5; int negociated; byMIIIndex = 0x04; MIIReturn = ReadMII(byMIIIndex); advertising = MIIReturn; byMIIIndex = 0x05; MIIReturn = ReadMII(byMIIIndex); mii_reg5 = MIIReturn; negociated = mii_reg5 & advertising; if ((negociated & 0x100) != 0 || (negociated & 0x1C0) == 0x40) return 1; else return 0; } private int ReadMII(int byMIIIndex) { int ReturnMII; int byMIIAdrbak; int byMIICRbak; int byMIItemp; byMIIAdrbak = getReg8(byMIIAD); byMIICRbak = getReg8(byMIICR); setReg8(byMIICR, byMIICRbak & 0x7f); MIIDelay(); setReg8(byMIIAD, byMIIIndex); MIIDelay(); setReg8(byMIICR, getReg8(byMIICR) | 0x40); byMIItemp = getReg8(byMIICR); byMIItemp = byMIItemp & 0x40; while (byMIItemp != 0) { byMIItemp = getReg8(byMIICR); byMIItemp = byMIItemp & 0x40; } MIIDelay(); ReturnMII = getReg16(wMIIDATA); setReg8(byMIIAD, byMIIAdrbak); setReg8(byMIICR, byMIICRbak); MIIDelay(); return (ReturnMII); } void WriteMII(int byMIISetByte, int byMIISetBit, int byMIIOP) { int ReadMIItmp; int MIIMask; int byMIIAdrbak; int byMIICRbak; int byMIItemp; byMIIAdrbak = getReg8(byMIIAD); byMIICRbak = getReg8(byMIICR); setReg8(byMIICR, byMIICRbak & 0x7f); MIIDelay(); setReg8(byMIIAD, byMIISetByte); MIIDelay(); setReg8(byMIICR, getReg8(byMIICR) | 0x40); byMIItemp = getReg8(byMIICR); byMIItemp = byMIItemp & 0x40; while (byMIItemp != 0) { byMIItemp = getReg8(byMIICR); byMIItemp = byMIItemp & 0x40; } MIIDelay(); ReadMIItmp = getReg16(wMIIDATA); MIIMask = 0x0001; MIIMask = MIIMask << byMIISetBit; if (byMIIOP == 0) { MIIMask = ~MIIMask; ReadMIItmp = ReadMIItmp & MIIMask; } else { ReadMIItmp = ReadMIItmp | MIIMask; } setReg16(wMIIDATA, ReadMIItmp); MIIDelay(); setReg8(byMIICR, getReg8(byMIICR) | 0x20); byMIItemp = getReg8(byMIICR); byMIItemp = byMIItemp & 0x20; while (byMIItemp != 0) { byMIItemp = getReg8(byMIICR); byMIItemp = byMIItemp & 0x20; } MIIDelay(); setReg8(byMIIAD, byMIIAdrbak & 0x7f); setReg8(byMIICR, byMIICRbak); MIIDelay(); } private void MIIDelay() { for (int i = 0; i < 0x7fff; i++) { getReg8(0x61); getReg8(0x61); getReg8(0x61); getReg8(0x61); } } void probe() { int options = -1; int i; int FDXFlag; int byMIIvalue, LineSpeed, MIICRbak; //if (rhine_debug > 0 && did_version++ == 0) // printf (version); reloadEEPROM(); /* Perhaps this should be read from the EEPROM? */ //--for (i = 0; i < ETH_ALEN; i++) //--nic->node_addr[i] = inb (byPAR0 + i); //--printf ("IO address %hX Ethernet Address: %!\n", ioaddr, nic->node_addr); /* restart MII auto-negotiation */ WriteMII(0, 9, 1); log.info("Analyzing Media type,this will take several seconds........"); for (i = 0; i < 5; i++) { /* need to wait 1 millisecond - we will round it up to 50-100ms */ try { Thread.sleep(70); } catch (InterruptedException x) { //ignore } if ((ReadMII(1) & 0x0020) != 0) break; } log.info("OK\n"); /* #if 0 //* JJM : for Debug printf("MII : Address %hhX ",inb(ioaddr+0x6c)); { unsigned char st1,st2,adv1,adv2,l1,l2; st1=ReadMII(1,ioaddr)>>8; st2=ReadMII(1,ioaddr)&0xFF; adv1=ReadMII(4,ioaddr)>>8; adv2=ReadMII(4,ioaddr)&0xFF; l1=ReadMII(5,ioaddr)>>8; l2=ReadMII(5,ioaddr)&0xFF; printf(" status 0x%hhX%hhX, advertising 0x%hhX%hhX, link 0x%hhX%hhX\n", st1,st2,adv1,adv2,l1,l2); } #endif */ /* query MII to know LineSpeed,duplex mode */ byMIIvalue = getReg8(0x6d); LineSpeed = byMIIvalue & MIISR_SPEED; if (LineSpeed != 0) { //JJM log.info("Linespeed=10Mbs"); } else { log.info("Linespeed=100Mbs"); } FDXFlag = queryAuto(); if (FDXFlag == 1) { log.info(" Fullduplex\n"); setReg16(byCR0, CR_FDX); } else { log.info(" Halfduplex\n"); } /* set MII 10 FULL ON */ WriteMII(17, 1, 1); /* turn on MII link change */ MIICRbak = getReg8(byMIICR); setReg8(byMIICR, MIICRbak & 0x7F); MIIDelay(); setReg8(byMIIAD, 0x41); MIIDelay(); /* while((inb(byMIIAD)&0x20)==0) ; */ setReg8(byMIICR, MIICRbak | 0x80); /* The lower four bits are the media type. */ if (options > 0) { full_duplex = (options & 16) != 0 ? 1 : 0; default_port = options & 15; if (default_port != 0) medialock = 1; } } public void release() { log.debug("release()"); io.release(); log.debug("irq.release"); irq.release(); log.debug("end of release"); } public void transmit(SocketBuffer buf, HardwareAddress destination, long timeout) throws InterruptedException, TimeoutException { log.debug("transmit(): to " + destination); // destination.writeTo(buf, 0); // hwAddress.writeTo(buf, 6); txRing.currentDesc().setOwnBit(); txRing.currentDesc().setPacket(buf); log.debug('\n' + hexDump(buf.toByteArray()) + '\n'); int CR1bak = getReg8(byCR1); CR1bak = CR1bak | CR1_TDMD1; setReg8(byCR1, CR1bak); // do { int i = 0; while (txRing.currentDesc().isOwnBit()) { try { Thread.sleep(10); } catch (InterruptedException x) { // } if (i++ > 5) break; } // if(tp->tx_ring[entry].tx_status.bits.terr == 0) // break; // if(tp->tx_ring[entry].tx_status.bits.abt == 1) // { // // turn on TX // int CR0bak = getReg8(byCR0); // CR0bak = CR0bak | CR_TXON; // setReg8(byCR0, CR0bak); // } // } while(true); txRing.next(); } private int getReg8(int reg) { return io.inPortByte(ioBase + reg); } private int getReg16(int reg) { return io.inPortWord(ioBase + reg); } @SuppressWarnings("unused") private int getReg32(int reg) { return io.inPortDword(ioBase + reg); } private void setReg8(int reg, int value) { io.outPortByte(ioBase + reg, value); } private void setReg16(int reg, int value) { io.outPortWord(ioBase + reg, value); } private void setReg32(int reg, int value) { io.outPortDword(ioBase + reg, value); } String hexDump(byte[] data) { try { InputStream is = new ByteArrayInputStream(data); StringWriter swriter = new StringWriter(); PrintWriter out = new PrintWriter(swriter); final int rowlen = 16; int prt = 0; int len; final byte[] buf = new byte[1024]; StringBuilder sb = new StringBuilder(); while ((len = is.read(buf)) > 0) { int left = len; int ofs = 0; // while (left > 0) { sb.setLength(0); int sz = Math.min(rowlen, left); sb.append(NumberUtils.hex(prt, 8)).append(" "); for (int i = 0; i < rowlen; i++) { if (ofs + i < len) sb.append(NumberUtils.hex(buf[ofs + i], 2)); else sb.append(" "); if ((i + 1) < rowlen) sb.append(' '); if ((i + 1) == rowlen / 2) sb.append(' '); } sb.append(" |"); for (int i = 0; i < rowlen; i++) { if (ofs + i < len) { char c = (char) buf[ofs + i]; if ((c >= ' ') && (c < (char) 0x7f)) sb.append(c); else sb.append('.'); } else sb.append(' '); } sb.append('|'); left -= sz; ofs += sz; prt += sz; out.println(sb.toString()); out.flush(); } } out.flush(); out.close(); is.close(); return swriter.toString(); } catch (Exception e) { throw new RuntimeException(e); } } }