Here my contribute back to forum with my solution for activating the loudspeaker without to require the APS, VoIP Audio services or MultimediaDD. Solution for MR + FP1 and semi-solution for FP2. All APIs are in the plugins.

For MR:
-----------------------------------------
// LoudspeakerControlMR.h file

#ifndef LOUDSPEAKERCONTROLMR_H
#define LOUDSPEAKERCONTROLMR_H

#include <e32base.h>
#include <e32std.h>

#include <DosSvrServices.h>
#include "MLoudspeakerControl.h"

/**
* CLoudspeakerControlMR
* Loudspeaker control for S60 MR/3.0
* Use via MLoudspeakerControl interface.
* Implement it in a single .dll with only one exported function
* which creates and returns an object of type: MLoudspeakerControl
* Uses DOS services from SharedData S60 plug-in
*/

/*
* Capabilities required: ReadUserData WriteUserData ReadDeviceData WriteDeviceData
*/

// function exported at ordinal 1 in dll
IMPORT_C MLoudspeakerControl * CreateLoudspeakerControlMR_L();

// CLoudspeakerControlMR does not export any method
class CLoudspeakerControlMR : public MLoudspeakerControl
{
public:

static CLoudspeakerControlMR* NewL();
static CLoudspeakerControlMR* NewLC();
virtual ~CLoudspeakerControlMR();

private:

CLoudspeakerControlMR();
void ConstructL();

public:

// from MLoudspeakerControl
TInt ActivateLoudspeaker(TBool aActivate);
TInt IsLoudspeakerActive(TBool& aIsActive);

protected:

RDosServer iDosServer; // dos server - domestic os
RDosAudio iDosAudio; // dos audio services

};

#endif

// .cpp file

#include "LoudspeakerControlMR.h"

// export method at ordinal 1 in dll
EXPORT_C MLoudspeakerControl * CreateLoudspeakerControlMR_L()
{
CLoudspeakerControlMR * aLspkCMR = CLoudspeakerControlMR::NewL();
return aLspkCMR;
}

CLoudspeakerControlMR::CLoudspeakerControlMR()
{
}

CLoudspeakerControlMR::~CLoudspeakerControlMR()
{
iDosAudio.Close();
iDosServer.Close();
}

CLoudspeakerControlMR* CLoudspeakerControlMR::NewLC()
{
CLoudspeakerControlMR* self = new (ELeave)CLoudspeakerControlMR();
CleanupStack::PushL(self);
self->ConstructL();
return self;
}

CLoudspeakerControlMR* CLoudspeakerControlMR::NewL()
{
CLoudspeakerControlMR* self=CLoudspeakerControlMR::NewLC();
CleanupStack::Pop(); // self;
return self;
}

void CLoudspeakerControlMR::ConstructL()
{
User::LeaveIfError(iDosServer.Connect());
User::LeaveIfError(iDosAudio.Open(iDosServer));
}

TInt CLoudspeakerControlMR::ActivateLoudspeaker(TBool aActivate)
{
TInt aRet = KErrGeneral;

// default Off/deactivated
EPSHandsFreeMode aLoudSpkMode = EPSIhfOff;

// switch on/active if required
if(aActivate)
aLoudSpkMode = EPSIhfOn;

// set the loudspeaker mode
aRet = iDosAudio.SetHandsfreeMode(aLoudSpkMode);

/* seems not a good solution for telephony - on MR the phone updates the menu options
but not the internal state of the audio loudspeaker.
// if we succeeded, update the according Publish&Subscribe key
// see: SharedData/include
// see: TelephonyInternalPSKeys.h
// this prop must be update so that the Telephony app is updated too
// and will show Activate Loudspk. or Activate Handset correctly
if(aRet == KErrNone)
{
TInt aKeyVal = EPSTelephonyIhfOff;
if(aActivate)
aKeyVal = EPSTelephonyIhfOn;
TInt aProSetRes = RProperty::Set(KPSUidTelephonyAudio,(TUint)KTelephonyIhfMode,aKeyVal);
}
*/

return aRet;
}

