/* * Copyright (c) 1998-2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1999-2002 Apple Computer, Inc. All rights reserved. * * HISTORY * */ #include #include #include #include #include #include #include #include OSDefineMetaClassAndStructors(IOFWIsochChannel, OSObject) OSMetaClassDefineReservedUnused(IOFWIsochChannel, 0); OSMetaClassDefineReservedUnused(IOFWIsochChannel, 1); OSMetaClassDefineReservedUnused(IOFWIsochChannel, 2); OSMetaClassDefineReservedUnused(IOFWIsochChannel, 3); void IOFWIsochChannel::threadFunc(thread_call_param_t arg, thread_call_param_t) { ((IOFWIsochChannel *)arg)->reallocBandwidth(); } bool IOFWIsochChannel::init(IOFireWireController *control, bool doIRM, UInt32 packetSize, IOFWSpeed prefSpeed, FWIsochChannelForceStopNotificationProcPtr stopProc, void *stopRefCon) { if(!OSObject::init()) return false; fControl = control; fDoIRM = doIRM; fPacketSize = packetSize; fPrefSpeed = prefSpeed; fStopProc = stopProc; fStopRefCon = stopRefCon; fTalker = NULL; fListeners = OSSet::withCapacity(1); fChannel = 64; // Illegal channel fReadCmd = new IOFWReadQuadCommand; if(fReadCmd) fReadCmd->initAll(fControl, 0, FWAddress(), NULL, 0, NULL, NULL); fLockCmd = new IOFWCompareAndSwapCommand; if(fLockCmd) fLockCmd->initAll(fControl, 0, FWAddress(), NULL, NULL, 0, NULL, NULL); return fListeners != NULL && fReadCmd != NULL && fLockCmd != NULL; } void IOFWIsochChannel::free() { if(fListeners) fListeners->release(); if(fReadCmd) fReadCmd->release(); if(fLockCmd) fLockCmd->release(); OSObject::free(); } IOReturn IOFWIsochChannel::setTalker(IOFWIsochPort *talker) { fTalker = talker; return kIOReturnSuccess; } IOReturn IOFWIsochChannel::addListener(IOFWIsochPort *listener) { if(fListeners->setObject(listener)) return kIOReturnSuccess; else return kIOReturnNoMemory; } IOReturn IOFWIsochChannel::updateBandwidth(bool claim) { FWAddress addr(kCSRRegisterSpaceBaseAddressHi, kCSRBandwidthAvailable); UInt16 irm; UInt32 generation; UInt32 newVal; UInt32 oldVal; IOReturn result = kIOReturnSuccess; bool tryAgain; do { fControl->getIRMNodeID(generation, irm); // Check we aren't being asked to reclaim in the same generation we originally allocated if(claim && fGeneration == generation) return kIOReturnSuccess; if (!claim && fGeneration != generation) { fChannel = 64 ; fBandwidth = 0 ; return kIOReturnSuccess ; } addr.nodeID = irm; if(fBandwidth != 0) { fReadCmd->reinit(generation, addr, &oldVal, 1); result = fReadCmd->submit(); if(kIOReturnSuccess != result) { IOLog("IRM read result 0x%x\n", result); break; } do { if(claim) { if(oldVal < fBandwidth) { result = kIOReturnNoSpace; break; } newVal = oldVal - fBandwidth; } else newVal = oldVal + fBandwidth; fLockCmd->reinit(generation, addr, &oldVal, &newVal, 1); result = fLockCmd->submit(); if(kIOReturnSuccess != result) { IOLog("bandwidth update result 0x%x\n", result); break; } tryAgain = !fLockCmd->locked(&oldVal); } while (tryAgain); if(claim && kIOReturnNoSpace == result) { // Couldn't reallocate bandwidth! fBandwidth = 0; fChannel = 64; } if(!claim) fBandwidth = 0; } if(fChannel != 64) { UInt32 mask; if(fChannel <= 31) { addr.addressLo = kCSRChannelsAvailable31_0; mask = 1 << (31-fChannel); } else { addr.addressLo = kCSRChannelsAvailable63_32; mask = 1 << (63-fChannel); } fReadCmd->reinit(generation, addr, &oldVal, 1); result = fReadCmd->submit(); if(kIOReturnSuccess != result) { break; } do { if(claim) { newVal = oldVal & ~mask; if(newVal == oldVal) { // Channel already allocated! result = kIOReturnNoSpace; break; } } else { newVal = oldVal | mask; } fLockCmd->reinit(generation, addr, &oldVal, &newVal, 1); result = fLockCmd->submit(); if(kIOReturnSuccess != result) { IOLog("channel update result 0x%x\n", result); break; } tryAgain = !fLockCmd->locked(&oldVal); } while (tryAgain); if(!claim) fChannel = 64; } } while (false); if(claim && kIOReturnNoSpace == result) { // Couldn't reallocate bandwidth or channel stop(); releaseChannel(); } fGeneration = generation; return result; } IOReturn IOFWIsochChannel::allocateChannelBegin( IOFWSpeed inSpeed, // to calculate bandwidth number UInt64 inAllowedChans, UInt32& outChannel ) { UInt32 generation ; UInt32 newVal ; FWAddress addr(kCSRRegisterSpaceBaseAddressHi, kCSRBandwidthAvailable) ; UInt32 oldIRM[3] ; UInt32 channel ; IOReturn err = kIOReturnSuccess ; // reserve bandwidth: if(fDoIRM) { // bandwidth is in units of quads at 1600 Mbs UInt32 bandwidth = (fPacketSize/4 + 3) * 16 / (1 << inSpeed); // get IRM nodeID into addr.nodeID fControl->getIRMNodeID(generation, addr.nodeID); // read IRM into oldIRM[3] (up to 5 retries) ; fReadCmd->reinit(generation, addr, oldIRM, 3); fReadCmd->setMaxPacket(4); // many cameras don't like block reads to IRM registers, eg. Canon GL-1 err = fReadCmd->submit(); // Claim bandwidth from IRM if ( !err && (oldIRM[0] < bandwidth) ) err = kIOReturnNoSpace; if (!err) { newVal = oldIRM[0] - bandwidth; fLockCmd->reinit(generation, addr, &oldIRM[0], &newVal, 1); err = fLockCmd->submit(); if ( !err && !fLockCmd->locked(& oldIRM[0]) ) err = kIOReturnCannotLock ; if (!err) fBandwidth = bandwidth; } } if ( !err && fDoIRM ) { // mask inAllowedChans by channels IRM has available inAllowedChans &= (UInt64)(oldIRM[2]) | ((UInt64)oldIRM[1] << 32); } // if we have an error here, the bandwidth wasn't allocated if (!err) { for(channel=0; channel<64; channel++) { if( inAllowedChans & ((UInt64)1 << ( 63 - channel )) ) break; } if(channel == 64) err = kIOReturnNoResources; } if (!err && fDoIRM) { UInt32* oldPtr; // Claim channel if(channel < 32) { addr.addressLo = kCSRChannelsAvailable31_0; oldPtr = &oldIRM[1]; newVal = *oldPtr & ~(1<<(31 - channel)); } else { addr.addressLo = kCSRChannelsAvailable63_32; oldPtr = &oldIRM[2]; newVal = *oldPtr & ~( (UInt64)1 << (63 - channel) ); } fLockCmd->reinit(generation, addr, oldPtr, &newVal, 1); err = fLockCmd->submit(); if (!err && !fLockCmd->locked(oldPtr)) err = kIOReturnCannotLock ; } if (!err) outChannel = channel; if(!err && fDoIRM) { fGeneration = generation; fControl->addAllocatedChannel(this); // we used to allocate the hardware resources for each port by calling AllocatePort() // on each one here, but that code has moved... // It now happens (after this function ends) in user space or kernel space depending // on where the end client lives. // we had to do this to avoid deadlocking user apps } return err ; } IOReturn IOFWIsochChannel::releaseChannelComplete() { // release bandwidth and channel if(fDoIRM) { // // Tell the controller that we don't need to know about // bus resets before doing anything else, since a bus reset // sets us into the state we want (no allocated bandwidth). // fControl->removeAllocatedChannel(this); updateBandwidth(false); } return kIOReturnSuccess; } IOReturn IOFWIsochChannel::allocateChannel() { UInt64 portChans; UInt64 allowedChans ; IOFWIsochPort* listen; IOFWSpeed portSpeed; OSIterator * listenIterator = NULL; IOReturn result = kIOReturnSuccess; // Get best speed, minimum of requested speed and paths from talker to each listener fSpeed = fPrefSpeed; do { // reduce speed to minimum of so far and what all ports can do, // and find valid channels allowedChans = ~(UInt64)0; if(fTalker) { fTalker->getSupported(portSpeed, portChans); if(portSpeed < fSpeed) fSpeed = portSpeed; allowedChans &= portChans; } listenIterator = OSCollectionIterator::withCollection(fListeners); if(listenIterator) { while( (listen = (IOFWIsochPort *) listenIterator->getNextObject())) { listen->getSupported(portSpeed, portChans); if(portSpeed < fSpeed) fSpeed = portSpeed; allowedChans &= portChans; } } // reserve bandwidth, allocate a channel do { result = allocateChannelBegin( fSpeed, allowedChans, fChannel ) ; } while ( result == kIOFireWireBusReset || result == kIOReturnCannotLock ) ; if ( kIOReturnSuccess != result ) break ; // allocate hardware resources for each port if(listenIterator) { listenIterator->reset(); while( (listen = (IOFWIsochPort *) listenIterator->getNextObject()) && (result == kIOReturnSuccess)) { result = listen->allocatePort(fSpeed, fChannel); } if(result != kIOReturnSuccess) break; } if(fTalker) result = fTalker->allocatePort(fSpeed, fChannel); } while (false); if(listenIterator) listenIterator->release(); if(result != kIOReturnSuccess) { releaseChannel(); } return result; } IOReturn IOFWIsochChannel::releaseChannel() { OSIterator *listenIterator; IOFWIsochPort *listen; if(fTalker) { fTalker->releasePort(); } listenIterator = OSCollectionIterator::withCollection(fListeners); if(listenIterator) { while( (listen = (IOFWIsochPort *) listenIterator->getNextObject())) { listen->releasePort(); } listenIterator->release(); } /* // release bandwidth and channel if(fDoIRM) { // Tell the controller that we don't need to know about // bus resets before doing anything else, since a bus reset // sets us into the state we want (no allocated bandwidth). fControl->removeAllocatedChannel(this); updateBandwidth(false); } */ return releaseChannelComplete() ; } void IOFWIsochChannel::reallocBandwidth() { updateBandwidth(true); } void IOFWIsochChannel::handleBusReset() { thread_call_func(threadFunc, this, true); } IOReturn IOFWIsochChannel::start() { OSIterator *listenIterator; IOFWIsochPort *listen; // Start all listeners, then start the talker listenIterator = OSCollectionIterator::withCollection(fListeners); if(listenIterator) { listenIterator->reset(); while( (listen = (IOFWIsochPort *) listenIterator->getNextObject())) { listen->start(); } listenIterator->release(); } if(fTalker) fTalker->start(); return kIOReturnSuccess; } IOReturn IOFWIsochChannel::stop() { OSIterator *listenIterator; IOFWIsochPort *listen; // Stop all listeners, then stop the talker listenIterator = OSCollectionIterator::withCollection(fListeners); if(listenIterator) { while( (listen = (IOFWIsochPort *) listenIterator->getNextObject())) { listen->stop(); } listenIterator->release(); } if(fTalker) fTalker->stop(); return kIOReturnSuccess; }