/*
* $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.bus.pci;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import org.jnode.util.NumberUtils;
/**
* @author epr
*/
public abstract class PCIDeviceConfig implements PCIConstants {
private static final int PCI_VENDOR_ID = 0x00; /* 16 bits */
private static final int PCI_DEVICE_ID = 0x02; /* 16 bits */
private static final int PCI_COMMAND = 0x04; /* 16 bits */
private static final int PCI_STATUS = 0x06; /* 16 bits */
private static final int PCI_CLASS_REVISION = 0x08; // High 24 bits are class, low 8
private static final int PCI_REVISION_ID = 0x08; /* Revision ID */
private static final int PCI_CLASS_PROG = 0x09; /* Reg. Level Programming Interface */
private static final int PCI_CLASS_DEVICE = 0x0a; /* Device class */
private static final int PCI_CACHE_LINE_SIZE = 0x0c; /* 8 bits */
private static final int PCI_LATENCY_TIMER = 0x0d; /* 8 bits */
private static final int PCI_HEADER_TYPE = 0x0e; /* 8 bits */
private static final int PCI_BIST = 0x0f; /* 8 bits */
private static final int PCI_CAPABILITY_LIST = 0x34; /* Offset of first capability list entry */
/**
* My device
*/
protected final PCIDevice device;
/**
* My device ID
*/
private final int deviceID;
/**
* The vendor ID
*/
private final int vendorID;
/**
* The major class
*/
private final int majorClass;
/**
* The sub class
*/
private final int subClass;
/**
* The minor class
*/
private int minorClass;
/**
* The revision
*/
private final int revision;
/**
* The header type
*/
private final int headerTypeRaw;
/**
* The list of capabilities
*/
private Map<Integer, Capability> capabilities;
/**
* Create a new instance
*
* @param device
*/
protected PCIDeviceConfig(PCIDevice device) {
this.device = device;
this.deviceID = device.readConfigWord(PCI_DEVICE_ID);
this.vendorID = device.readConfigWord(PCI_VENDOR_ID);
int reg2 = device.readConfigDword(PCI_CLASS_REVISION);
this.majorClass = (reg2 >> 24) & 0xFF;
this.subClass = (reg2 >> 16) & 0xFF;
this.minorClass = (reg2 >> 8) & 0xFF;
this.revision = (reg2 & 0xFF);
this.headerTypeRaw = device.readConfigByte(PCI_HEADER_TYPE);
}
/**
* Create the correct device config for the given device,
* based on the header type of the device configuration settings.
*
* @param device
* @return the config instance
*/
static final PCIDeviceConfig createConfig(PCIDevice device) {
final int headerTypeRaw = device.readConfigByte(PCI_HEADER_TYPE);
final int headerType = headerTypeRaw & 0x7F;
switch (headerType) {
case HEADER_TYPE_NORMAL:
return new PCIHeaderType0(device);
case HEADER_TYPE_BRIDGE:
return new PCIHeaderType1(device);
case HEADER_TYPE_CARDBUS:
return new PCIHeaderType2(device);
default:
return new PCIDeviceConfig(device) {
};
}
}
/**
* Gets the identifier of this unit
*/
public final int getDeviceID() {
return deviceID;
}
/**
* Gets the ID of the device at the given location.
*
* @param pci
* @param bus
* @param unit
* @param func
* @return the ID
*/
static final int getDeviceID(PCIDriver pci, int bus, int unit, int func) {
return pci.readConfigWord(bus, unit, func, PCI_DEVICE_ID);
}
/**
* Is this unit present
*/
public final boolean isPresent() {
final int devID = getDeviceID();
switch (devID) {
case 0xFFFF:
return false;
case 0:
return (device.getFunction() == 0);
default:
return true;
}
}
/**
* Is a device at a given location present?
*/
static final boolean isPresent(PCIDriver pci, int bus, int unit, int func) {
final int devID = getDeviceID(pci, bus, unit, func);
switch (devID) {
case 0xFFFF:
return false;
case 0:
return (func == 0);
default:
return true;
}
}
/**
* Gets the PCI class information of a device at a given location
*
* @param pci
* @param bus
* @param unit
* @param func
* @return { major, sub, minor, revision }
*/
static final int[] getPCIClass(PCIDriver pci, int bus, int unit, int func) {
final int reg2 = pci.readConfigDword(bus, unit, func, PCI_CLASS_REVISION);
final int[] rc = new int[4];
rc[0] = (reg2 >> 24) & 0xFF;
rc[1] = (reg2 >> 16) & 0xFF;
rc[2] = (reg2 >> 8) & 0xFF;
rc[3] = (reg2 & 0xFF);
return rc;
}
/**
* Gets the identifier of the vendor of this unit
*/
public final int getVendorID() {
return vendorID;
}
/**
* Gets the descriptor of the vendor of this unit
*/
public final VendorDescriptor getVendorDescriptor() {
return PCIDescriptors.getInstance().findVendor(getVendorID());
}
/**
* Gets the descriptor of this device.
*/
public final DeviceDescriptor getDeviceDescriptor() {
return getVendorDescriptor().findDevice(getDeviceID());
}
/**
* Gets the status of this unit
*/
public final int getStatus() {
return device.readConfigWord(PCI_STATUS);
}
/**
* Gets the command info of this unit
*/
public final int getCommand() {
return device.readConfigWord(PCI_COMMAND);
}
/**
* Gets the command info of this unit
*/
public final void setCommand(int command) {
device.writeConfigWord(PCI_COMMAND, command);
}
/**
* Gets the major class of this unit
*/
public final int getBaseClass() {
return majorClass;
}
/**
* Gets the subclass of this unit
*/
public final int getSubClass() {
return subClass;
}
/**
* Gets the minor class (API identification) of this unit
*/
public final int getMinorClass() {
return minorClass;
}
/**
* Sets the minor class (API identification) of this unit
*
* @param v
*/
public final void setMinorClass(int v) {
device.writeConfigByte(PCI_CLASS_PROG, v);
minorClass = device.readConfigByte(PCI_CLASS_PROG);
}
/**
* Gets the revision of this unit
*/
public final int getRevision() {
return revision;
}
public int getBist() {
return device.readConfigByte(PCI_BIST);
}
public final int getHeaderType() {
return headerTypeRaw & 0x7F;
}
/**
* Is this unit a multifunctional device?
*/
public final boolean isMultiFunctional() {
return ((headerTypeRaw & 0x80) != 0);
}
public int getLatency() {
return device.readConfigByte(PCI_LATENCY_TIMER);
}
public int getCacheLineSize() {
return device.readConfigByte(PCI_CACHE_LINE_SIZE);
}
/**
* Gets a capability with a given id.
*
* @return Null if not found, otherwise the capability with the given id.
*/
public final Capability getCapability(int id) {
if (capabilities == null) {
capabilities = Collections.unmodifiableMap(readCapabilities());
}
return capabilities.get(id);
}
/**
* Gets all capabilities.
*
* @return the device capabilities
*/
public final Collection<Capability> getCapabilities() {
if (capabilities == null) {
capabilities = Collections.unmodifiableMap(readCapabilities());
}
return capabilities.values();
}
/**
* Is this a header type 0 configuration.
*
* @return {@code true} if this is a type 0 configuration.
*/
public final boolean isHeaderType0() {
return (getHeaderType() == HEADER_TYPE_NORMAL);
}
/**
* Is this a header type 1 configuration.
*
* @return {@code true} if this is a type 1 configuration.
*/
public final boolean isHeaderType1() {
return (getHeaderType() == HEADER_TYPE_BRIDGE);
}
/**
* Is this a header type 2 configuration.
*
* @return {@code true} if this is a type 2 configuration.
*/
public final boolean isHeaderType2() {
return (getHeaderType() == HEADER_TYPE_CARDBUS);
}
/**
* Gets this configuration as a header type 0 (normal devices) accessor.
*
* @return the configuration
* @throws ClassCastException If header type != 0.
*/
public final PCIHeaderType0 asHeaderType0() {
return (PCIHeaderType0) this;
}
/**
* Gets this configuration as a header type 1 (pci-pci bridge) accessor.
*
* @return the configuration
* @throws ClassCastException If header type != 1.
*/
public final PCIHeaderType1 asHeaderType1() {
return (PCIHeaderType1) this;
}
/**
* Gets this configuration as a header type 2 (cardbus bridge) accessor.
*
* @return the configuration
* @throws ClassCastException If header type != 2.
*/
public final PCIHeaderType2 asHeaderType2() {
return (PCIHeaderType2) this;
}
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("device=0x");
sb.append(NumberUtils.hex(getDeviceID(), 4));
sb.append(", vendor=0x");
sb.append(NumberUtils.hex(getVendorID(), 4));
sb.append(", ");
sb.append(", class=");
sb.append(NumberUtils.hex(getBaseClass(), 2));
sb.append(':');
sb.append(NumberUtils.hex(getSubClass(), 2));
sb.append(':');
sb.append(NumberUtils.hex(getMinorClass(), 2));
sb.append(", revision=");
sb.append(getRevision());
sb.append(", headertype=");
sb.append(getHeaderType());
final Collection<Capability> caps = getCapabilities();
if (!caps.isEmpty()) {
sb.append(", capabilities [");
boolean first = true;
for (Capability c : caps) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(c);
}
sb.append(']');
}
return sb.toString();
}
/**
* Gets a 32-bit int from the device's configuration space.
*
* @param offset Byte offset of the requested dword.
* @return the value fetched.
*/
public int getDWord(int offset) {
return device.readConfigDword(offset);
}
/**
* Load the capability map from the device's configuration space.
*
* @return the capability map
*/
private Map<Integer, Capability> readCapabilities() {
if ((getStatus() & PCI_STATUS_CAP_LIST) == 0) {
return Collections.emptyMap();
}
int ptr = device.readConfigByte(PCI_CAPABILITY_LIST) & ~0x03;
if (ptr == 0) {
return Collections.emptyMap();
}
final TreeMap<Integer, Capability> map = new TreeMap<Integer, Capability>();
while (ptr != 0) {
final Capability c = Capability.createCapability(device, ptr);
map.put(c.getId(), c);
ptr = device.readConfigByte(ptr + 1) & ~0x03;
}
return map;
}
}