kernel/eka/drivers/pbus/spbus.cpp
changeset 0 a41df078684a
child 39 5d2844f35677
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/drivers/pbus/spbus.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1121 @@
+// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// e32\drivers\pbus\spbus.cpp
+// 
+//
+
+#include <drivers/pbus.h>
+
+const TInt KPBusSocketThreadPriority=26;
+
+GLDEF_D DMediaChangeBase* TheMediaChanges[KMaxMediaChanges];
+GLDEF_D DPBusSocket* TheSockets[KMaxPBusSockets];
+GLDEF_D DPBusPsuBase* TheVccs[KMaxPBusVccs];
+GLDEF_D DPBusPsuBase* TheVccCores[KMaxPBusVccs]; 
+
+/********************************************
+ * Peripheral bus callback
+ ********************************************/
+EXPORT_C TPBusCallBack::TPBusCallBack()
+	:	iSocket(NULL), iFunction(NULL), iIntMask(0), iPtr(NULL)
+	{
+	iNext=NULL;
+	}
+
+EXPORT_C TPBusCallBack::TPBusCallBack(TPBusCallBackFn aFunction, TAny* aPtr)
+	: iSocket(NULL), iFunction(aFunction), iIntMask(0), iPtr(aPtr)
+	{
+	iNext=NULL;
+	}
+
+EXPORT_C TPBusCallBack::TPBusCallBack(TPBusIsr anIsr, TAny* aPtr, TUint anIntMask)
+	: iSocket(NULL), iFunction(NULL), iIntMask(anIntMask), iIsr(anIsr), iPtr(aPtr)
+	{
+	iNext=NULL;
+	}
+
+EXPORT_C void TPBusCallBack::Remove()
+	{
+	TInt irq=NKern::DisableAllInterrupts();
+	if (iNext)
+		Deque();
+	iNext=NULL;
+	NKern::RestoreInterrupts(irq);
+	}
+
+EXPORT_C void TPBusCallBack::SetSocket(TInt aSocket)
+	{
+	iSocket=TheSockets[aSocket];
+	}
+
+/********************************************
+ * Media change base class
+ ********************************************/
+ 
+/**
+ * Constructor for a DMediaChangeBase object.
+ *
+ * @param aMediaChangeNum The media change number
+ */
+EXPORT_C DMediaChangeBase::DMediaChangeBase(TInt aMediaChangeNum)
+	:	iMediaChangeNum(aMediaChangeNum),
+		iReplyCount(0),
+		iDoorOpenDfc(DoorOpenDfcFn,this,Kern::DfcQue1(),1)
+	{
+	}
+
+
+/**
+ * Creates a DMediaChangeBase object.
+ * This should be overridden at the media and variant layer to allow
+ * interrupts and other media/variant-specific parameters to be initialised.
+ *
+ * Method should be called post object creation, although could be used to
+ * re-initialise parameters.
+ *
+ * @return KErrNone Default
+ */
+EXPORT_C TInt DMediaChangeBase::Create()
+	{
+	return KErrNone;
+	}
+
+/**
+ * Called from ISR triggered by media change or from 
+ * the Peripheral Bus Controller Media Driver context
+ * if a media change is being forced.
+ *
+ * Method adds/enques a media change event on to the door 
+ * open DFC queue. If called by PBUS thread then DFC queue 
+ * is by-passed and change event is dealt with synchronously.
+ *
+ * Media change events are platform specific although are 
+ * generally related to a media door or slot being opened.
+ */
+EXPORT_C void DMediaChangeBase::DoorOpenService()
+	{
+	if (NKern::CurrentContext()==NKern::EInterrupt)
+		iDoorOpenDfc.Add();
+	else 
+		{
+		if (Kern::DfcQue1()->iThread==(NThreadBase *)NKern::CurrentThread()) 	// check if this is being called from PBUS thread
+			MediaChangeEvent(ETrue);
+		else
+			iDoorOpenDfc.Enque();
+		}
+	}
+
+
+/**
+ * High priority DFC triggered by media change interrupt.
+ * 
+ * Media changes events are added/enqued by DMediaChangeBase::DoorOpenService().
+ * 
+ * @param aPtr Pointer to an instantiated class which enqued/added this DFC event
+ */
+void DMediaChangeBase::DoorOpenDfcFn(TAny* aPtr)
+	{
+	DMediaChangeBase* pM=(DMediaChangeBase*)aPtr;
+	pM->MediaChangeEvent(ETrue);
+	}
+
+/**
+ *
+ * Notifies sockets of door close event.
+ *
+ * This function must be called by variant when door close has been detected.
+ */
+EXPORT_C void DMediaChangeBase::DoorClosedService()
+	{
+	MediaChangeEvent(EFalse);
+	}
+
+/**
+ * Notifies relevant peripheral bus sockets of door open or close events.
+ *
+ * @param aDoorOpened  ETrue if door is opened
+ *
+ * @see DPBusSocket::DPBusSocket
+ */
+void DMediaChangeBase::MediaChangeEvent(TBool aDoorOpened)
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">DMediaChangeBase(%d)::MediaChangeEvent(%d)",iMediaChangeNum,aDoorOpened));
+	TInt i;
+
+	// notify all sockets affected
+	for (i=0; i<KMaxPBusSockets; i++)
+		{
+		DPBusSocket* pS=TheSockets[i];
+		if (pS && pS->iMediaChange==this)
+			{
+			// Only increment base reply count if actually adding a DFC
+			if (!pS->iMediaChangeDfc.Queued())
+			    __e32_atomic_add_ord32(&iReplyCount, 1);
+			pS->MediaChangeEvent(aDoorOpened);
+			}
+		}
+	}
+
+/**
+ * To be called by peripheral bus socket derived classes when
+ * door open/close event has been processed.
+ *
+ * @param aDoorOpened   ETrue door opened event processed,
+ *                      EFalse door closed event processed
+ *
+ * @see DPBusSocket::DoorOpenEvent()
+ * @see DPBusSocket::DoorCloseEvent()
+ */
+void DMediaChangeBase::AcknowledgeEvent(TBool aDoorOpened)
+	{
+	TInt c = __e32_atomic_tas_ord32(&iReplyCount, 1, -1, 0);
+	if (c==1)
+		{
+		if (aDoorOpened)
+			DoDoorOpen();
+		else
+			DoDoorClosed();
+		}
+	}
+
+/********************************************
+ * Power supply base class
+ ********************************************/
+void psuTick(TAny* aPtr)
+	{
+	DPBusPsuBase* pP=(DPBusPsuBase*)aPtr;
+	pP->iPsuDfc.Enque();
+	}
+	
+void psuDfc(TAny* aPtr)
+	{
+	DPBusPsuBase* pP=(DPBusPsuBase*)aPtr;
+	pP->DoTickService();
+	}
+
+/**
+Constructor for a DPBusPsuBase object.
+
+@param aPsuNum Unique power supply identification number
+@param aMediaChangedNum Unique media change identification number
+*/
+DPBusPsuBase::DPBusPsuBase(TInt aPsuNum, TInt aMediaChangeNum)
+	: iPsuNum(aPsuNum), iMediaChangeNum(aMediaChangeNum), iVoltCheckMethod(EPsuChkComparator), iState(EPsuOff),
+	iPsuDfc(psuDfc, this, 4),
+	iPwrDownCheckFn(DoPwrDownCheck)
+	{
+//	iCurrLimited=EFalse;
+//	iVoltageSupported=0;
+//	iMaxCurrentInMicroAmps=0;
+//	iVoltCheckInterval=0;
+//	iInactivityCount=0;
+//	iNotLockedCount=0;
+//	iInactivityTimeout=0;
+//	iNotLockedTimeout=0;
+	}
+
+void DPBusPsuBase::DoPwrDownCheck(TAny* aPtr)
+	{
+	DPBusPsuBase& self = *static_cast<DPBusPsuBase*>(aPtr);
+	self.PwrDownCheck();
+	}
+
+/**
+Initialises a DPBusPsuBase object.
+
+Sets object information based on hardware variant PSU inforamtion.
+Calls DoCreate to initialise the PSU.
+
+@return Standard Symbian OS error code.
+
+@see DPBusPsuBase::PsuInfo()
+@see DPBusPsuBase::DoCreate()
+*/
+TInt DPBusPsuBase::Create()
+	{
+
+	TPBusPsuInfo pi;
+	PsuInfo(pi);
+	iVoltageSupported=pi.iVoltageSupported;
+	iMaxCurrentInMicroAmps=pi.iMaxCurrentInMicroAmps;
+	iVoltCheckInterval=pi.iVoltCheckInterval;
+	iVoltCheckMethod=pi.iVoltCheckMethod;
+	iInactivityTimeout=pi.iInactivityTimeOut;
+	iNotLockedTimeout=pi.iNotLockedTimeOut;
+
+	TInt r=DoCreate();
+	if (r!=KErrNone)
+		return r;
+	
+	iPsuDfc.SetDfcQ(&iSocket->iDfcQ);
+	
+	return KErrNone;
+	}
+
+
+/**
+Initialises the power supply unit.
+
+The function is provided by the hardware variant layer, and needs to initialise 
+interrupts and other variant-specific parameters.
+
+The default implementation returns KErrNone.
+
+@return KErrNone
+*/
+EXPORT_C TInt DPBusPsuBase::DoCreate()
+	{
+	return KErrNone;
+	}
+
+
+/**
+Reset (turn off) the power supply unit.
+Sets PSU state to EPsuOff.
+*/
+void DPBusPsuBase::Reset()
+	{
+	SetState(EPsuOff);
+	iCurrLimited=EFalse;
+	}
+
+
+/**
+Checks whether this PSU is powering a bus containing
+a locked device, i.e. one that is recognised and in use by a client.
+
+The function is provided at the media layer, could be used to ensure power is not
+removed whilst media is locked or some other media specific power management activatity.
+
+The default implementation just returns EFalse.
+
+@return EFalse
+*/
+EXPORT_C TBool DPBusPsuBase::IsLocked()
+	{
+	return EFalse;
+	}
+
+/**
+Controls the power supply state.
+
+@param aState A TPBusPsuState enumeration specifying the required state
+			 (EPsuOnFull, EPsuOff, EPsuOnCurLimit)
+
+@return KErrNone if successful, otherwise one of the other system wide error codes.
+
+@see TPBusPsuState
+@see DPBusPsuBase::DoSetState()
+*/
+EXPORT_C TInt DPBusPsuBase::SetState(TPBusPsuState aState)
+	{
+
+	TInt r=KErrGeneral;
+	if (aState==EPsuOff)
+		{
+		iTickLink.Cancel(); // No point in having the 1 second tick running while the PSU is off
+		}
+	else
+		{
+		// Start the 1 second tick to monitor for inactivity, not in use and PSU level checking
+		iTickLink.Cancel();
+		iTickLink.Periodic(KPBusPsuTickInterval,psuTick,this);
+		}
+
+	// Don't turn the PSU back on if it has current limited since the last reset event 
+	iInactivityCount=0;
+	iNotLockedCount=0;
+	if (aState==EPsuOff || !iCurrLimited)
+		{
+		DoSetState(aState);
+		iState=aState;
+		r=KErrNone;
+		}
+	__KTRACE_OPT(KPBUS2,Kern::Printf("<Psu(%d):Set(%d)-%d",iPsuNum,aState,r));
+	return r;
+	}
+
+
+/**
+Check the voltage level of the power supply unit is as expected.
+This method is called every PSU tick.
+
+@param aCheckStatus Power check status in which voltage check can be performed (e.g. KPsuChkOnPwrUp).
+
+@return KErrNone Voltage checking has been performed.
+        KErrNotSupported Voltage checking is not supported by the hardware variant.
+        
+@see KPsuChkOnPwrUp       
+@see KPsuChkWhileOn
+@see DPBusPsuBase::DoTickService()
+@see DPBusPsuBase::DoCheckVoltage()
+*/
+TInt DPBusPsuBase::CheckVoltage(TUint aCheckStatus)
+	{
+	// Check that voltage checking is in order at this time
+	if (
+		(aCheckStatus&iVoltCheckInterval) &&
+		((aCheckStatus&KPsuChkOnPwrUp) || ((aCheckStatus&KPsuChkWhileOn)&&iState==EPsuOnFull))
+	   )
+		{
+		DoCheckVoltage();
+		return KErrNone;
+		}
+	return KErrNotSupported;
+	}
+
+
+/**
+Reports the result of the voltage check.
+
+The function is called by the variant implementation of DoCheckVoltage() 
+to report the result.
+
+Reporting a result of KErrGeneral (to indicate a failure) will result in a 
+call to DPBusSocket::PsuFault(), otherwise report KErrNone to indicate a pass
+and KErrNotReady if the voltage check was not completed.
+
+@param anError System wide error code
+
+@see DPBusPsuBase::DoCheckVoltage()
+@see DPBusSocket::PsuFault()
+*/
+EXPORT_C void DPBusPsuBase::ReceiveVoltageCheckResult(TInt anError)
+	{
+//	__KTRACE_OPT(KPBUS1,Kern::Printf("DPBusPsuBase(%d)::ReceiveVoltageCheckResult(%d)",iPsuNum,anError));
+	if (anError==KErrGeneral)
+		{
+		SetCurrLimited();
+		iSocket->PsuFault(KErrCorrupt);
+		}
+	}
+
+/**
+Get the current power supply unit status
+
+@return PSU status.
+
+@see TPBusPsuStatus
+*/
+TPBusPsuStatus DPBusPsuBase::Status()
+	{
+	if (iCurrLimited)
+		return(EPsuStatError);
+	else
+		return( (iState==EPsuOff) ? EPsuStatOff : EPsuStatOn );
+	}
+
+
+/**
+Checks if power supply unit can be turned off.
+
+@see DPBusPsuBase::DoTickService()
+*/
+void DPBusPsuBase::PwrDownCheck()
+	{
+
+	if (
+		(iNotLockedTimeout&&!IsLocked()&&++iNotLockedCount>iNotLockedTimeout) ||
+		(iInactivityTimeout&&++iInactivityCount>iInactivityTimeout)
+	   )
+			iSocket->PsuTimeout();
+	}
+	
+	
+/**
+Services the Pc Card Tick (called in timer thread).
+*/
+EXPORT_C void DPBusPsuBase::DoTickService()
+	{
+	if (iPwrDownCheckFn)
+		(*iPwrDownCheckFn)(this);	
+	CheckVoltage(KPsuChkWhileOn);	// Check voltage level
+	}
+
+
+/********************************************
+ * Peripheral bus power handler
+ ********************************************/
+DPBusPowerHandler::DPBusPowerHandler(DPBusSocket* aSocket)
+	:	DPowerHandler(*aSocket->iName),
+		iSocket(aSocket)
+	{
+	}
+
+void DPBusPowerHandler::PowerUp()
+	{
+	iSocket->iPowerUpDfc.Enque();
+	}
+
+void DPBusPowerHandler::PowerDown(TPowerState)
+	{
+	iSocket->iPowerDownDfc.Enque();
+	}
+
+/********************************************
+ * Peripheral bus socket base class
+ ********************************************/
+void mediaChangeDfc(TAny* aPtr)
+	{
+	DPBusSocket* pS=(DPBusSocket*)aPtr;
+	if (pS->iDoorOpened)
+		pS->DoorOpenEvent();
+	else
+		pS->DoorCloseEvent();
+	}
+
+void powerUpDfc(TAny* aPtr)
+	{
+	DPBusSocket* pS=(DPBusSocket*)aPtr;
+	pS->DoPowerUp();
+	}
+
+void powerDownDfc(TAny* aPtr)
+	{
+	DPBusSocket* pS=(DPBusSocket*)aPtr;
+	pS->DoPowerDown();
+	}
+
+	/**
+    PBus Socket panics. Faults the system. 
+	This will start the Crash Debugger if it is present, otherwise the system is rebooted by calling Kern::Restart(0)
+	@param aPanic	The panic to be raised
+	@see DPBusSocket::TPanic
+	*/
+EXPORT_C void DPBusSocket::Panic(DPBusSocket::TPanic aPanic)
+	{
+	Kern::Fault("PBUS",aPanic);
+	}
+
+	/**
+    Flags the media driver as entering a critical part of its processing.
+    In this context, critical means that the driver must be allowed to complete its current activity.
+	
+	@return	KErrNone if successful,
+			KErrNotReady if there is any postponed events outstanding.
+	@see DPBusSocket::EndInCritical()
+	*/
+EXPORT_C TInt DPBusSocket::InCritical()
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">DPBusSocket(%d)::InCritical",iSocketNumber));
+	if (iPostponeCount==0 && iPostponedEvents!=0)
+		return KErrNotReady;	// we are about to do media change/power down
+	++iPostponeCount;
+	return KErrNone;
+	}
+
+	/**
+    Flags the media driver as leaving a critical part of its processing.
+	This function enque the media change DFC or power down DFC depending on the event DPBusSocket::iPostponedEvents.
+	@see TPostponedEvent
+	@see DPBusSocket::InCritical()
+	*/
+EXPORT_C void DPBusSocket::EndInCritical()
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">DPBusSocket(%d)::EndInCritical",iSocketNumber));
+	if (iPostponeCount && --iPostponeCount==0)
+		{
+		if (iPostponedEvents & EMediaChange)
+			{
+			iMediaChangeDfc.Enque();
+			__KTRACE_OPT(KPBUS1,Kern::Printf("Media change - done postponed"));
+			}
+		if (iPostponedEvents & EPowerDown)
+			{
+			iPowerDownDfc.Enque();
+			__KTRACE_OPT2(KPBUS1,KPOWER,Kern::Printf("Power down - done postponed"));
+			}
+		}
+	}
+
+	/**
+	Sets the incremental value of current consumption to aCurrent.
+    @param	aCurrent	Delta Current in Milliamps 
+	@see DPowerHandler::DeltaCurrentConsumption()
+	*/
+EXPORT_C void DPBusSocket::DeltaCurrentConsumption(TInt aDelta)
+	{
+	iPowerHandler->DeltaCurrentConsumption(aDelta);
+	}
+
+	/**
+	Constructor for DPBusSocket.
+	Sets the iSocketNumber and initializes the DFC queue for Media Change Dfc, PowerUp Dfc, PowerDown Dfc and PSU Dfc queue.
+    @param	aSocketNumber Pbus socket number
+	*/
+DPBusSocket::DPBusSocket(TInt aSocketNumber)
+	:	iSocketNumber(aSocketNumber),
+		iMediaChangeDfc(mediaChangeDfc, this, 6),
+		iPowerUpDfc(powerUpDfc, this, 4),
+		iPowerDownDfc(powerDownDfc, this, 4),
+		iPsuDfc(psuDfc, this, 4)
+	{
+//	iPowerGroup=0;
+//	iName=NULL;
+//	iState=EPBusCardAbsent;
+//	iPostponeCount=0;
+//	iPostponedEvents=0;
+//	iPowerHandler=NULL;
+	}
+
+	/**
+	Creates a new Socket.
+	This method sets the DFC Queue for the driver associated,
+    Constructs power handler and registers it with the Power Manager.
+	@param  aName	Assigns aName to the PBus socket.
+	@return KErrNone	if successful, otherwise one of the other system wide error codes.
+	@see DPBusPowerHandler
+	@see iMediaChangeDfc
+	@see iPowerUpDfc
+	@see iPowerDownDfc
+	@see iPsuDfc
+	*/
+TInt DPBusSocket::Create(const TDesC* aName)
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">DPBusSocket(%d)::Create %lS",iSocketNumber,aName));
+	iName=aName;
+	DPBusPowerHandler* pH=new DPBusPowerHandler(this);
+	if (!pH)
+		return KErrNoMemory;
+	iPowerHandler=pH;
+	pH->Add();		// register power handler
+	TInt r=Kern::DfcQInit(&iDfcQ, KPBusSocketThreadPriority, iName);
+	if (r!=KErrNone)
+		return r;
+	iMediaChangeDfc.SetDfcQ(&iDfcQ);
+	iPowerUpDfc.SetDfcQ(&iDfcQ);
+	iPowerDownDfc.SetDfcQ(&iDfcQ);
+	
+	return KErrNone;
+	}
+
+	/**
+	Initializes the PBus socket by changing its state to EPBusOff.
+	@return	KErrNone	if successful,
+			otherwise one of the other system wide error codes.
+	*/
+TInt DPBusSocket::Init()
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">DPBusSocket(%d)::Init",iSocketNumber));
+	__PM_ASSERT(iState == EPBusCardAbsent);
+	if (MediaState()==EDoorClosed && CardIsPresent())
+		ChangeState(EPBusOff,KErrNotReady);
+	return KErrNone;
+	}
+
+void DPBusSocket::ResetSocket(TBool aFullReset)
+	{
+	Reset1();
+	iVcc->Reset();
+	if (aFullReset)
+		Reset2();
+	}
+
+void DPBusSocket::ChangeState(TInt aState, TInt anError)
+//
+// Change state, notifying all clients
+//
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("Socket %d ChangeState %d to %d, err %d",iSocketNumber,iState,aState,anError));
+	if (iState!=aState)
+		{
+		if(iState == EPBusCardAbsent && aState == EPBusOff && anError == KErrTimedOut)
+			{
+			// Maintain the internal state to EPBusCardAbsent when PSU
+			// times out to prevent the media from being powered back up.
+			}
+		else
+			{
+			iState=aState;
+			}
+
+		// notify all clients of state change
+		SDblQueLink* pC=iCallBackQ.iA.iNext;
+		while (pC && pC!=&iCallBackQ.iA)
+			{
+			((TPBusCallBack*)pC)->NotifyPBusStateChange(aState,anError);
+			pC=pC->iNext;
+			}
+		}
+	}
+
+void DPBusSocket::Isr(TInt anId)
+//
+// Service a card interrupt
+//
+	{
+	// notify all interested clients of interrupt
+	SDblQueLink* pC=iCallBackQ.iA.iNext;
+#ifdef _DEBUG
+	TInt n=0;
+#endif
+	while (pC!=&iCallBackQ.iA)
+		{
+#ifdef _DEBUG
+		n++;
+#endif
+		((TPBusCallBack*)pC)->Isr(anId);
+		pC=pC->iNext;
+		}
+#ifdef _DEBUG
+	__KTRACE_OPT(KPBUS1,Kern::Printf("!%d",n));
+#endif
+	}
+
+	/**
+	This function adds a callback function to the socket.
+	@param aCallBack is a pointer to PBus callback function for event notification.
+	@see TPBusCallBack
+	*/
+EXPORT_C void DPBusSocket::Add(TPBusCallBack* aCallBack)
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("DPBusSocket(%d)::Add(%08x) next %08x",iSocketNumber,aCallBack,aCallBack->iNext));
+	TInt irq=NKern::DisableAllInterrupts();
+	if (!aCallBack->iNext)
+		iCallBackQ.Add(aCallBack);
+	NKern::RestoreInterrupts(irq);
+	}
+
+	/**
+	Called by clients to power up the socket.
+	@return	KErrNone	if successful, otherwise one of the other system-wide error codes including: 
+				KErrNotReady if card absent or media change has occurred,
+				KErrServerBusy if already powering up,
+				KErrCompletion if already powered up,
+				KErrCorrupt if PSU fault occurs.
+
+    @panic PBUS 1, if PBUS state is invalid.
+	@see TPBusState
+	*/
+EXPORT_C TInt DPBusSocket::PowerUp()
+	{
+	__KTRACE_OPT2(KPBUS1,KPOWER,Kern::Printf(">DPBusSocket(%d)::PowerUp state %d",iSocketNumber,iState));
+	TInt r=KErrNone;
+	switch (iState)
+		{
+		case EPBusCardAbsent:		// card absent or media change has occurred
+			r=KErrNotReady;
+			break;
+		case EPBusOff:
+			break;
+		case EPBusPoweringUp:		// already powering up
+		case EPBusPowerUpPending:
+			r=KErrServerBusy;
+			break;
+		case EPBusOn:				// already powered up
+			r=KErrCompletion;
+			break;
+		case EPBusPsuFault:
+			r=KErrCorrupt;
+			break;
+		default:
+			Panic(EPowerUpInvalidState);
+		}
+	if (r==KErrNone)
+		{
+		if (iStandby)
+			{
+			// machine is powering down, so delay client until machine powers back up
+			// remember to power up when machine powers back up
+			ChangeState(EPBusPowerUpPending,KErrNone);
+			}
+		else
+			{
+			ChangeState(EPBusPoweringUp,KErrNone);
+			InitiatePowerUpSequence();
+			}
+		}
+	__KTRACE_OPT2(KPBUS1,KPOWER,Kern::Printf("<DPBusSocket(%d)::PowerUp ret %d, state %d",iSocketNumber,r,iState));
+	return r;
+	}
+	/**
+	This function is called upon completion of the power up sequence of the device.
+	This is method is called by the derived class methods to terminate the powerup sequence with error codes.
+
+	@param anError	One of the system wide error codes.
+	@see DPBusSocket::InitiatePowerUpSequence()
+	*/
+
+EXPORT_C void DPBusSocket::PowerUpSequenceComplete(TInt anError)
+	{
+	__KTRACE_OPT2(KPBUS1,KPOWER,Kern::Printf("DPBusSocket(%d)::PowerUpSequenceComplete state %d error %d",iSocketNumber,iState,anError));
+	if (iState!=EPBusCardAbsent && iState!=EPBusOff)
+		{
+		if (anError==KErrNone)
+			ChangeState(EPBusOn,KErrNone);
+		else if (anError==KErrBadPower || anError==KErrAbort || anError==KErrTimedOut)
+			ChangeState(EPBusOff,anError);
+		else if (anError == KErrNotReady)
+			ChangeState(EPBusCardAbsent,KErrAbort);
+		else
+			ChangeState(EPBusPsuFault,anError);
+		}
+	}
+
+void DPBusSocket::PsuFault(TInt anError)
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">DPBusSocket(%d)::PsuFault state %d error %d",iSocketNumber,iState,anError));
+	ResetSocket(ETrue);
+	ChangeState(EPBusPsuFault,anError);
+	}
+
+void DPBusSocket::PsuTimeout()
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">DPBusSocket(%d)::PsuTimeout state %d",iSocketNumber,iState));
+	ResetSocket(EFalse);
+	ChangeState(EPBusOff,KErrTimedOut);
+	}
+
+void DPBusSocket::DoPowerUp()
+//
+// Called on transition from standby
+//
+	{
+	__KTRACE_OPT2(KPBUS1,KPOWER,Kern::Printf("DPBusSocket(%d)::DoPowerUp state %d",iSocketNumber,iState));
+	__PM_ASSERT(iStandby);
+	if (iState!=EPBusCardAbsent && iState!=EPBusOff && iState!=EPBusPowerUpPending)
+		Panic(EMcPowerUpInvalidState);
+
+	// when we power up, check whether the door is closed and a card is present
+	// if so we should start in state Off otherwise in state CardAbsent
+
+	TMediaState doorState = MediaState();
+	TBool cardIsPresent = CardIsPresent();
+
+#ifdef __ENABLE_SIMULATED_MEDIA_CHANGE
+	// Override the default media state is we are simulating media change
+	if(iSimulatedMediaState != DPBusSocket::EPeriphBusMediaNormal)
+		{
+		doorState     = (iSimulatedMediaState == DPBusSocket::EPeriphBusDoorOpen) ? EDoorOpen : EDoorClosed;
+		cardIsPresent = (iSimulatedMediaState == DPBusSocket::EPeriphBusMediaPresent);
+		}
+#endif
+
+	if (!(doorState==EDoorClosed && cardIsPresent))
+		ChangeState(EPBusCardAbsent,KErrNotReady);
+	else if (iState==EPBusPowerUpPending)
+		{
+		// if a power-up request is pending, process it now
+		ChangeState(EPBusPoweringUp,KErrNone);
+		InitiatePowerUpSequence();
+		}
+	else
+		ChangeState(EPBusOff,KErrNotReady);
+	iStandby = EFalse;
+	iPowerHandler->PowerUpDone();
+	}
+
+void DPBusSocket::DoPowerDown()
+//
+// Called by DPowerManager on transition to standby
+//
+	{
+	__KTRACE_OPT2(KPBUS1,KPOWER,Kern::Printf("DPBusSocket(%d)::DoPowerDown state %d",iSocketNumber,iState));
+	__PM_ASSERT(!iStandby);
+	if (iPostponeCount)
+		{
+		iPostponedEvents |= EPowerDown;
+		__KTRACE_OPT(KPBUS1,Kern::Printf("Power down postponed"));
+		return;
+		}
+	iPostponedEvents &= ~EPowerDown;
+	switch (iState)
+		{
+		case EPBusPoweringUp:
+		case EPBusOn:
+		case EPBusPsuFault:
+			ChangeState(EPBusOff,KErrNone);
+		case EPBusCardAbsent:
+		case EPBusOff:
+		case EPBusPowerUpPending:
+			break;
+		default:
+			Panic(EEmergencyPowerDownInvalidState);
+		}
+		
+	if(iRequestPowerDownCount == 0)
+		{
+		ResetSocket(EFalse);
+		iStandby = ETrue;
+		iPowerHandler->PowerDownDone();
+		}
+	}
+
+	/**
+	Notifies the socket that we are deferring this power down event. 
+	The function increments the iRequestPowerDownCount reference count
+	@see DPBusSocket::PowerDownComplete()
+	*/
+EXPORT_C void DPBusSocket::RequestAsyncPowerDown()
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("DPBusSocket::RequestAsyncPowerDown"));	
+    __e32_atomic_add_ord32(&iRequestPowerDownCount, 1);
+	__KTRACE_OPT(KPBUS1,Kern::Printf("   >> count=%d", iRequestPowerDownCount));
+	}
+
+	/**
+	This function power down the PBus. Powers down the PBus if iRequestPowerDownCount is equal to 1.
+	@see DPBusSocket::RequestAsyncPowerDown()
+	@see iRequestPowerDownCount
+	*/
+EXPORT_C void DPBusSocket::PowerDownComplete()
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf("DPBusSocket::PowerDownComplete"));
+	if (__e32_atomic_tas_ord32(&iRequestPowerDownCount, 1, -1, 0) == 1)
+		{
+		__KTRACE_OPT(KPBUS1,Kern::Printf("   > Signalling Power Down (deferred)"));
+		DoPowerDown();
+		}
+	__KTRACE_OPT(KPBUS1,Kern::Printf("   >> count=%d", iRequestPowerDownCount));
+	}
+	
+	/**
+	This function is called by the local media device driver to force a remount of the media device.
+	@see DMediaChangeBase::ForceMediaChange()
+	*/
+EXPORT_C void DPBusSocket::ForceMediaChange()
+	{
+	iMediaChange->ForceMediaChange();
+	}
+
+void DPBusSocket::MediaChangeEvent(TBool aDoorOpened)
+//
+// Called in high-priority DFC
+//
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">DPBusSocket(%d)::MediaChangeEvent %d state %d",iSocketNumber,aDoorOpened,iState));
+	iDoorOpened=aDoorOpened;
+	iMediaChangeDfc.Enque();
+	}
+
+void DPBusSocket::DoorOpenEvent()
+//
+// Called in socket thread
+//
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">DPBusSocket(%d)::DoorOpenEvent state %d",iSocketNumber,iState));
+
+	if (iPostponeCount)
+		{
+		iPostponedEvents |= EMediaChange;
+		__KTRACE_OPT(KPBUS1,Kern::Printf("Media change postponed"));
+		return;
+		}
+	iPostponedEvents &= ~EMediaChange;
+
+    // notify all clients of media change
+	ChangeState(EPBusCardAbsent,KErrNotReady);
+	
+	// power down the socket
+	ResetSocket(ETrue);
+
+	// get the media state befor calling AcknowledgeEvent() as the PSL may start a debounce 
+	// timer on this call and return EDoorOpen while the timer is active....
+	TMediaState mediaState = MediaState();
+
+#ifdef __ENABLE_SIMULATED_MEDIA_CHANGE
+	// Only acknowledge the media change to the PSL if we are running in normal mode
+	if(iSimulatedMediaState == EPeriphBusMediaNormal)
+		iMediaChange->AcknowledgeEvent(ETrue);
+#else
+	iMediaChange->AcknowledgeEvent(ETrue);
+#endif
+
+	// If there are multiple doors, then it is assumed that :
+	// - DMediaChangeBase::MediaState() will return EDoorClosed if ANY door is closed, and 
+	// - DPBusSocket::CardIsPresent() will return ETrue if ANY card is present
+	// so that if, for example,  one door is  open and one door closed, then the bus will 
+	// power down and then up again when one of the cards is next accessed.
+	// NB This doesn't worrk for a simulated media change since this doesn't affect the 
+	// PSL's door state
+#ifdef __ENABLE_SIMULATED_MEDIA_CHANGE
+	if ((iSimulatedMediaState == EPeriphBusMediaNormal) &&
+		(mediaState == EDoorClosed && CardIsPresent()))
+#else
+	if (mediaState == EDoorClosed && CardIsPresent())
+#endif
+		{
+		__KTRACE_OPT(KPBUS1,Kern::Printf("At least 1 door still closed"));;
+		ChangeState(EPBusOff,KErrNotReady);
+		}
+
+	}
+
+void DPBusSocket::DoorCloseEvent()
+	{
+	__KTRACE_OPT(KPBUS1,Kern::Printf(">DPBusSocket(%d)::DoorCloseEvent state %d",iSocketNumber,iState));
+
+	// NB If there are multiple doors then the bus may already be powererd up, 
+	// so it's not possible to determine the bus state.
+	//if (iState!=EPBusCardAbsent)
+	//	Panic(EDoorCloseInvalidState);
+
+	// door has been closed - check for a card
+
+	TBool cardIsPresent = CardIsPresent();
+
+#ifdef __ENABLE_SIMULATED_MEDIA_CHANGE
+	// Override the default drive state if we are simulating the media state
+	if((iSimulatedMediaState == EPeriphBusDoorOpen) || (iSimulatedMediaState == EPeriphBusMediaRemoved))
+		cardIsPresent = EFalse;
+#endif
+
+	
+	if (cardIsPresent)
+		{
+		if (iState == EPBusCardAbsent)
+			{
+			// Notifies clients of a media change
+			ChangeState(EPBusOff,KErrNotReady);
+			}
+		else	// if there's already a card present (iState != EPBusCardAbsent), power the bus off and on
+			{
+			// Notify clients of a media change, cancel any outstanding requests, close media driver(s)
+			// and set the DPrimaryMediaBase's state to EClosed to force a subsequent power-up
+			ChangeState(EPBusCardAbsent,KErrNotReady);
+			ChangeState(EPBusOff,KErrNotReady);
+			// NB Don't power down the socket when iState == EPBusCardAbsent as this can take a small amount of time 
+			// and will cause DPBusPrimaryMedia::QuickCheckStatus() to return KErrNotReady in the meantime: this will 
+			// result in any requests to the DPrimaryMediaBase being completed IMMEDIATELY with KErrNotReady, i.e. the
+			// requests won't be queued until the power up completes.
+			ResetSocket(ETrue);
+			}
+		}
+
+#ifdef __ENABLE_SIMULATED_MEDIA_CHANGE
+	// Only acknowledge the media change to the PSL if we are running in normal mode
+	if(iSimulatedMediaState == EPeriphBusMediaNormal)
+		iMediaChange->AcknowledgeEvent(EFalse);
+#else
+	iMediaChange->AcknowledgeEvent(EFalse);
+#endif
+	}
+	/**
+	Gets pointer to the PBus Socket corresponding to the opened logical unit.
+	@param anId	logical id of the PBus Socket.	
+	@return Pointer to the PBusSocket for valid anId, else NULL.
+	*/
+EXPORT_C DPBusSocket* DPBusSocket::SocketFromId(TInt anId)
+	{
+	if (anId>=0 && anId<KMaxPBusSockets)
+		return TheSockets[anId];
+	return NULL;
+	}
+
+	/**
+	Default implementation for handling debug functionality.
+	This function can only be used if __ENABLE_SIMULATED_MEDIA_CHANGE is defined. 
+	Otherwise, this method is not implemented and it always returns KErrNotSupported.
+
+	@param aFunction	refer to TPBusDebugFunction
+	@param aParam1	Simulated media state.
+	@param aParam2 Not used in this method.
+	@return  KErrNone - if successful, otherwise one of the other system-wide error 
+	codes including: 
+			KErrNotSupported - if aFunction is invalid or __ENABLE_SIMULATED_MEDIA_CHANGE 
+	is not defined,
+			KErrArgument -  if aParam1 does not corresponds to TPBusSimulateMediaState.
+	@see TPBusSimulateMediaState
+	@see TPBusDebugFunction
+	*/
+EXPORT_C TInt DPBusSocket::ControlIO(TInt aFunction, TAny* aParam1, TAny* /*aParam2*/)
+	{
+	TInt err = KErrNone;
+
+	switch(aFunction)
+		{
+		case EControlMediaState:
+			//
+			// EOverrideMediaState - Set the media state manually for simulation purposes.
+			//	- aParam1 : Simulated Media State (TPBusSimulateMediaState)
+			//
+			{
+#ifdef __ENABLE_SIMULATED_MEDIA_CHANGE
+			TUint16 newState = (TUint16)(TInt)aParam1;
+			if(newState != iSimulatedMediaState)
+				{
+				iSimulatedMediaState = newState;
+
+				switch(iSimulatedMediaState)
+					{
+					case EPeriphBusMediaNormal:
+						//
+						// Normal state
+						// - Signal that the door is open and generate a media change.
+						//
+						iMediaChange->MediaChangeEvent(ETrue);
+						break;
+					
+					case EPeriphBusDoorOpen:
+						//
+						// Simulated door open or back to normal state.
+						// - Signal that the door is open and generate a media change.
+						//
+						MediaChangeEvent(ETrue);
+						break;
+					
+					case EPeriphBusMediaRemoved:
+					case EPeriphBusMediaPresent:
+						//
+						// Simulated door close with media present or absent
+						// - Signal that the door is closed.
+						//
+						MediaChangeEvent(EFalse);
+						break;
+
+					case EPeriphBusMediaDoubleDoorOpen:
+						// simulate 2 door open interrupts 
+						iSimulatedMediaState = EPeriphBusMediaNormal;
+						iMediaChange->MediaChangeEvent(ETrue);
+						iMediaChange->MediaChangeEvent(ETrue);
+						break;
+
+					default:
+						//
+						// Unsupported media state
+						//
+						err = KErrArgument;
+						break;
+					}
+				}
+#else
+			aParam1 = aParam1;
+			err = KErrNotSupported;
+#endif
+			break;
+			}
+
+		default:
+			err = KErrNotSupported;
+			break;
+		}
+
+	return err;
+	}
+
+/********************************************
+ * Extension entry point
+ ********************************************/
+
+GLDEF_C TInt KernelModuleEntry(TInt aReason)
+	{
+	if (aReason==KModuleEntryReasonExtensionInit0 || aReason==KModuleEntryReasonExtensionInit1)
+		return KErrNone;
+	return KErrArgument;
+	}
+
+
+
+
+