IOPCCardBridge.cpp [plain text]
#define IN_CARD_SERVICES
#include <IOKit/pccard/IOPCCard.h>
__BEGIN_DECLS
#include <IOKit/pccard/ss.h>
#include "yenta.h"
__END_DECLS
#ifdef PCMCIA_DEBUG
int ds_debug = PCMCIA_DEBUG;
MODULE_PARM(ds_debug, "i");
#define DEBUG(n, args...) if (ds_debug>(n)) printk(KERN_DEBUG args)
static const char *version =
"ds.c 1.104 2000/01/11 01:18:02 (David Hinds)";
#else
#define DEBUG(n, args...)
#endif
typedef struct socket_info_t {
IOPCCardBridge * bridge;
client_handle_t handle;
config_req_t config;
client_handle_t configHandle;
int state;
struct timer_list removal;
OSArray * nubs;
} socket_info_t;
#define SOCKET_PRESENT 0x01
#define SOCKET_BUSY 0x02
#define SOCKET_CONFIG 0x04
#define SOCKET_REMOVAL_PENDING 0x08
#define SOCKET_SUSPEND 0x10
static unsigned int sockets = 0;
static socket_info_t *socket_table = NULL;
static dev_info_t dev_info = "Driver Services";
#define kInterruptControllerNamePrefix "IOPCCardInterruptController"
static IOLock * gIOPCCardBridgeLock;
static int gIOPCCardConfigurationInited = 0;
static int gIOPCCardGlobalsInited = 0;
static int gIOPCCardResourcesInited = 0;
static OSArray * gAvailableIORanges = 0;
static OSArray * gAvailableMemRanges = 0;
static IOWorkLoop * gIOPCCardWorkLoop = 0;
IOCommandGate * gIOPCCardCommandGate = 0;
OSSet * gIOPCCardMappedBridgeSpace = 0;
static IODeviceMemory * gDynamicBridgeSpace;
static IODeviceMemory * gDynamicBridgeIOSpace;
static inline void
cs_error(client_handle_t handle, int func, int ret)
{
error_info_t err = { func, ret };
CardServices(ReportError, handle, &err, NULL);
}
static int
read_tuple(client_handle_t handle, cisdata_t code, void *parse, u_int attributes = 0)
{
tuple_t tuple;
static cisdata_t buf[255];
int ret;
tuple.DesiredTuple = code;
tuple.Attributes = attributes;
ret = CardServices(GetFirstTuple, handle, &tuple, NULL);
if (ret != CS_SUCCESS) return ret;
tuple.TupleData = buf;
tuple.TupleOffset = 0;
tuple.TupleDataMax = sizeof(buf);
ret = CardServices(GetTupleData, handle, &tuple, NULL);
if (ret != CS_SUCCESS) return ret;
ret = CardServices(ParseTuple, handle, &tuple, parse);
return ret;
}
bool static
wackBridgeBusNumbers(IOPCIDevice * bridgeDevice)
{
UInt32 value = bridgeDevice->configRead32(CB_PRIMARY_BUS);
DEBUG(2, "wackBridgeBusNumbers sub/cardbus/pci before 0x%x\n", value & 0xffffff);
#ifdef PCMCIA_DEBUG
if ((value & 0x00ffff00) == 0x000400) {
static int nextBus = 1;
value &= 0xff0000ff;
value |= (nextBus << 8) | ((nextBus+3) << 16);
bridgeDevice->configWrite32(CB_PRIMARY_BUS, value);
nextBus += 5;
}
#endif
if (!(value & 0x00ffffff)) {
static int nextBus = 1;
value |= (nextBus << 8) | ((nextBus+3) << 16);
bridgeDevice->configWrite32(CB_PRIMARY_BUS, value);
nextBus += 5;
}
#ifdef PCMCIA_DEBUG
value = bridgeDevice->configRead32(CB_PRIMARY_BUS);
#endif
DEBUG(2, "wackBridgeBusNumbers sub/cardbus/pci after 0x%x\n", value & 0xffffff);
return true;
}
#undef super
#define super IOPCI2PCIBridge
OSDefineMetaClassAndStructorsWithInit(IOPCCardBridge, IOPCI2PCIBridge, IOPCCardBridge::metaInit());
OSMetaClassDefineReservedUsed(IOPCCardBridge, 0); OSMetaClassDefineReservedUnused(IOPCCardBridge, 1);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 2);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 3);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 4);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 5);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 6);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 7);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 8);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 9);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 10);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 11);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 12);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 13);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 14);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 15);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 16);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 17);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 18);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 19);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 20);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 21);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 22);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 23);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 24);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 25);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 26);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 27);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 28);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 29);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 30);
OSMetaClassDefineReservedUnused(IOPCCardBridge, 31);
void
IOPCCardBridge::metaInit(void)
{
#ifdef PCMCIA_DEBUG
extern boolean_t zone_check;
zone_check = TRUE;
#endif
gIOPCCardBridgeLock = IOLockAlloc();
init_pcmcia_cs();
}
void
IOPCCardBridge::free(void)
{
DEBUG(2, "IOPCCardBridge::free\n");
IOLockLock(gIOPCCardBridgeLock);
if (gIOPCCardGlobalsInited > 1) {
gIOPCCardGlobalsInited--;
} else if (gIOPCCardGlobalsInited == 1) {
gIOPCCardMappedBridgeSpace->release();
gIOPCCardWorkLoop->removeEventSource(gIOPCCardCommandGate);
gIOPCCardCommandGate->release();
gIOPCCardWorkLoop->release();
gIOPCCardGlobalsInited = 0;
}
if (gIOPCCardResourcesInited > 1) {
gIOPCCardResourcesInited--;
} else if (gIOPCCardResourcesInited == 1) {
gDynamicBridgeSpace->release();
gDynamicBridgeIOSpace->release();
gIOPCCardResourcesInited = 0;
}
IOLockUnlock(gIOPCCardBridgeLock);
super::free();
}
UInt8
IOPCCardBridge::firstBusNum(void)
{
return bridgeDevice->configRead8(0x19);
}
UInt8
IOPCCardBridge::lastBusNum(void)
{
return bridgeDevice->configRead8(0x1a);
}
void
IOPCCardBridge::probeBus(IOService * provider, UInt8 busNum)
{
return;
}
IOReturn
IOPCCardBridge::getNubResources(IOService * service)
{
IOPCIDevice * nub = (IOPCIDevice *) service;
IOReturn err;
if( service->getDeviceMemory())
return( kIOReturnSuccess );
err = getNubAddressing(nub);
return( err);
}
bool
IOPCCardBridge::getModuleParameters(void)
{
#ifdef PCMCIA_DEBUG
OSDictionary * debugDict = OSDynamicCast(OSDictionary, getProperty("Debug Settings"));
if (debugDict) {
extern int pc_debug, cb_debug, i82365_debug;
OSNumber * debugProp;
debugProp = OSDynamicCast(OSNumber, debugDict->getObject("Driver Services"));
if (debugProp) ds_debug = debugProp->unsigned32BitValue();
debugProp = OSDynamicCast(OSNumber, debugDict->getObject("Card Services"));
if (debugProp) pc_debug = debugProp->unsigned32BitValue();
debugProp = OSDynamicCast(OSNumber, debugDict->getObject("Card Bus"));
if (debugProp) cb_debug = debugProp->unsigned32BitValue();
debugProp = OSDynamicCast(OSNumber, debugDict->getObject("i82365"));
if (debugProp) i82365_debug = debugProp->unsigned32BitValue();
}
#endif
return true;
}
bool
IOPCCardBridge::getConfigurationSettings(void)
{
OSData * modelData = 0;
IORegistryEntry * root = 0;
IOLockLock(gIOPCCardBridgeLock);
if (gIOPCCardConfigurationInited) {
gIOPCCardConfigurationInited++;
} else {
if((root = IORegistryEntry::fromPath( "/", gIODTPlane ))) {
modelData = OSDynamicCast(OSData, root->getProperty("model"));
root->release();
}
if (!modelData) {
IOLog("IOPCCardBridge::getConfigurationSettings: can't find this platform's model name in registry?\n");
IOLockUnlock(gIOPCCardBridgeLock);
return false;
}
char * modelName = (char *)modelData->getBytesNoCopy();
OSDictionary * config = OSDynamicCast(OSDictionary, getProperty("Configuration Settings"));
if (!config) {
IOLog("IOPCCardBridge::getConfigurationSettings: Configuration Settings is not set?\n");
IOLockUnlock(gIOPCCardBridgeLock);
return false;
}
OSDictionary * settings = OSDynamicCast(OSDictionary, config->getObject(modelName));
#ifdef PCMCIA_DEBUG
if (!settings) settings = OSDynamicCast(OSDictionary, config->getObject("Test Machine"));
#endif
if (!settings) {
IOLog("IOPCCardBridge::getConfigurationSettings: can't find model name \"%s\" in Configuration Settings?\n", modelName);
IOLockUnlock(gIOPCCardBridgeLock);
return false;
}
gAvailableIORanges = OSDynamicCast(OSArray, settings->getObject("I/O Port Ranges"));
if (!gAvailableIORanges) {
IOLog("IOPCCardBridge::getConfigurationSettings: can't find \"I/O Port Ranges\" property for model name \"%s\" in Configuration Settings?\n");
IOLockUnlock(gIOPCCardBridgeLock);
return false;
}
gAvailableMemRanges = OSDynamicCast(OSArray, settings->getObject("Memory Ranges"));
if (!gAvailableMemRanges) {
IOLog("IOPCCardBridge::getConfigurationSettings: can't find \"Memory Ranges\" property for model name \"%s\" in Configuration Settings?\n");
IOLockUnlock(gIOPCCardBridgeLock);
return false;
}
#ifdef NOTYET
gAvailableIRQRanges = OSDynamicCast(OSArray, settings->getObject("IRQ Ranges"));
if (!gAvailableIRQRanges) {
IOLog("IOPCCardBridge::getConfigurationSettings: can't find \"IRQ Ranges\" property for model name \"%s\" in Configuration Settings?\n");
IOLockUnlock(gIOPCCardBridgeLock);
return false;
}
#endif
gIOPCCardConfigurationInited = 1;
}
IOLockUnlock(gIOPCCardBridgeLock);
return true;
}
bool
IOPCCardBridge::configureDynamicBridgeSpace(void)
{
unsigned int i, max;
int ret = 0;
OSData * range;
IOPhysicalAddress * p;
const unsigned int maxRangeIndex = 16; IOPhysicalRange ranges[maxRangeIndex];
if (!wackBridgeBusNumbers(bridgeDevice)) return false;
IOLockLock(gIOPCCardBridgeLock);
if (gIOPCCardResourcesInited) {
gIOPCCardResourcesInited++;
} else {
IOPhysicalAddress memStart = 0;
IOPhysicalAddress memEnd = 0;
IOPhysicalLength memLength = 0;
adjust_t adj;
adj.Action = ADD_MANAGED_RESOURCE;
adj.Attributes = 0;
max = gAvailableMemRanges->getCount();
if (max > maxRangeIndex) return false;
for (i=0; i < max; i++) {
range = OSDynamicCast(OSData, gAvailableMemRanges->getObject(i));
if (!range) return false;
if (range->getLength() != 8) return false;
p = (IOPhysicalAddress *)range->getBytesNoCopy();
memStart = *p++;
memEnd = *p;
memLength = (memEnd + 1) - memStart;
DEBUG(1, "adding bridge mem space 0x%x-0x%x\n", memStart, memEnd);
if (!addBridgeMemoryRange(memStart, memLength, false)) {
IOLog("IOPCCardBridge: failed to get bridge memory range 0x%x - 0x%x\n",
memStart, memStart + memLength - 1);
return false;
}
adj.Resource = RES_MEMORY_RANGE;
adj.resource.memory.Base = memStart;
adj.resource.memory.Size = memLength;
ret = CardServices(AdjustResourceInfo, CS_ADJUST_FAKE_HANDLE, &adj, NULL);
if (ret != CS_SUCCESS) {
cs_error(NULL, AdjustResourceInfo, ret);
return false;
}
ranges[i].address = (IOPhysicalAddress)memStart;
ranges[i].length = (IOByteCount)memLength;
}
gDynamicBridgeSpace = (IODeviceMemory *)IOMemoryDescriptor::withPhysicalRanges(ranges, max, kIODirectionNone);
IOPhysicalAddress ioStart = 0;
IOPhysicalAddress ioEnd = 0;
IOPhysicalLength ioLength = 0;
max = gAvailableIORanges->getCount();
if (max > maxRangeIndex) return false;
for (i=0; i < max; i++) {
range = OSDynamicCast(OSData, gAvailableIORanges->getObject(i));
if (!range) return false;
if (range->getLength() != 8) return false;
p = (IOPhysicalAddress *)range->getBytesNoCopy();
ioStart = *p++;
ioEnd = *p;
ioLength = (ioEnd + 1) - ioStart;
DEBUG(1, "adding bridge io space 0x%x-0x%x\n", ioStart, ioEnd);
if (!addBridgeIORange(ioStart, ioLength)) {
IOLog("IOPCCardBridge: failed to get bridge io range 0x%x - 0x%x\n",
ioStart, ioStart + ioLength - 1);
return false;
}
adj.Resource = RES_IO_RANGE;
adj.resource.io.BasePort = ioStart & 0x0000ffff;
if (ioLength > 0xffff) {
adj.resource.io.NumPorts = 0xffff;
} else {
adj.resource.io.NumPorts = ioLength;
}
adj.resource.io.IOAddrLines = 0;
ret = CardServices(AdjustResourceInfo, CS_ADJUST_FAKE_HANDLE, &adj, NULL);
if (ret != CS_SUCCESS) {
IOLockUnlock(gIOPCCardBridgeLock);
cs_error(NULL, AdjustResourceInfo, ret);
return false;
}
ranges[i].address = (IOPhysicalAddress)ioStart;
ranges[i].length = (IOByteCount)ioLength;
}
gDynamicBridgeIOSpace = (IODeviceMemory *)IOMemoryDescriptor::withPhysicalRanges(ranges, max, kIODirectionNone);
gIOPCCardResourcesInited = 1;
}
dynamicBridgeSpace = gDynamicBridgeSpace;
dynamicBridgeSpace->retain();
dynamicBridgeIOSpace = gDynamicBridgeIOSpace;
dynamicBridgeIOSpace->retain();
IOLockUnlock(gIOPCCardBridgeLock);
return true;
}
IODeviceMemory *
IOPCCardBridge::getDynamicBridgeSpace(void)
{
return dynamicBridgeSpace;
}
IODeviceMemory *
IOPCCardBridge::getDynamicBridgeIOSpace(void)
{
return dynamicBridgeIOSpace;
}
bool
IOPCCardBridge::initializeSocketServices(void)
{
return init_i82365(this, bridgeDevice, cardBusRegisterMap->getVirtualAddress()) == 0;
}
bool
IOPCCardBridge::initializeDriverServices(void)
{
client_reg_t client_reg;
servinfo_t serv;
bind_req_t bind;
socket_info_t *s;
int ret;
unsigned int i, starting_socket;
DEBUG(0, "%s\n", version);
CardServices(GetCardServicesInfo, &serv, NULL, NULL);
if (serv.Revision != CS_RELEASE_CODE) {
printk(KERN_NOTICE "ds: Card Services release does not match!\n");
return false;
}
if ((serv.Count == 0) || (serv.Count == sockets)) {
printk(KERN_NOTICE "ds: no new sockets found?\n");
return false;
}
starting_socket = sockets;
sockets = serv.Count;
if (starting_socket) {
DEBUG(2, "ds: reallocing socket_table, sockets = %d, starting_socket = %d\n", sockets, starting_socket);
socket_table = (socket_info_t *)krealloc(socket_table, sockets*sizeof(socket_info_t), GFP_KERNEL);
} else {
socket_table = (socket_info_t *)kmalloc(sockets*sizeof(socket_info_t), GFP_KERNEL);
}
if (!socket_table) return false;
for (i = starting_socket, s = socket_table+starting_socket; i < sockets; i++, s++) {
s->bridge = this;
s->state = 0;
s->configHandle = 0;
bzero(&s->config, sizeof(config_req_t));
s->removal.data = i;
s->removal.function = &cardRemovalHandler;
s->handle = NULL;
s->nubs = OSArray::withCapacity(1);
}
client_reg.dev_info = bind.dev_info = &dev_info;
client_reg.Attributes = INFO_MASTER_CLIENT;
client_reg.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
CS_EVENT_EJECTION_REQUEST | CS_EVENT_EJECTION_COMPLETE |
CS_EVENT_INSERTION_REQUEST | CS_EVENT_PM_RESUME;
client_reg.event_handler = &cardEventHandler;
client_reg.Version = 0x0210;
for (i = starting_socket; i < sockets; i++) {
bind.Socket = i;
bind.Function = BIND_FN_ALL;
ret = CardServices(BindDevice, &bind, NULL, NULL);
if (ret != CS_SUCCESS) {
cs_error(NULL, BindDevice, ret);
break;
}
client_reg.event_callback_args.client_data = (void *)i;
ret = CardServices(RegisterClient, &socket_table[i].handle,
&client_reg, NULL);
if (ret != CS_SUCCESS) {
cs_error(NULL, RegisterClient, ret);
break;
}
}
return true;
}
OSDictionary *
IOPCCardBridge::constructPCCard16Properties(IOPCCard16Device *nub)
{
int ret;
client_handle_t handle = nub->getCardServicesHandle();
OSDictionary * propTable = OSDictionary::withCapacity(8);
if (!propTable) return 0;
do {
OSNumber *numberProp;
cisinfo_t cisinfo;
ret = CardServices(ValidateCIS, handle, &cisinfo, NULL);
if (ret != CS_SUCCESS) {
cs_error(handle, ValidateCIS, ret);
break;
}
if (cisinfo.Chains > 0) {
cistpl_manfid_t manfid;
ret = read_tuple(handle, CISTPL_MANFID, &manfid, TUPLE_RETURN_COMMON);
if (ret == CS_SUCCESS) {
numberProp = OSNumber::withNumber(manfid.manf, 32);
if (!numberProp) break;
propTable->setObject(kIOPCCardVendorIDMatchKey, numberProp);
numberProp->release();
numberProp = OSNumber::withNumber(manfid.card, 32);
if (!numberProp) break;
propTable->setObject(kIOPCCardDeviceIDMatchKey, numberProp);
numberProp->release();
char nameStr[32];
sprintf(nameStr, "pccard%x,%x", manfid.manf, manfid.card);
const OSSymbol *nameProp = OSSymbol::withCString(nameStr);
if (!nameProp) break;
propTable->setObject(gIONameKey, (OSSymbol *) nameProp);
nameProp->release();
} else {
const OSSymbol *nameProp = OSSymbol::withCString("pccard-no-manfid");
if (!nameProp) break;
propTable->setObject(gIONameKey, (OSSymbol *) nameProp);
nameProp->release();
}
cistpl_vers_1_t version_1;
if (read_tuple(handle, CISTPL_VERS_1, &version_1, TUPLE_RETURN_COMMON) == CS_SUCCESS && (version_1.ns)) {
OSArray *vers1 = OSArray::withCapacity(version_1.ns);
if (!vers1) break;
propTable->setObject(kIOPCCardVersionOneMatchKey, vers1);
for (UInt32 i=0; i < version_1.ns; i++) {
const OSSymbol *str = OSSymbol::withCString(version_1.str+version_1.ofs[i]);
if (!str) break;
vers1->setObject(i, (OSSymbol *)str);
str->release();
}
vers1->release();
}
cistpl_funcid_t funcid;
funcid.func = CISTPL_FUNCID_INVALID; ret = read_tuple(handle, CISTPL_FUNCID, &funcid);
if (ret == CS_SUCCESS) {
numberProp = OSNumber::withNumber(funcid.func, 32);
if (!numberProp) break;
propTable->setObject(kIOPCCardFunctionIDMatchKey, numberProp);
numberProp->release();
static const char * functionNames[] = {
"Multi-Function", "Memory", "Serial Port", "Parallel Port", "Fixed Disk", "Video Adapter",
"Network Adapter", "AIMS", "SCSI", "Security", "Instrument"
};
const char *funcStr = 0;
if (funcid.func <= CISTPL_FUNCID_MAX_ID) {
funcStr = functionNames[funcid.func];
} else if (funcid.func == CISTPL_FUNCID_VENDOR) {
funcStr = "Vendor-Specific";
}
if (funcStr) {
const OSSymbol *funcProp = OSSymbol::withCString(funcStr);
if (!funcProp) break;
propTable->setObject(kIOPCCardFunctionNameMatchKey, (OSSymbol *)funcProp);
funcProp->release();
}
OSArray * funceArray = OSArray::withCapacity(10);
if (!funceArray) break;
tuple_t tuple;
cisdata_t buf[255];
tuple.DesiredTuple = CISTPL_FUNCE;
tuple.Attributes = TUPLE_RETURN_COMMON;
ret = CardServices(GetFirstTuple, handle, &tuple, NULL);
while (ret == CS_SUCCESS) {
tuple.TupleData = buf;
tuple.TupleOffset = 0;
tuple.TupleDataMax = sizeof(buf);
ret = CardServices(GetTupleData, handle, &tuple, NULL);
if ((ret == CS_SUCCESS) && (tuple.TupleDataLen > 0)) {
const OSData *funceEntry = OSData::withBytes(tuple.TupleData, tuple.TupleDataLen);
if (!funceEntry) break;
funceArray->setObject((OSSymbol *)funceEntry);
funceEntry->release();
}
ret = CardServices(GetNextTuple, handle, &tuple, NULL);
}
if (funceArray->getCount()) {
propTable->setObject(kIOPCCardFunctionExtensionMatchKey, (OSSymbol *)funceArray);
}
funceArray->release();
}
static const char * deviceNames[] = {
"No Device", "ROM", "OTPROM", "EPROM",
"EEPROM", "Flash", "SRAM", "DRAM",
"Reserved_8", "Reserved_9", "Reserved_A", "Reserved_B",
"Reserved_C", "Function Specific","Extended","Reserved_F"
};
if (funcid.func == CISTPL_FUNCID_MEMORY || funcid.func == CISTPL_FUNCID_INVALID) {
cistpl_device_t device;
ret = read_tuple(handle, CISTPL_DEVICE, &device, TUPLE_RETURN_COMMON);
if (ret != CS_SUCCESS) {
ret = read_tuple(handle, CISTPL_DEVICE_A, &device, TUPLE_RETURN_COMMON);
}
if (ret == CS_SUCCESS) {
const char *deviceStr = 0;
if (device.ndev > 0) deviceStr = deviceNames[device.dev[0].type];
if (deviceStr) {
const OSSymbol *deviceProp = OSSymbol::withCString(deviceStr);
if (!deviceProp) break;
propTable->setObject(kIOPCCardMemoryDeviceNameMatchKey, (OSSymbol *)deviceProp);
deviceProp->release();
}
}
#ifdef LATERXXX
cistpl_jedec_t jedec;
ret = read_tuple(handle, CISTPL_JEDEC_C, &device);
if (ret != CS_SUCCESS) {
ret = read_tuple(handle, CISTPL_JEDEC_A, &device);
}
if (ret == CS_SUCCESS) {
}
#endif
}
} else {
const OSSymbol *nameProp = OSSymbol::withCString("pccard-no-cis");
if (!nameProp) break;
propTable->setObject(gIONameKey, (OSSymbol *) nameProp);
nameProp->release();
const OSSymbol *funcProp = OSSymbol::withCString("Anonymous");
if (!funcProp) break;
propTable->setObject(kIOPCCardFunctionNameMatchKey, (OSSymbol *)funcProp);
funcProp->release();
}
} while (0);
return propTable;
}
OSDictionary *
IOPCCardBridge::constructCardBusProperties(IOPCIAddressSpace space)
{
OSDictionary * dict = super::constructProperties(space);
UInt32 classCode = configRead32(space, kIOPCIConfigClassCode & 0xfc) >> 8;
OSData *prop = OSData::withBytes( &classCode, 4 );
if (prop) {
dict->setObject("class-code", prop );
prop->release();
}
return dict;
}
void
IOPCCardBridge::constructCardBusCISProperties(IOCardBusDevice *nub)
{
client_handle_t handle = nub->getCardServicesHandle();
cistpl_vers_1_t version_1;
if (read_tuple(handle, CISTPL_VERS_1, &version_1, TUPLE_RETURN_COMMON) == CS_SUCCESS && (version_1.ns)) {
OSArray *vers1 = OSArray::withCapacity(version_1.ns);
if (!vers1) return;
nub->setProperty(kIOPCCardVersionOneMatchKey, vers1);
for (UInt32 i=0; i < version_1.ns; i++) {
const OSSymbol *str = OSSymbol::withCString(version_1.str+version_1.ofs[i]);
if (!str) return;
vers1->setObject(i, (OSSymbol *)str);
str->release();
}
vers1->release();
}
}
void
IOPCCardBridge::addNubInterruptProperties(OSDictionary * propTable)
{
int i;
OSArray * controller;
OSArray * specifier;
OSData * tmpData;
long tmpLong;
#define kNumNubVectors 1
specifier = OSArray::withCapacity(kNumNubVectors);
assert(specifier);
for (i = 0; i < kNumNubVectors; i++) {
tmpLong = i;
tmpData = OSData::withBytes(&tmpLong, sizeof(tmpLong));
specifier->setObject(tmpData);
}
controller = OSArray::withCapacity(kNumNubVectors);
assert(controller);
for (i = 0; i < kNumNubVectors; i++)
controller->setObject(interruptControllerName);
propTable->setObject(gIOInterruptControllersKey, controller);
propTable->setObject(gIOInterruptSpecifiersKey, specifier);
specifier->release();
controller->release();
}
bool
IOPCCardBridge::publishPCCard16Nub(IOPCCard16Device * nub, UInt32 socketIndex, UInt32 functionIndex)
{
if (nub) {
nub->setProperty("SocketNumber", socketIndex, 32);
nub->setProperty("FunctionNumber", functionIndex, 32);
char location[24];
sprintf(location, "%X,%X", socketIndex, functionIndex);
nub->setLocation(location);
if (nub->attach(this)) {
nub->registerService();
return true;
}
}
return false;
}
bool
IOPCCardBridge::publishCardBusNub(IOCardBusDevice * nub, UInt32 socketIndex, UInt32 functionIndex)
{
if (nub) {
nub->setProperty("SocketNumber", socketIndex, 32);
nub->setProperty("FunctionNumber", functionIndex, 32);
}
return super::publishNub(nub, socketIndex);
}
int
IOPCCardBridge::cardEventHandler(cs_event_t event, int priority,
event_callback_args_t *args)
{
DEBUG(1, "IOPCCardBridge::cardEventHandler(0x%06x, %d, 0x%p)\n",
event, priority, args->client_handle);
int ret;
int i = (int)args->client_data;
socket_info_t *s = &socket_table[i];
switch (event) {
case CS_EVENT_CARD_REMOVAL:
s->state &= ~SOCKET_PRESENT;
if (!(s->state & SOCKET_REMOVAL_PENDING)) {
s->state |= SOCKET_REMOVAL_PENDING;
s->removal.expires = jiffies + HZ/10;
add_timer(&s->removal);
}
break;
case CS_EVENT_CARD_INSERTION:
s->state |= SOCKET_PRESENT;
{
cs_status_t status;
status.Function = 0;
ret = CardServices(GetStatus, s->handle, &status, NULL);
if (ret != CS_SUCCESS) {
cs_error(s->handle, GetStatus, ret);
break;
}
int has_cis = 0;
cisinfo_t cisinfo;
ret = CardServices(ValidateCIS, s->handle, &cisinfo, NULL);
if (ret != CS_SUCCESS) cs_error(s->handle, ValidateCIS, ret);
DEBUG(2, "ValidateCIS returned 0x%x, chain count = %d\n", ret, cisinfo.Chains);
has_cis = ((ret == 0) && (cisinfo.Chains > 0));
if (status.CardState & CS_EVENT_CB_DETECT) {
int count = s->nubs->getCount();
for (int fn=0; fn < count; fn++) {
config_info_t config;
config.Function = fn;
ret = CardServices(GetConfigurationInfo, s->handle, &config, NULL);
if (ret != CS_SUCCESS) {
cs_error(s->handle, GetConfigurationInfo, ret);
break;
}
IOPCCardBridge *bus = (IOPCCardBridge *)config.PCCardNub;
IOCardBusDevice *nub = (IOCardBusDevice *)config.CardBusNub;
if (!bus || !nub) continue;
#ifdef PCMCIA_DEBUG
if (nub != s->nubs->getObject(fn)) {
IOLog("IOPCCardBridge::cardEventHandler: nub %p does not match nubs[%d]?\n", nub, fn);
continue;
}
#endif
nub->bindCardServices();
if (has_cis) bus->constructCardBusCISProperties(nub);
if (!bus->publishCardBusNub(nub, i, fn)) continue;
}
} else {
cistpl_longlink_mfc_t mfc;
mfc.nfn = 1;
if (has_cis) {
if (read_tuple(s->handle, CISTPL_LONGLINK_MFC, &mfc, TUPLE_RETURN_COMMON) == CS_SUCCESS)
DEBUG(2, "number of functions %d\n", mfc.nfn);
}
for (int fn=0; fn < 1; fn++) {
config_info_t config;
config.Function = fn;
ret = CardServices(GetConfigurationInfo, s->handle, &config, NULL);
if (ret != CS_SUCCESS) {
cs_error(s->handle, GetConfigurationInfo, ret);
continue;
}
IOPCCardBridge *bus = (IOPCCardBridge *)config.PCCardNub;
if (!bus) continue;
IOPCCard16Device * nub = bus->createPCCard16Nub(i, fn);
if (!nub) continue;
if (!bus->publishPCCard16Nub(nub, i, fn)) continue;
}
}
}
break;
case CS_EVENT_PM_RESUME:
if (s->state & SOCKET_CONFIG) {
ret = CardServices(RequestConfiguration, s->configHandle, &s->config, NULL);
if (ret) cs_error(s->configHandle, RequestConfiguration, ret);
}
if (s->bridge) (s->bridge)->acknowledgeSetPowerState();
break;
case CS_EVENT_EJECTION_COMPLETE:
{
const OSSymbol * matchString = OSSymbol::withCString(kIOPCCardEjectCategory);
if (!matchString) break;
IOService * provider = OSDynamicCast(IOService, s->bridge->getProvider());
if (!provider) break;
IOPCCardEjectController * driver = OSDynamicCast(IOPCCardEjectController,
provider->getClientWithCategory(matchString));
matchString->release();
if (!driver) break;
driver->ejectCard();
}
break;
default:
break;
}
return 0;
}
void
IOPCCardBridge::cardRemovalHandler(u_long sn)
{
socket_info_t *s = &socket_table[sn];
s->state &= ~SOCKET_REMOVAL_PENDING;
IOLog("IOPCCard: shutting down socket %d.\n", sn);
int count = s->nubs->getCount();
IOService *nub;
for (int fn=count - 1; fn >= 0; fn--) {
nub = OSDynamicCast(IOService, s->nubs->getObject(fn));
IOLog("IOPCCard: calling terminate on socket %d function %d nub %p.\n", sn, fn, nub);
if (nub && !nub->terminate(kIOServiceRequired | kIOServiceSynchronous)) {
IOLog("IOPCCard: terminate failed on nub 0x%x.\n", nub);
}
s->nubs->removeObject(fn);
}
if (s->state) {
IOLog("IOPCCard: socket %d did not terminate cleanly, state = 0x%x.\n", sn, s->state);
s->state = 0;
s->configHandle = 0;
bzero(&s->config, sizeof(config_req_t));
}
}
int
IOPCCardBridge::cardServicesGate(IOService *, void *func, void * arg1, void * arg2, void * arg3)
{
int function = (int)func;
if (function >= 0) {
return CardServices(function, arg1, arg2, arg3);
} else {
switch (function) {
case kCSGateProbeBridge: {
IOPCCardBridge *bridge = OSDynamicCast(IOPCCardBridge, (OSObject *)arg1);
if (!bridge) break;
if (!bridge->initializeSocketServices()) break;
if (!bridge->initializeDriverServices()) break;
return 0;
}
case kCSGateTimerCallout: {
struct timer_list * timer = (struct timer_list *)arg1;
(*timer->function)(timer->data);
return 0;
}
case kCSGateSetBridgePower: {
IOPCCardBridge *bridge = OSDynamicCast(IOPCCardBridge, (OSObject *)arg1);
if (!bridge) break;
return bridge->setBridgePowerState((unsigned long)arg2, (IOService *)arg3);
}
default:
break;
}
}
return -1;
}
void
IOPCCardBridge::interruptDispatcher(void)
{
DEBUG(4, "***** Entering IOPCCardBridge::interruptDispatcher\n");
interrupt_handler_t * h = interruptHandlers;
while (h) {
h->top_handler(h->socket);
h = h->next;
}
DEBUG(4, "***** Exiting IOPCCardBridge::interruptDispatcher\n");
}
IOPCCard16Device *
IOPCCardBridge::createPCCard16Nub(UInt8 socket, UInt8 function)
{
OSDictionary * propTable = 0;
bool isBound = false;
IOPCCard16Device *nub = new IOPCCard16Device;
if (!nub) return 0;
nub->socket = socket;
nub->function = function;
do {
if (!nub->bindCardServices()) break;
isBound = true;
propTable = constructPCCard16Properties(nub);
if (!propTable) break;
if (ioDeviceMemory())
nub->ioMap = ioDeviceMemory()->map();
addNubInterruptProperties(propTable);
if (!nub->init(propTable)) break;
socket_table[socket].nubs->setObject(nub);
nub->release();
return nub;
} while (false);
if (isBound) nub->unbindCardServices();
if (propTable) propTable->release();
nub->release();
return 0;
}
IOCardBusDevice *
IOPCCardBridge::createCardBusNub(UInt8 socket, UInt8 function)
{
IOPCIAddressSpace space;
space.bits = 0;
space.s.busNum = firstBusNum();
space.s.deviceNum = 0;
space.s.functionNum = function;
UInt32 vendor = configRead32(space, 0) & 0x0000ffff;
DEBUG(2, "IOPCCardBridge: createCardBusNub socket %d, function %d, vendor id = 0x%x\n", socket, function, vendor);
if ((vendor == 0) || (vendor == 0xffff)) {
return 0;
}
OSDictionary * propTable = constructCardBusProperties(space);
if (propTable) {
IOCardBusDevice *nub = new IOCardBusDevice;
if (!nub) {
propTable->release();
return 0;
}
nub->socket = socket;
nub->function = function;
spaceFromProperties(propTable, &((struct IOCardBusDevice *)nub)->space);
nub->parent = this;
if (ioDeviceMemory())
nub->ioMap = ioDeviceMemory()->map();
addNubInterruptProperties(propTable);
if (!nub->init(propTable)) propTable->release();
socket_table[socket].nubs->setObject(nub);
nub->release();
return nub;
}
return 0;
}
bool
IOPCCardBridge::addCSCInterruptHandler(unsigned int socket, unsigned int irq,
int (*top_handler)(int), int (*bottom_handler)(int),
int (*enable_functional)(int), int (*disable_functional)(int),
const char* name)
{
struct interrupt_handler * h = (struct interrupt_handler *)IOMalloc(sizeof(interrupt_handler_t));
if (!h) return false;
h->socket = socket;
h->irq = irq;
h->top_handler = top_handler;
h->bottom_handler = bottom_handler;
h->enable_functional = enable_functional;
h->disable_functional = disable_functional;
h->name = name;
h->interruptSource = interruptSource;
bridgeDevice->disableInterrupt(0);
h->next = interruptHandlers;
interruptHandlers = h;
interruptController->updateCSCInterruptHandlers(interruptHandlers);
bridgeDevice->enableInterrupt(0);
return true;
}
bool
IOPCCardBridge::removeCSCInterruptHandler(unsigned int socket)
{
interrupt_handler_t * prev = NULL; interrupt_handler_t * h = interruptHandlers;
bridgeDevice->disableInterrupt(0);
while (h) {
if (h->socket == socket) {
if (h == interruptHandlers) {
interruptHandlers = h->next;
} else {
prev->next = h->next;
}
IOFree(h, sizeof(interrupt_handler_t));
}
prev = h;
h = h->next;
}
interruptController->updateCSCInterruptHandlers(interruptHandlers);
bridgeDevice->enableInterrupt(0);
return true;
}
IOService *
IOPCCardBridge::probe(IOService * provider, SInt32 * score)
{
if (!getModuleParameters()) return 0;
DEBUG(2, "IOPCCardBridge::probe\n");
if (!getConfigurationSettings()) return 0;
bridgeDevice = OSDynamicCast(IOPCIDevice, provider);
if (!bridgeDevice) return 0;
return super::probe(provider, score);
}
bool
IOPCCardBridge::start(IOService * provider)
{
IOReturn error;
DEBUG(2, "IOPCCardBridge::start\n");
bridgeDevice = OSDynamicCast(IOPCIDevice, provider);
if (!bridgeDevice) return false;
do {
bool success = false;
IOLockLock(gIOPCCardBridgeLock);
if (gIOPCCardGlobalsInited) {
gIOPCCardGlobalsInited++;
success = true;
} else {
gIOPCCardMappedBridgeSpace = OSSet::withCapacity(16);
gIOPCCardWorkLoop = IOWorkLoop::workLoop();
gIOPCCardCommandGate = IOCommandGate::commandGate((OSObject *)0x50434344, (IOCommandGate::Action)cardServicesGate);
success = gIOPCCardMappedBridgeSpace && gIOPCCardWorkLoop && gIOPCCardCommandGate;
success &= !gIOPCCardWorkLoop->addEventSource(gIOPCCardCommandGate);
if (success) gIOPCCardGlobalsInited = 1;
}
IOLockUnlock(gIOPCCardBridgeLock);
if (!success) break;
if (!super::start(provider)) break;
changePowerStateTo(kIOPCIDeviceOnState);
interruptController = new IOPCCardInterruptController;
if (interruptController == NULL) break;
error = interruptController->initInterruptController(provider);
if (error != kIOReturnSuccess) break;
IOInterruptAction handler = interruptController->getInterruptHandlerAddress();
provider->registerInterrupt(0, interruptController, handler, 0);
UInt32 uniqueNumber = (bridgeDevice->getBusNumber() << 16) |
(bridgeDevice->getDeviceNumber() << 8) |
bridgeDevice->getFunctionNumber();
char * uniqueName = kInterruptControllerNamePrefix "12345678";
sprintf(uniqueName, "%s%08x", kInterruptControllerNamePrefix, uniqueNumber);
interruptControllerName = (OSSymbol *)OSSymbol::withCString(uniqueName);
if (!interruptControllerName) break;
getPlatform()->registerInterruptController(interruptControllerName, interruptController);
interruptControllerName->release();
interruptSource = IOInterruptEventSource::interruptEventSource((OSObject *) this,
(IOInterruptEventAction)&IOPCCardBridge::interruptDispatcher,
(IOService *) 0,
(int) 0);
if (!interruptSource) break;
if (gIOPCCardWorkLoop->addEventSource(interruptSource)) break;
bridgeDevice->enableInterrupt(0);
configureDynamicBridgeSpace();
bridgeDevice->setMemoryEnable(true);
bridgeDevice->setIOEnable(true);
bridgeDevice->setBusMasterEnable(true);
cardBusRegisterMap = bridgeDevice->mapDeviceMemoryWithRegister(0x10);
if (!cardBusRegisterMap) break;
return gIOPCCardCommandGate->runCommand((void *)kCSGateProbeBridge, (void *)this, NULL, NULL) == 0;
} while (false);
IOLog("IOPCCardBridge::start failed\n");
stop(provider);
return false;
}
bool
IOPCCardBridge::configure(IOService * provider)
{
DEBUG(2, "IOPCCardBridge::configure\n");
return true;
}
void
IOPCCardBridge::stop(IOService * provider)
{
DEBUG(2, "IOPCCardBridge::stop\n");
if (dynamicBridgeSpace) dynamicBridgeSpace->release();
if (dynamicBridgeIOSpace) dynamicBridgeIOSpace->release();
if (cardBusRegisterMap) cardBusRegisterMap->release();
gIOPCCardWorkLoop->removeEventSource(interruptSource);
if (interruptSource) interruptSource->release();
bridgeDevice->unregisterInterrupt(0);
if (interruptController) interruptController->release();
super::stop(provider);
}
IOReturn
IOPCCardBridge::setPowerState(unsigned long powerState,
IOService * whatDevice)
{
DEBUG(1, "IOPCCardBridge::setPowerState state=%d\n", powerState);
return gIOPCCardCommandGate->runCommand((void *)kCSGateSetBridgePower, (void *)this, (void *)powerState, (void *)whatDevice);
}
IOReturn
IOPCCardBridge::setBridgePowerState(unsigned long powerState,
IOService * whatDevice)
{
int ret;
DEBUG(1, "IOPCCardBridge::setBridgePowerState state=%d\n", powerState);
for (unsigned i=0; i < sockets; i++) {
socket_info_t *s = &socket_table[i];
if (s->bridge != this) continue;
if ((powerState == kIOPCIDeviceOnState) || (powerState == 1)) {
if (!(s->state & SOCKET_SUSPEND)) return IOPMAckImplied;
s->state &= ~SOCKET_SUSPEND;
bridgeDevice->enableInterrupt(0);
if (s->state & SOCKET_PRESENT) {
ret = CardServices(ResumeCard, s->handle, NULL, NULL);
if (ret) cs_error(s->configHandle, ResumeCard, ret);
return 6000000;
}
} else if (powerState == kIOPCIDeviceOffState) {
if (s->state & SOCKET_SUSPEND) return IOPMAckImplied;
s->state |= SOCKET_SUSPEND;
if (s->state & SOCKET_CONFIG) {
ret = CardServices(ReleaseConfiguration, s->configHandle, &s->config, NULL);
if (ret) cs_error(s->configHandle, ReleaseConfiguration, ret);
}
if (s->state & SOCKET_PRESENT) {
ret = CardServices(SuspendCard, s->handle, NULL, NULL);
if (ret) cs_error(s->configHandle, SuspendCard, ret);
}
bridgeDevice->disableInterrupt(0);
}
}
return IOPMAckImplied;
}
static socket_info_t *
getSocketInfo(IOService * nub)
{
for (unsigned i=0; i < sockets; i++) {
OSArray *nubs = socket_table[i].nubs;
if (!nubs) return 0;
int j = 0;
while (IOService *funcNub = (IOService *)nubs->getObject(j++)) {
if (nub == funcNub) return &socket_table[i];
}
}
return 0;
}
int
IOPCCardBridge::configureSocket(IOService * nub, config_req_t * configuration)
{
DEBUG(1, "IOPCCardBridge::configureSocket nub=%p\n", nub);
socket_info_t *s = getSocketInfo(nub);
if (!s) return CS_BAD_HANDLE;
if (s->state & SOCKET_CONFIG) return CS_CONFIGURATION_LOCKED;
client_handle_t handle = 0;
if (IOCardBusDevice *cardBusNub = OSDynamicCast(IOCardBusDevice, (OSObject *)nub)) {
handle = cardBusNub->getCardServicesHandle();
} else if (IOPCCard16Device *pccard16Nub = OSDynamicCast(IOPCCard16Device, (OSObject *)nub)) {
handle = pccard16Nub->getCardServicesHandle();
}
if (!handle) return CS_BAD_HANDLE;
s->state |= SOCKET_CONFIG;
int ret = CardServices(RequestConfiguration, handle, configuration, NULL);
if (ret) {
cs_error(handle, RequestConfiguration, ret);
return ret;
}
s->config = *configuration;
s->configHandle = handle;
DEBUG(1, "IOPCCardBridge::configureSocket completed successfully\n");
return CS_SUCCESS;
}
int
IOPCCardBridge::unconfigureSocket(IOService * nub)
{
DEBUG(1, "IOPCCardBridge::unconfigureSocket nub=%p\n", nub);
socket_info_t *s = getSocketInfo(nub);
if (!s) return CS_BAD_HANDLE;
if (!(s->state & SOCKET_CONFIG)) return CS_BAD_HANDLE; if (s->state & SOCKET_SUSPEND) return CS_BUSY;
client_handle_t handle = 0;
if (IOCardBusDevice *cardBusNub = OSDynamicCast(IOCardBusDevice, (OSObject *)nub)) {
handle = cardBusNub->getCardServicesHandle();
} else if (IOPCCard16Device *pccard16Nub = OSDynamicCast(IOPCCard16Device, (OSObject *)nub)) {
handle = pccard16Nub->getCardServicesHandle();
}
if (!handle) return CS_BAD_HANDLE;
int ret = CardServices(ReleaseConfiguration, handle, &s->config, NULL);
if (ret) {
cs_error(handle, ReleaseConfiguration, ret);
return ret;
}
s->state &= ~SOCKET_CONFIG;
s->configHandle = 0;
bzero(&s->config, sizeof(config_req_t));
DEBUG(1, "IOPCCardBridge::unconfigureSocket completed successfully\n");
return CS_SUCCESS;
}
IOWorkLoop *
IOPCCardBridge::getWorkLoop() const
{
return gIOPCCardWorkLoop;
}
IODeviceMemory *
IOPCCardBridge::ioDeviceMemory(void)
{
return getDynamicBridgeIOSpace();
}
int
IOPCCardBridge::requestCardEjection(IOService * bridgeDevice)
{
for (unsigned i = 0; i < sockets; i++) {
if (socket_table[i].handle &&
socket_table[i].bridge &&
socket_table[i].bridge->getProvider() == bridgeDevice) {
return gIOPCCardCommandGate->runCommand((void *)EjectCard, socket_table[i].handle, 0, 0);
}
}
return CS_BAD_ADAPTER;
}
#undef super
#define super IOInterruptController
OSDefineMetaClassAndStructors(IOPCCardInterruptController, IOInterruptController);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 0);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 1);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 2);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 3);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 4);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 5);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 6);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 7);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 8);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 9);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 10);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 11);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 12);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 13);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 14);
OSMetaClassDefineReservedUnused(IOPCCardInterruptController, 15);
#define kNumVectors (16)
IOReturn
IOPCCardInterruptController::initInterruptController(IOService *provider)
{
int cnt;
IOPCIDevice *bridgeDevice = OSDynamicCast(IOPCIDevice, provider);
if (!bridgeDevice) {
IOLog("IOPCCardInterruptController::initInterruptController failed\n");
return kIOReturnBadArgument;
}
vectors = (IOInterruptVector *)IOMalloc(kNumVectors * sizeof(IOInterruptVector));
if (vectors == NULL) return kIOReturnNoMemory;
bzero(vectors, kNumVectors * sizeof(IOInterruptVector));
for (cnt = 0; cnt < kNumVectors; cnt++) {
vectors[cnt].interruptLock = IOLockAlloc();
if (vectors[cnt].interruptLock == NULL) {
for (cnt = 0; cnt < kNumVectors; cnt++) {
if (vectors[cnt].interruptLock != NULL)
IOLockFree(vectors[cnt].interruptLock);
}
return kIOReturnNoResources;
}
}
return kIOReturnSuccess;
}
IOInterruptAction
IOPCCardInterruptController::getInterruptHandlerAddress(void)
{
return (IOInterruptAction)&IOPCCardInterruptController::handleInterrupt;
}
#ifdef __ppc__
#define sync() __asm__ volatile("sync")
#define isync() __asm__ volatile("isync")
#endif
IOReturn
IOPCCardInterruptController::handleInterrupt(void *,
IOService *,
int )
{
long vectorNumber;
IOInterruptVector *vector;
interrupt_handler_t * h = interruptHandlers;
while (h) {
unsigned int event = h->bottom_handler(h->socket);
if (event) h->interruptSource->interruptOccurred(0, 0, 0);
h = h->next;
}
pendingEvents = 0;
for (vectorNumber=0; vectorNumber < kNumVectors; vectorNumber++) {
vector = &vectors[vectorNumber];
vector->interruptActive = 1;
#ifdef __ppc__
sync();
isync();
#endif
if (!vector->interruptDisabledSoft) {
#ifdef __ppc__
isync();
#endif
if (vector->interruptRegistered) {
vector->handler(vector->target, vector->refCon,
vector->nub, vector->source);
}
} else {
vector->interruptDisabledHard = 1;
disableVectorHard(vectorNumber, vector);
}
vector->interruptActive = 0;
}
return kIOReturnSuccess;
}
bool
IOPCCardInterruptController::vectorCanBeShared(long , IOInterruptVector *)
{
return true;
}
int
IOPCCardInterruptController::getVectorType(long ,
IOInterruptVector *)
{
return kIOInterruptTypeLevel;
}
void
IOPCCardInterruptController::disableVectorHard(long vectorNumber, IOInterruptVector *)
{
interrupt_handler_t * h = interruptHandlers;
while (h) {
h->disable_functional(h->socket);
h = h->next;
}
}
void
IOPCCardInterruptController::enableVector(long vectorNumber,
IOInterruptVector *vector)
{
interrupt_handler_t * h = interruptHandlers;
while (h) {
h->enable_functional(h->socket);
h = h->next;
}
}
void
IOPCCardInterruptController::causeVector(long vectorNumber,
IOInterruptVector *)
{
pendingEvents |= 1 << vectorNumber;
bridgeDevice->causeInterrupt(0);
}
bool
IOPCCardInterruptController::updateCSCInterruptHandlers(interrupt_handler_t *handlers)
{
interruptHandlers = handlers;
return true;
}
IOReturn
IOPCCardInterruptController::getInterruptType(int source, int *interruptType)
{
*interruptType = getVectorType(0 , 0);
return kIOReturnSuccess;
}