/*
* $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.bus.ide;
import java.security.PrivilegedExceptionAction;
import javax.naming.NameNotFoundException;
import org.apache.log4j.Logger;
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.PCIDeviceConfig;
import org.jnode.naming.InitialNaming;
import org.jnode.system.resource.IOResource;
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.NumberUtils;
import org.jnode.util.TimeUtils;
import org.jnode.util.TimeoutException;
/**
* IDE IO-port accessor.
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
public class DefaultIDEIO implements IDEIO {
/**
* My logger
*/
private static final Logger log = Logger.getLogger(DefaultIDEIO.class);
/**
* IDE Taskfile io space
*/
private IOResource cmdBlock;
/**
* IDE High taskfile io space
*/
private IOResource ctrlBlock;
/**
* First port address of command block
*/
private final int cmdBlockStart;
/**
* First port address of control block
*/
private final int ctrlBlockStart;
/**
* Port number of AltStatus register
*/
private final int altStatusPort;
/**
* IRQ number to respond to
*/
private final int irq;
/**
* Create a new instance
*/
protected DefaultIDEIO(Device device, boolean primary)
throws IllegalArgumentException, DriverException, ResourceNotFreeException {
int cmdBlockStart = (primary ? IDE0_START_PORT : IDE1_START_PORT);
int ctrlBlockStart = cmdBlockStart + HIGH_OFFSET;
int cmdBlockSize = IDE_NR_PORTS;
int ctrlBlockSize = IDE_NR_HIGH_PORTS;
int altStatusPort = ctrlBlockStart + R8_ALTSTATUS_OFFSET;
int irq = (primary ? IDE0_IRQ : IDE1_IRQ);
boolean nativeMode = false;
// Detect PCI IDE Controller, look for enhanced mode
if (device instanceof PCIDevice) {
final PCIDevice pciDev = (PCIDevice) device;
final PCIDeviceConfig pciCfg = pciDev.getConfig();
final int pIntf = pciCfg.getMinorClass();
final int progMask = 0x02 | 0x08;
final int enhModeMask = 0x01 | 0x04;
if ((pIntf & progMask) == progMask) {
// Mode is programmable, set enhanced mode
//pciCfg.setMinorClass(pIntf | enhModeMask);
}
if ((pciCfg.getMinorClass() & enhModeMask) == enhModeMask) {
// Use enhanced mode
final PCIBaseAddress[] baseAddrs = pciCfg.asHeaderType0().getBaseAddresses();
final int idx = (primary ? 0 : 2);
cmdBlockStart = baseAddrs[idx].getIOBase();
cmdBlockSize = 8;
ctrlBlockStart = baseAddrs[idx + 1].getIOBase();
ctrlBlockSize = 4;
altStatusPort = ctrlBlockStart + 0x02;
irq = pciCfg.asHeaderType0().getInterruptLine();
nativeMode = true;
}
}
log.info("Using PCI IDE " + (nativeMode ? "Native" : "Compatibility") + " mode [irq=" + irq + "]");
// Now claim the resources
IOResource cmdBlock = null;
IOResource ctrlBlock = null;
final ResourceManager rm;
try {
rm = InitialNaming.lookup(ResourceManager.NAME);
cmdBlock = claimPorts(rm, device, cmdBlockStart, cmdBlockSize);
ctrlBlock = claimPorts(rm, device, ctrlBlockStart, ctrlBlockSize);
} catch (NameNotFoundException ex) {
throw new ResourceNotFreeException("Cannot find ResourceManager",
ex);
} catch (ResourceNotFreeException ex) {
if (cmdBlock != null) {
cmdBlock.release();
}
if (ctrlBlock != null) {
ctrlBlock.release();
}
throw ex;
}
this.irq = irq;
this.cmdBlockStart = cmdBlockStart;
this.ctrlBlockStart = ctrlBlockStart;
this.cmdBlock = cmdBlock;
this.ctrlBlock = ctrlBlock;
this.altStatusPort = altStatusPort;
}
/**
* Stop this processor.
*/
public void release() {
cmdBlock.release();
ctrlBlock.release();
}
/**
* Gets a word from the data register
*
* @return a word from the data register
*/
public final int getDataReg() {
return cmdBlock.inPortWord(cmdBlockStart + RW16_DATA_OFFSET);
}
/**
* Writes a word to the data register
*
* @param dataWord
*/
public final void setDataReg(int dataWord) {
cmdBlock.outPortWord(cmdBlockStart + RW16_DATA_OFFSET, dataWord);
}
/**
* Gets the contents of the error register
*
* @return the contents of the error register
*/
public final int getErrorReg() {
return cmdBlock.inPortByte(cmdBlockStart + R8_ERROR_OFFSET);
}
/**
* Sets the contents of the featureregister
*
* @param features
*/
public final void setFeatureReg(int features) {
cmdBlock.outPortByte(cmdBlockStart + W8_FEATURE_OFFSET, features);
}
/**
* Gets the contents of the sector count register
*
* @return the contents of the sector count register
*/
public final int getSectorCountReg() {
return cmdBlock.inPortByte(cmdBlockStart + RW8_SECTOR_COUNT_OFFSET);
}
/**
* Sets the sector count register
*
* @param sectorCount
*/
public final void setSectorCountReg(int sectorCount) {
cmdBlock.outPortByte(cmdBlockStart + RW8_SECTOR_COUNT_OFFSET, sectorCount);
}
/**
* Gets the contents of the sector register
*
* @return the contents of the sector register
*/
public final int getSectorReg() {
return cmdBlock.inPortByte(cmdBlockStart + RW8_SECTOR_OFFSET);
}
/**
* Gets the contents of the LBA low register
*
* @return the contents of the LBA low register
*/
public final int getLbaLowReg() {
return cmdBlock.inPortByte(cmdBlockStart + RW8_LBA_LOW_OFFSET);
}
/**
* Gets the contents of the LBA mid register
*
* @return the contents of the LBA mid register
*/
public final int getLbaMidReg() {
return cmdBlock.inPortByte(cmdBlockStart + RW8_LBA_MID_OFFSET);
}
/**
* Gets the contents of the LBA high register
*
* @return the contents of the LBA high register
*/
public final int getLbaHighReg() {
return cmdBlock.inPortByte(cmdBlockStart + RW8_LBA_HIGH_OFFSET);
}
/**
* Sets the contents of the LBA low register
*
* @param value
*/
public final void setLbaLowReg(int value) {
cmdBlock.outPortByte(cmdBlockStart + RW8_LBA_LOW_OFFSET, value);
}
/**
* Sets the contents of the LBA mid register
*
* @param value
*/
public final void setLbaMidReg(int value) {
cmdBlock.outPortByte(cmdBlockStart + RW8_LBA_MID_OFFSET, value);
}
/**
* Sets the contents of the LBA high register
*
* @param value
*/
public final void setLbaHighReg(int value) {
cmdBlock.outPortByte(cmdBlockStart + RW8_LBA_HIGH_OFFSET, value);
}
/**
* Sets the sector register
*
* @param sector
*/
protected final void setSectorReg(int sector) {
cmdBlock.outPortByte(cmdBlockStart + RW8_SECTOR_OFFSET, sector);
}
/**
* Gets the combined cylinder value out the the cylinder LSB and MSB
* registers.
*/
protected final int getCylinderRegs() {
final int lsb = cmdBlock.inPortByte(cmdBlockStart + RW8_CYLINDER_LSB_OFFSET);
final int msb = cmdBlock.inPortByte(cmdBlockStart + RW8_CYLINDER_MSB_OFFSET);
return ((msb & 0xFF) << 8) | (lsb & 0xFF);
}
/**
* Sets the cylinder registers value (both LSB and MSB)
*/
protected final void setCylinderRegs(int cylinder) {
final int lsb = cylinder & 0xFF;
final int msb = (cylinder >> 8) & 0xFF;
cmdBlock.outPortByte(cmdBlockStart + RW8_CYLINDER_LSB_OFFSET, lsb);
cmdBlock.outPortByte(cmdBlockStart + RW8_CYLINDER_MSB_OFFSET, msb);
}
/**
* Gets the contents of the select register
*
* @return the contents of the select register
*/
public final int getSelectReg() {
return cmdBlock.inPortByte(cmdBlockStart + RW8_SELECT_OFFSET);
}
/**
* Sets the select register
*
* @param select
*/
public final void setSelectReg(int select) {
cmdBlock.outPortByte(cmdBlockStart + RW8_SELECT_OFFSET, select);
}
/**
* Gets the status of the IDE controller. Any pending IRQ is reset.
*
* @return the status of the IDE controller. Any pending IRQ is reset.
*/
public final int getStatusReg() {
return cmdBlock.inPortByte(cmdBlockStart + R8_STATUS_OFFSET);
}
/**
* Gets the alternative status of the IDE controller. Any pending IRQ is
* NOT reset.
*
* @return the alternative status of the IDE controller
*/
public final int getAltStatusReg() {
return ctrlBlock.inPortByte(altStatusPort);
}
/**
* Sets the command register. This also activates the IDE controller so
* always set other registers first.
*
* @param command
*/
public final void setCommandReg(int command) {
cmdBlock.outPortByte(cmdBlockStart + W8_COMMAND_OFFSET, command);
}
/**
* Sets the control register.
*
* @param control The new value for the control register
*/
public final void setControlReg(int control) {
ctrlBlock.outPortByte(ctrlBlockStart + W8_CONTROL_OFFSET, control);
}
/**
* Is this channel busy.
*
* @return if this channel is busy
*/
public final boolean isBusy() {
return ((getAltStatusReg() & ST_BUSY) == ST_BUSY);
}
/**
* Block the current thread until the status of the controller masked by the given mask equals value.
*/
public void waitUntilStatus(int mask, int value, long timeout, String message) throws TimeoutException {
while ((getAltStatusReg() & mask) != value) {
final int state = getAltStatusReg();
if ((state & ST_ERROR) != 0) {
throw new TimeoutException("IDE error " + NumberUtils.hex(getErrorReg(), 2));
}
if (timeout <= 0) {
throw new TimeoutException((message != null) ? message : "Timeout in waitUntilStatus");
}
TimeUtils.sleep(10);
timeout -= 10;
}
}
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);
}
}
/**
* @return Returns the irq.
*/
public final int getIrq() {
return this.irq;
}
}