// Copyright (c) 1999-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "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:
// L2CAP Host resolver.
// Implements Inquiry and Name lookup
//
//
#ifndef L2HOSTRESOLVER_H
#define L2HOSTRESOLVER_H
#include <bt_sock.h>
#include <es_prot.h>
#include <bluetooth/hcicommandqueue.h>
#include <bluetooth/hcicommandqueueclient.h>
// BT1.1: Could make these runtime configurable.
// HCI Inquiry length time, in 1.28 seconds.
static const TInt KInquiryLength = 10;
// Upper limit to number of results we can handle
static const TInt KInquiryMaxResults = 00;
// Seconds between flushes
static const TInt KFlushTimeoutSecs = 5;
// Number of flushes required to actually clear a record
static const TInt KRecordDeathAge = 120; // 10 mins
// Maximum flushes before the a device disappears to inquires
static const TInt KRecordStaleAge = 10; // 50 secs
// Maximum flushes before the cache is set to stale (needing refresh)
static const TInt KCacheStaleAge = -1; // -1 == always needs refresh
// While doing inquiry, set of watchdog every second
static const TInt KInquiryWatchdogPeriod = 1;
// Immidiately fetch the first 3 names
static const TInt KImmediateNameFetch = 3;
// Page Timeout for name request, we'll initially use this for each one
static const TUint16 KMinNamePageTimeout = 0x1200; // 0x1200 = 3 secs
// Amount to increase pagetimeout, everytime a lookup fails
static const TUint16 KNamePageTimeoutIncrement = 0x140; // 0x140 = .2 sec
/** Max Page Timeout for name request, we'll consider using (ACL connects
are blocked while doing name lookup, so mustn't be too high!) **/
static const TUint16 KMaxNamePageTimeout = 0x2500; // 0x2500 = 6 secs
// Max times we'll try to get a single name while an inquiry pending
static const TInt KMaxNameLookupAttemptsDuringInquiry = 1;
// Max number of times to try to get a name
static const TInt KMaxNameLookupAttempts = 4;
class CBTInquiryMgr;
class CBTInqResultRef;
class CLinkMgrProtocol;
enum TBTInqResultValid
{
EBluetoothAddr = 0x001, //000000000001
EBluetoothCoD = 0x008, //000000001000
EBluetoothPageScanRepMode = 0x080, //000010000000
EBluetoothPageScanMode = 0x100, //000100000000
EBluetoothClockOffSet = 0x200, //001000000000
EBluetoothRssi = 0x400, //010000000000
EBluetoothEir = 0x800, //100000000000
EBluetoothJuice = EBluetoothCoD |
EBluetoothPageScanRepMode |
EBluetoothPageScanMode |
EBluetoothClockOffSet
};
/**
BT Inquiry Result record.
Ref counted object, deletes itself when no more CBTInqResultRef objects
point to it. Aggregates the TInquiryLogEntry type.
**/
NONSHARABLE_CLASS(CBTInqResultRecord) : public CBase
{
friend class CBTInquiryMgr;
friend class CBTInqResultRef;
public:
TInquiryLogEntry& LogEntry();
void SetName(const TDesC8& aName);
const TDesC8& Name() const;
TBool IsNameRequestPending() const;
void SetNamePending(TBool aBool);
TBool IsNameRefreshRequested() const;
void SetNameRefreshRequested(TBool aBool);
TBool IsNameComplete() const;
void SetNameComplete(TBool aBool);
TBool IsExplicitNameRequest() const;
void SetExplicitNameRequest(TBool aBool);
TBool IsNameValid() const;
void SetNameValid(TBool aBool);
void SetNameLookupResultCode(TInt aResultCode);
TBool HaveNameLookupResult() const;
TInt NameLookupResultCode() const;
TInt NameLookupAttempts() const;
TInt IncFlushes();
void GetInquirySockAddr(TInquirySockAddr& aAddr);
TInt GetEir(TNameRecord& aNameRec, TBool aIgnoreCachedName);
TInt GetName(TNameRecord& aNameRec);
TInt AddIAC(TUint aIAC);
TBool HasRespondedToIAC(TUint aIAC);
TInt NumberOfIACsRespondedTo();
void ClearIACs();
inline TBool IsJuiceFromHCI() { return (EBluetoothJuice == (iJuiceFromHCIMask & EBluetoothJuice)); };//for the moment demand all
inline TBool IsPageScanModeFromHCI() { return iJuiceFromHCIMask & EBluetoothPageScanMode; };
inline TBool IsPageScanRepModeFromHCI() { return iJuiceFromHCIMask & EBluetoothPageScanRepMode; };
inline TBool IsCoDFromHCI() { return iJuiceFromHCIMask & EBluetoothCoD; };
inline TBool IsClockOffsetFromHCI() { return iJuiceFromHCIMask & EBluetoothClockOffSet; }
inline TBool IsEirPresent() { return iJuiceFromHCIMask & EBluetoothEir; }
inline TExtendedInquiryResponseDataCodec& Codec() { return iCodec; }
private:
enum TNameStatus
{
ENamePending = 1,
ENameRefreshRequested = 2,
ENameComplete = 4,
ENameExplicitRequest = 8, // An explicit name lookup has been requested, so don't put this to the back of the queue
};
~CBTInqResultRecord();
// Interface for CBTInquiryMgr
CBTInqResultRecord(const TBTDevAddr& aAddr);
// Interface for CBTInqResultRef
void Open();
void Close();
// Number of CBTInqResultRef objects pointing to this result
TInt iRefCount;
// The actual inquiry log entry. It contains EIR data, RSSI value and old style TInquiryLogEntry.
TInquiryLogEntryWithEir iEntry;
// The device name's state
TInt iNameStatus; //Bit flag
// The result of name lookup, of >0 if one hasn't been done
TInt iNameLookupResultCode;
// The device's name (if known), it can be from EIR or a Remote Name Request
TBuf8<KHCIRemoteDeviceNameMaxLength> iName;
// Number of attempts made at getting name.
TInt iNameLookupAttempts;
// Flush cycles since last seen (lower == newer).
TInt iFlushes;
// IACs this device has responded to
RArray<TUint> iIACs;
// Has inquiry log entry been populated by inquiry?
TUint16 iJuiceFromHCIMask;
// Has this entry been found during the current, ongoing inquiry?
TBool iFoundDuringCurrentInquiry;
TExtendedInquiryResponseDataCodec iCodec;
};
/**
Inquiry result reference.
References a CBTInqResultRecord.
**/
NONSHARABLE_CLASS(CBTInqResultRef) : public CBase
{
public:
CBTInqResultRef(CBTInqResultRef& aRef);
CBTInqResultRef(CBTInqResultRecord& aRec);
~CBTInqResultRef();
CBTInqResultRecord& Result() const;
private:
// The pointer to actual CBTInqResultRecord
CBTInqResultRecord& iRecord;
public:
// List to go on a view, in CBTInqResultSet
TDblQueLink iLink;
};
/**
BT Inquiry Result Set.
A view onto a set of the current inquiry results.
**/
NONSHARABLE_CLASS(CBTInqResultSet) : public CBase
{
public:
CBTInqResultSet();
~CBTInqResultSet();
void Reset();
CBTInqResultRef* Add(CBTInqResultRecord& aRec);
CBTInqResultRef* FindEntry(const TBTDevAddr& aAddr);
CBTInqResultRef* NextResult();
CBTInqResultRef* CurrentResult();
void ReturnToFirstResult();
TBool IsEmpty();
void MoveToback(CBTInqResultRef& aRef);
private:
typedef TDblQue<CBTInqResultRef> TResultRefQue;
typedef TDblQueIter<CBTInqResultRef> TResultQueIter;
TResultRefQue iResultRefs;
TResultQueIter iNextRefIter;
CBTInqResultRef* iCurrentResult;
};
NONSHARABLE_STRUCT(TInquiryCacheAge)
{
public:
TUint iIAC;
TInt iCacheAge;
};
/**
BT Host resolver.
Represents one client host resolver session.
Only implements GetByAddress, as this is all the
BT API supports.
**/
NONSHARABLE_CLASS(CBTHostResolver) : public CHostResolvProvdBase
{
public:
enum TInquiryStatus
{
EInquiryReady,
EInquiring,
EInquiryComplete
};
enum TNameLookupMode
{
EDontGetNames,
EDoGetNames
};
enum TRequestState
{
EIdle,
EInquiry,
ENameLookup,
ESetLocalName,
EGetLocalName,
EError // LC added
};
CBTHostResolver(CBTInquiryMgr& aInquiryMgr);
~CBTHostResolver();
// From CHostResolvProvdBase
void GetByName(TNameRecord& aName);
void GetByAddress(TNameRecord& aName);
void SetHostName(TDes& aNameBuf);
void GetHostName(TDes& aNameBuf);
// Need this to allow instantiation. Doesn't seem to be callable though!
// Simply stub it, as we don't actually care...
virtual TInt SetOption(TUint aLevel, TUint aName, const TDesC8& aOption);
// From CResolverProvdBase
void CancelCurrentOperation();
// IquiryMgr interface
void InquiryResult(CBTInqResultRecord& aResult);
void NameLookupResult(TInt aErr, const TBTDevAddr& aAddr, const TDesC8& aName);
void InquiryComplete(TInt aErr);
void SetLocalNameComplete(TInt aErr);
void GetLocalNameComplete(TInt aErr, const TDesC8& aName);
TUint GetIAC() const;
inline static TInt LinkOffset() {return _FOFF(CBTHostResolver, iLink);}
//From CResolverProvdBase
TInt SecurityCheck(MProvdSecurityChecker *aSecurityChecker);
private:
void TryToCompleteRequest();
void CompleteRequest(TInt aErr);
void CompleteCurrentOperation();
CBTInquiryMgr& iInquiryMgr;
// The record we're currently retreiving
TNameRecord* iNameRecord;
// the buffer we get host name into
TDes* iHostNameBuf;
// Current user request.
TRequestState iRequestState;
// Status of the on-going inquiry process
TInquiryStatus iInquiryStatus;
// Get names or not?
TNameLookupMode iNameLookupMode;
// Error code... Only valid when iInquiryStatus == EInquiryComplete
TInt iInqCompletionCode;
// Set of devices found, to be returned to client
CBTInqResultSet iResults;
// Member of Q in InquiryMgr.
TDblQueLink iLink;
//Mixin providing security checking, This is not an owned variable.
MProvdSecurityChecker* iSecurityChecker;
};
#ifdef CONNECTION_PREEMPTS_INQUIRY
class CConnectingStatusSubscriber;
#endif
/**
BT Inquiry Manager.
Singleton object that manages host resolvers, inquiries, and
name lookups.
**/
NONSHARABLE_CLASS(CBTInquiryMgr) : public CBase, public MHCICommandQueueClient
{
public:
// Interface for CL2CAPProtocol
~CBTInquiryMgr();
static CBTInquiryMgr* NewL(CLinkMgrProtocol& aProtocol);
CBTHostResolver* NewHostResolverL();
void SetHCICommandQueue(MHCICommandQueue& aCommandQueue);
void ClearHCICommandQueue();
void ClockOffsetResult(const TBTDevAddr& aAddr, TBasebandTime aClockOffset);
void CoDResult(const TBTDevAddr& aConn, TUint aCoD);
void SetLocalNameComplete(TInt aErr);
void GetLocalNameComplete(TInt aErr, const TDesC8& aName);
void Suspend();
void Resume();
void SetInquiryMode();
#ifdef _DEBUG
void IncrementHRCount() {++iNumHRs;};
void DecrementHRCount() {--iNumHRs;};
#endif
// Interface for CBTHostResolver
void StartInquiry(CBTHostResolver& aResolver, TUint aIAC, TBool aIgnoreCache);
void LookupName(CBTHostResolver& aResolver, const TBTDevAddr& aAddr, TBool aIgnoreCache, TBool aExplicitNameRequest);
void ClearCache();
TInt SetLocalName(const TDesC8& aName);
TInt GetLocalName();
CBTInqResultRef* FindExistingCacheEntry(const TBTDevAddr& aAddr);
CBTInqResultRef* AddEntryToCache(const TBTDevAddr& aAddr);
CBTInqResultRef* AddEntryWithJuiceToCache(const TInquiryLogEntry& aEntry);
CBTInqResultRef* AddEntryWithCoDToCache(const TBTDevAddr& aAddr, const TUint aCoD);
CBTInqResultRef* AddEntryWithClockOffsetToCache(const TBTDevAddr& aAddr, const TBasebandTime aClockOffset);
const TDesC8* DeviceNameFromCache(const TBTDevAddr& aAddr);
CBTInqResultRecord* BasebandParametersFromCache(const TBTDevAddr& aAddr);
void DeletingHostResolver();
enum THWState
{
EIdle = 0,
EInquiry,
ENameLookup,
EConnecting, // can't do anything really!
// EConnected // can't do anything ?
EOff,
ECancellingForNewIAC,
};
THWState HWState(){ return iHWState; }
void SetHWState(THWState aState);
void PublishStatus();
void CompleteCommands(TInt aErr);
TBool IsExtendedInquiryResponseSupported();
// commands sent to the controller -- called from external classes
void ReadRemoteNameL(const TBTDevAddr& aAddr);
private: // Handling logical inquiry events
void WriteInquiryModeComplete(TBool aSucceeded);
void InquiryResult(TInt aErr,const TInquiryLogEntry& aEntry);
void InquiryComplete(TInt aErr, TUint8 aNumResponses);
void RemoteNameResult(TInt aErr, const TBTDevAddr& aAddr, const TBTDeviceName8& aBuf);
void RemoteHostSupportedFeatures(TInt aErr, const TBTDevAddr& aAddr, const TUint64& aHostSupportedFeatures);
private: // from MHCICommandQueueClient
virtual void MhcqcCommandEventReceived(const THCIEventBase& aEvent, const CHCICommandBase* aRelatedCommand);
virtual void MhcqcCommandErrored(TInt aErrorCode, const CHCICommandBase* aCommand);
private: // HCI event handling functions
// basic types
void CommandCompleteEvent(const THCIEventBase& aEvent);
void CommandStatusEvent(const THCIEventBase& aEvent, const CHCICommandBase& aCommand);
// first class events
void InquiryResultEvent(const THCIEventBase& aEvent);
void InquiryResultWithRSSIEvent(const THCIEventBase& aEvent);
void ExtendedInquiryResultEvent(const THCIEventBase& aEvent);
void InquiryCompleteEvent(const THCIEventBase& aEvent);
void RemoteNameReqCompleteEvent(const THCIEventBase& aEvent);
void RemoteHostSupportedFeaturesNotificationEvent(const THCIEventBase& aEvent);
// command complete events
void WriteInquiryModeOpcode(THCIErrorCode aHciErr, const THCIEventBase& aEvent);
void ReadLocalNameOpcode(THCIErrorCode aHciErr, const THCIEventBase& aEvent);
void InquiryCancelOpcode(THCIErrorCode aHciErr, const THCIEventBase& aEvent);
private:
CBTInquiryMgr(CLinkMgrProtocol& aProtocol);
void ConstructL();
MHCICommandQueue& CommandQueue() const;
// commands sent to the controller
void CancelInquiryL();
void CancelRemoteNameL(const TBTDevAddr& aAddr);
void StartInquiryL(TUint aIAC, TUint8 aLength, TUint8 aNumResponses);
void WriteInquiryModeL(TUint8 aInquiryMode);
void LookupNameL(const TInquiryLogEntry& aEntry);
void ReadLocalNameL();
void DoInquiry();
void DoNameLookup(TBool aInquiryComplete);
void HandleRemoteNameResult(TInt aErr, CBTInqResultRef& aRef, const TBTDeviceName8& aBuf);
void TryToInterruptInquiryForNameLookup();
void UpdateNotifiers(const TBTDevAddr& aAddr, const TDesC8& aName);
void ClearCurrentInquiryResults();
TInt CancelHardwareInquiry();
TInt StartHardwareInquiry();
static TInt InquiryWatchdog(TAny* aPtr);
static TInt Flush(TAny* aPtr);
void DoFlush();
void EnableFlusher();
TInt CacheAge(TUint aIAC) const;
void SetCacheAge(TUint aIAC, TInt aAge);
private:
MHCICommandQueue* iCommandQueue;
// Singleton owner of us
CLinkMgrProtocol& iLinkMgrProtocol;
// What the hardware is currently up to (as far as we know)
THWState iHWState;
// IAC of the current inquiry
TUint iCurrentInquiryIAC;
// IAC of the inquiry that is currently requested by a host resolver, either in progress or about to start
TUint iRequestedInquiryIAC;
// IAC of the inquiry that has been put on hold while a higher priority IAC is in progress
TUint iQueuedInquiryIAC;
// Number of times we've interrupted the current inquiry
TInt iInquiryInteruptions;
// Time since the last inquiry, per IAC
RArray<TInquiryCacheAge> iCacheAge;
// Timer to age records and the cache itself
CPeriodic* iFlusher;
// Results since watchdog last went off
TInt iResultCount;
// Number of watchdogs seeing 0 devices
TInt iInquirySilenceCount;
// Number of name requests pending
TInt iPendingNameRequests;
// True is there are new page requests pending
TInt iNewPageRequestsPending;
// Current name request pagetimeout
TUint16 iNamePageTimeout;
// Current Inquiry Mode
TUint8 iInquiryMode;
// Pending Inquiry Mode
TUint8 iPendingInquiryMode;
CBTInqResultSet iCurrentResults;
TDblQue<CBTHostResolver> iHRs;
TBool iReportedInquiryState; // what did we last tell the Inquiry state P&S key
#ifdef CONNECTION_PREEMPTS_INQUIRY
// Monitors if a ACL connection is currently being established.
CConnectingStatusSubscriber* iConnectingStatus;
#endif
#ifdef _DEBUG
TInt iNumHRs; // no Count() in que's - handy to have
#endif
};
#ifdef CONNECTION_PREEMPTS_INQUIRY
// *******************************************************************
// ACL Connecting status subscriber
// *******************************************************************
NONSHARABLE_CLASS(CConnectingStatusSubscriber) : public CActive
{
public:
static CConnectingStatusSubscriber* NewL(CBTInquiryMgr& aInquiryMgr);
~CConnectingStatusSubscriber();
private:
CConnectingStatusSubscriber(CBTInquiryMgr& aInquiryMgr);
void ConstructL();
void Subscribe();
void RunL();
void DoCancel();
CBTInquiryMgr& iParent;
RProperty iProperty;
};
#endif // CONNECTION_PREEMPTS_INQUIRY
#endif