TInt CLoudspeakerControlMR::IsLoudspeakerActive(TBool& aIsActive)
{
EPSHandsFreeMode aLoudSpkMode = EPSIhfOff;
TInt aRet = iDosAudio.GetHandsfreeMode(aLoudSpkMode);

// check if we got correct value for the speaker mode
if(aRet == KErrNone)
{
// got the loudspeaker mode
// update the argument
aIsActive = (aLoudSpkMode == EPSIhfOn);
}

return aRet;
}


// for FP1
---------
/*
* Capabilities required: ReadUserData WriteUserData ReadDeviceData WriteDeviceData
*/
// .h file

#ifndef LOUDSPEAKERCONTROLFP1_H
#define LOUDSPEAKERCONTROLFP1_H

#include <e32base.h>
#include <e32std.h>
#include <e32property.h>

#include <TelephonyInternalPSKeys.h>
#include "MLoudspeakerControl.h"

/**
* CLoudspeakerControlFP1
* Loudspeaker control for FP1
* see: SharedData/include
* see: TelephonyInternalPSKeys.h
*/

class CLoudspeakerControlFP1 : public MLoudspeakerControl
{

public:

static CLoudspeakerControlFP1* NewL();
static CLoudspeakerControlFP1* NewLC();
virtual ~CLoudspeakerControlFP1();

private:

CLoudspeakerControlFP1();
void ConstructL();

public:

// from MLoudspeakerControl
TInt ActivateLoudspeaker(TBool aActivate);
TInt IsLoudspeakerActive(TBool& aIsActive);

protected:

TUid iCat;
TUint iKey;
};

#endif

// .cpp file

#include "LoudspeakerControlFP1.h"

CLoudspeakerControlFP1::CLoudspeakerControlFP1()
{
// see: SharedData/include
// see: TelephonyInternalPSKeys.h
iCat = KTelephonyAudioOutput;
iKey = (TUint)KTelephonyAudioOutputPreference;
}

CLoudspeakerControlFP1::~CLoudspeakerControlFP1()
{
}

CLoudspeakerControlFP1* CLoudspeakerControlFP1::NewLC()
{
CLoudspeakerControlFP1* self = new (ELeave)CLoudspeakerControlFP1();
CleanupStack::PushL(self);
self->ConstructL();
return self;
}

CLoudspeakerControlFP1* CLoudspeakerControlFP1::NewL()
{
CLoudspeakerControlFP1* self=CLoudspeakerControlFP1::NewLC();
CleanupStack::Pop(); // self;
return self;
}

void CLoudspeakerControlFP1::ConstructL()
{
}

TInt CLoudspeakerControlFP1::ActivateLoudspeaker(TBool aActivate)
{
// if the property is set/unset, the Loudspeaker is turned on/off
// and also the built in Telephone app will be notified so that
// it will show correct options; this valid for S60 FP1/3.1
/*EPSTelephonyAudioOutput*/TInt aValue = EPSPrivate;
if(aActivate)
aValue = EPSPublic;
return RProperty::Set(iCat,iKey,aValue);
}

TInt CLoudspeakerControlFP1::IsLoudspeakerActive(TBool& aIsActive)
{
/*EPSTelephonyAudioOutput*/TInt aValue = EPSPrivate;
TInt aRet = RProperty::Get(iCat,iKey,aValue);
if(aRet == KErrNone)
{
// got the loudspeaker mode
// update the argument
aIsActive = (aValue == EPSPublic);
}

return aRet;
}

// interface MLoudspeakerControl.h

#ifndef MLOUDSPEAKERCONTROL_H
#define MLOUDSPEAKERCONTROL_H

class MLoudspeakerControl : public CBase
{

public:

MLoudspeakerControl() {};
virtual ~MLoudspeakerControl() {};

public:

// sets Loudspeaker as active or inactive;
// returns KErrNone if the operation/method was succesfully performed
virtual TInt ActivateLoudspeaker(TBool aActivate) = 0;

// gets the loudspeaker state active/inactive in the argument aIsActive
// returns KErrNone if the operation/method was succesfully performed
virtual TInt IsLoudspeakerActive(TBool& aIsActive) = 0;

};

#endif

For FP2 is a must to use the VoIP Audio Services plugin which requires MultimediaDD or, you can simulate right soft key press while the call is connected if you are comfortable with this.

Best
tek