/*
* Copyright (c) 2010 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:
*
*/
/**
* @file
* @internalComponent
*/
#include "ncmclientmanager.h"
#ifdef OVERDUMMY_NCMCC
#include <usb/testncmcc/dummy_ncminternalsrv.h>
#else
#include "ncminternalsrv.h"
#endif // OVERDUMMY_NCMCC
#include "ncmclassdescriptor.h"
// For OST tracing
#include "OstTraceDefinitions.h"
#ifdef OST_TRACE_COMPILER_IN_USE
#include "ncmclientmanagerTraces.h"
#endif
// NCM Interface MTU size
extern const TUint KEthernetFrameSize;
// NCM MAC Address String descriptor buffer length
const TUint KMacAddrStringDescSize = 32;
/**
* Construtor
* @param aMacAddress the NCM ethernet interface MAC address
*/
CNcmClientManager::CNcmClientManager(const TNcmMacAddress& aHostMacAddress):
iHostMacAddress(aHostMacAddress)
{
OstTraceFunctionEntryExt( CNCMCLIENTMANAGER_CNCMCLIENTMANAGER_ENTRY, this );
// No implementation required
OstTraceFunctionExit1( CNCMCLIENTMANAGER_CNCMCLIENTMANAGER_EXIT, this );
}
/**
* Destructor
*/
CNcmClientManager::~CNcmClientManager()
{
OstTraceFunctionEntry0( CNCMCLIENTMANAGER_CNCMCLIENTMANAGER_ENTRY_DESTRUCTOR );
TInt err = KErrNone;
if (iCommLddInitiated)
{
RChunk* commChunk = NULL;
OstTrace0(TRACE_NORMAL, CNCMCLIENTMANAGER_CNCMCLIENTMANAGER_PRE_CLOSE_COMM_INTERFACE, "About to get chunk handle for communication interface!");
err = iCommLdd.GetDataTransferChunk(commChunk);
if (KErrNone == err)
{
OstTrace0(TRACE_NORMAL, CNCMCLIENTMANAGER_CNCMCLIENTMANAGER_CLOSE_COMM_CHUNK, "About to close chunk handle for communication interface!");
commChunk->Close();
}
else
{
OstTrace1(TRACE_ERROR, CNCMCLIENTMANAGER_CNCMCLIENTMANAGER_CLOSE_COMM_CHUNK_FAIL, "Can not get chunk handle for communication interface: err = %d", err);
}
OstTrace0(TRACE_NORMAL, CNCMCLIENTMANAGER_CNCMCLIENTMANAGER_CLOSE_COMM_INTERFACE, "About to close device handle for communication interface!");
iCommLdd.Close();
}
if (iDataLddInitiated)
{
RChunk* dataChunk = NULL;
OstTrace0(TRACE_NORMAL, CNCMCLIENTMANAGER_CNCMCLIENTMANAGER_PRE_CLOSE_DATA_INTERFACE, "About to get chunk handle for data interface!");
err = iDataLdd.GetDataTransferChunk(dataChunk);
if (KErrNone == err)
{
OstTrace0(TRACE_NORMAL, CNCMCLIENTMANAGER_CNCMCLIENTMANAGER_CLOSE_DATA_CHUNK, "About to close chunk handle for communication interface!");
dataChunk->Close();
}
else
{
OstTrace1(TRACE_ERROR, CNCMCLIENTMANAGER_CNCMCLIENTMANAGER_CLOSE_DATA_CHUNK_FAIL, "Can not get chunk handle for data interface: err = %d", err);
}
OstTrace0(TRACE_NORMAL, CNCMCLIENTMANAGER_CNCMCLIENTMANAGER_CLOSE_DATA_INTERFACE, "About to close device handle for data interface!");
iDataLdd.Close();
}
OstTraceFunctionExit0( CNCMCLIENTMANAGER_CNCMCLIENTMANAGER_ENTRY_DESTRUCTOR_EXIT );
}
/**
* Setup NCM interfaces
* @param[out] aDataEpBufferSize, NCM data interface EP buffer size
*/
void CNcmClientManager::SetNcmInterfacesL(TUint& aDataEpBufferSize)
{
OstTraceFunctionEntryExt( CNCMCLIENTMANAGER_SETNCMINTERFACESL_ENTRY, this );
// Setup NCM communication interface
SetCommunicationInterfaceL();
// Setup NCM data interface
SetDataInterfaceL(aDataEpBufferSize);
// Retrieve data interface number
TUint8 dataInterfaceNumber = 1;
User::LeaveIfError(DataInterfaceNumber(dataInterfaceNumber));
// Setup NCM class descriptor with correct interface number
User::LeaveIfError(SetupClassSpecificDescriptor(dataInterfaceNumber));
OstTraceFunctionExit1( CNCMCLIENTMANAGER_SETNCMINTERFACESL_EXIT, this );
}
/**
* Transfer NCM interface handlers from NCM class controller to NCM
* packet driver through NCM internal server.
* @param aServer a reference to RNcmInternalSrv
*/
void CNcmClientManager::TransferInterfacesL(RNcmInternalSrv& aServer)
{
OstTraceFunctionEntryExt( CNCMCLIENTMANAGER_TRANSFERINTERFACESL_ENTRY, this );
RChunk* commChunk = NULL;
RChunk* dataChunk = NULL;
OstTrace0(TRACE_NORMAL, CNCMCLIENTMANAGER_TRANSFERINTERFACESL_PRE_TRANSFER_INTERFACES, "About to Transfer handles to NCM internal server!");
User::LeaveIfError(iCommLdd.GetDataTransferChunk(commChunk));
User::LeaveIfError(iDataLdd.GetDataTransferChunk(dataChunk));
User::LeaveIfError(aServer.TransferHandle(iCommLdd, *commChunk,
iDataLdd, *dataChunk));
OstTrace0(TRACE_NORMAL, CNCMCLIENTMANAGER_TRANSFERINTERFACESL_INTERFACES_TRANSFERED, "Transfer handles to NCM internal server Done!");
OstTraceFunctionExit1( CNCMCLIENTMANAGER_TRANSFERINTERFACESL_EXIT, this );
}
/**
* Setup NCM communication inteface
*/
void CNcmClientManager::SetCommunicationInterfaceL()
{
OstTraceFunctionEntry1( CNCMCLIENTMANAGER_SETCOMMUNICATIONINTERFACEL_ENTRY, this );
User::LeaveIfError(iCommLdd.Open(0));
iCommLddInitiated = ETrue;
TInt err = KErrNone;
TUsbcScInterfaceInfoBuf ifc0;
TUsbcDeviceState deviceStatus;
User::LeaveIfError(iCommLdd.DeviceStatus(deviceStatus));
if (deviceStatus == EUsbcDeviceStateConfigured)
{
User::Leave( KErrInUse);
}
TUsbDeviceCaps dCaps;
User::LeaveIfError(iCommLdd.DeviceCaps(dCaps));
TInt epNum = dCaps().iTotalEndpoints;
TUsbcEndpointData data[KUsbcMaxEndpoints];
TPtr8 dataptr(reinterpret_cast<TUint8*> (data), sizeof(data),
sizeof(data));
User::LeaveIfError(iCommLdd.EndpointCaps(dataptr));
TBool foundIntIN = EFalse;
for (TInt i = 0; i < epNum; i++)
{
const TUsbcEndpointCaps* caps = &data[i].iCaps;
if ((caps->iTypesAndDir & (KUsbEpTypeInterrupt | KUsbEpDirIn))
== (KUsbEpTypeInterrupt | KUsbEpDirIn))
{
// EEndpoint1 is going to be our INTERRUPT (IN, write) endpoint
ifc0().iEndpointData[0].iType = KUsbEpTypeInterrupt;
ifc0().iEndpointData[0].iDir = KUsbEpDirIn;
ifc0().iEndpointData[0].iSize = caps->MinPacketSize();
ifc0().iEndpointData[0].iInterval = 0x01;
ifc0().iEndpointData[0].iInterval_Hs = 0x01;
foundIntIN = ETrue;
break;
}
}
if (EFalse == foundIntIN)
{
OstTrace0( TRACE_ERROR, CNCMCLIENTMANAGER_SETCOMMUNICATIONINTERFACEL, "Can not find proper endpint for NCM communication interface" );
User::Leave( KErrNotFound);
}
/*********************************************************************************************/
//Communication Class Interface (0x00)
/*********************************************************************************************/
_LIT16(KIfClassName0, "USB Networking (NCM)");
HBufC16* string0 = KIfClassName0().AllocLC();
ifc0().iString = string0;
ifc0().iTotalEndpointsUsed = 1;
ifc0().iClass.iClassNum = 0x02;
ifc0().iClass.iSubClassNum = 0x0D;
ifc0().iClass.iProtocolNum = 0x00;
User::LeaveIfError(iCommLdd.SetInterface(0, ifc0));
err = iCommLdd.FinalizeInterface();
if (KErrNone != err)
{
OstTrace1(TRACE_ERROR, CNCMCLIENTMANAGER_SETCOMMUNICATIONINTERFACEL_FAIL_TO_INIT, "Failed to FinalizeInterface, err %d", err);
User::Leave(err);
}
CleanupStack::PopAndDestroy(string0);
OstTraceFunctionExit1( CNCMCLIENTMANAGER_SETCOMMUNICATIONINTERFACEL_EXIT, this );
}
/**
* Setup NCM data interface
* @param aDataEpBufferSize, the determined data interface
* endpoint buffer size.
*/
void CNcmClientManager::SetDataInterfaceL(TUint& aDataEpBufferSize)
{
OstTraceFunctionEntryExt( CNCMCLIENTMANAGER_SETDATAINTERFACEL_ENTRY, this );
const TUint KMaxScBufferSize = 1048576; // Up limit of end point buffer
const TUint KMinScBufferSize = 262144; // Lower limit of end point buffer
const TUint KMaxScReadSize = 65536;
User::LeaveIfError(iDataLdd.Open(0));
iDataLddInitiated = ETrue;
TUsbcScInterfaceInfoBuf ifc0, ifc1;
TUsbDeviceCaps dCaps;
User::LeaveIfError(iDataLdd.DeviceCaps(dCaps));
TBool isResourceAllocationV2 = ((dCaps().iFeatureWord1
& KUsbDevCapsFeatureWord1_EndpointResourceAllocV2) != 0);
TUsbcDeviceState deviceStatus;
User::LeaveIfError(iDataLdd.DeviceStatus(deviceStatus));
if (deviceStatus == EUsbcDeviceStateConfigured)
{
User::Leave(KErrInUse);
}
TInt epNum = dCaps().iTotalEndpoints;
TUsbcEndpointData data[KUsbcMaxEndpoints];
TPtr8 dataptr(reinterpret_cast<TUint8*> (data), sizeof(data),
sizeof(data));
User::LeaveIfError(iDataLdd.EndpointCaps(dataptr));
const TUint KNcmDataInterfaceEpNumber = 2;
TBool foundBulkIN = EFalse;
TBool foundBulkOUT = EFalse;
TInt maxPacketSize = 0;
for (TInt i = 0; i < epNum; i++)
{
const TUsbcEndpointCaps* caps = &data[i].iCaps;
maxPacketSize = caps->MaxPacketSize();
if (!foundBulkIN && (caps->iTypesAndDir & (KUsbEpTypeBulk
| KUsbEpDirIn)) == (KUsbEpTypeBulk | KUsbEpDirIn))
{
// EEndpoint1 is going to be our TX (IN, write) endpoint
ifc1().iEndpointData[0].iType = KUsbEpTypeBulk;
ifc1().iEndpointData[0].iDir = KUsbEpDirIn;
ifc1().iEndpointData[0].iSize = maxPacketSize;
ifc1().iEndpointData[0].iInterval_Hs = 0x01;
ifc1().iEndpointData[0].iBufferSize = KMaxScBufferSize;
if (isResourceAllocationV2)
{
ifc1().iEndpointData[0].iFeatureWord1 |=
(KUsbcEndpointInfoFeatureWord1_DMA | KUsbcEndpointInfoFeatureWord1_DoubleBuffering);
}
foundBulkIN = ETrue;
if (foundBulkIN && foundBulkOUT)
{
break;
}
continue;
}
if (!foundBulkOUT && (caps->iTypesAndDir & (KUsbEpTypeBulk
| KUsbEpDirOut)) == (KUsbEpTypeBulk | KUsbEpDirOut))
{
// EEndpoint2 is going to be our RX (OUT, read) endpoint
ifc1().iEndpointData[1].iType = KUsbEpTypeBulk;
ifc1().iEndpointData[1].iDir = KUsbEpDirOut;
ifc1().iEndpointData[1].iSize = maxPacketSize;
ifc1().iEndpointData[1].iInterval_Hs = 0;
ifc1().iEndpointData[1].iBufferSize = KMaxScBufferSize;
ifc1().iEndpointData[1].iReadSize = KMaxScReadSize;
if (isResourceAllocationV2)
{
ifc1().iEndpointData[1].iFeatureWord1 |=
(KUsbcEndpointInfoFeatureWord1_DMA | KUsbcEndpointInfoFeatureWord1_DoubleBuffering);
}
foundBulkOUT = ETrue;
if (foundBulkIN && foundBulkOUT)
{
break;
}
continue;
}
}
// Leave if no properly endpoint is found
if (EFalse == foundBulkIN || EFalse == foundBulkOUT)
{
User::Leave(KErrNotFound);
}
_LIT16(KIfClassName0, "NCM Data Interface 0");
HBufC16* string0 = KIfClassName0().AllocL();
CleanupStack::PushL(string0);
ifc0().iString = string0;
ifc0().iTotalEndpointsUsed = 0;
ifc0().iClass.iClassNum = 0x0A;
ifc0().iClass.iSubClassNum = 0x00;
ifc0().iClass.iProtocolNum = 0x01;
User::LeaveIfError(iDataLdd.SetInterface(0, ifc0));
_LIT16(KIfClassName1, "NCM Data Interface 1");
HBufC16* string1 = KIfClassName1().AllocL();
CleanupStack::PushL(string1);
ifc1().iString = string1;
ifc1().iTotalEndpointsUsed = KNcmDataInterfaceEpNumber;
ifc1().iClass.iClassNum = 0x0A;
ifc1().iClass.iSubClassNum = 0x00;
ifc1().iClass.iProtocolNum = 0x01;
// Try to allocate expected memory for data interface endpoints
aDataEpBufferSize = KMaxScBufferSize;
TInt err = KErrNone;
FOREVER
{
OstTrace1(TRACE_NORMAL, CNCMCLIENTMANAGER_SETDATAINTERFACEL_TRY_NEW_BUF_SIZE, "Try buffer size: %d", aDataEpBufferSize);
err = iDataLdd.SetInterface(1, ifc1);
if (KErrNoMemory == err)
{
// Reduce buffer size and retry
aDataEpBufferSize = aDataEpBufferSize / 2;
if (aDataEpBufferSize < KMinScBufferSize)
{
User::Leave(KErrNoMemory);
}
ifc1().iEndpointData[0].iBufferSize = aDataEpBufferSize;
ifc1().iEndpointData[1].iBufferSize = aDataEpBufferSize;
continue;
}
else
{
OstTrace1(TRACE_ERROR, CNCMCLIENTMANAGER_SETDATAINTERFACEL_SET_INTERFACE_FAIL, "Set data interface and the returned err code is %d", err);
// Leave with error code
User::LeaveIfError(err);
break;
}
}
CleanupStack::PopAndDestroy(2, string0);
User::LeaveIfError(iDataLdd.FinalizeInterface());
OstTraceFunctionExit1( CNCMCLIENTMANAGER_SETDATAINTERFACEL_EXIT, this );
}
/**
* Setup the Class Descriptors
* @param aDataInterfaceNumber The interface number of the data class
* @return Error.
*/
TInt CNcmClientManager::SetupClassSpecificDescriptor(TUint8 aDataInterfaceNumber)
{
OstTraceFunctionEntryExt( CNCMCLIENTMANAGER_SETUPCLASSSPECIFICDESCRIPTOR_ENTRY, this );
TInt res;
TNcmClassDescriptor descriptor;
// Header Functional Descriptor- CDC spec table 15
descriptor.iHdrSize = 0x05;
descriptor.iHdrType = 0x24;
descriptor.iHdrSubType = 0x00;
descriptor.iHdrBcdCDC = 0x0120;
// Ethernet Networking Functional Descriptor- ECM spec table 3
descriptor.iEthFunLength = 0x0D;
descriptor.iEthFunType = 0x24;
descriptor.iEthFunSubtype = 0x0F;
// Generate the MAC address new NCM interface
res = SetMacAddressString(descriptor.iMACAddress);
if (res != KErrNone)
{
OstTraceFunctionExitExt( CNCMCLIENTMANAGER_SETUPCLASSSPECIFICDESCRIPTOR_EXIT, this, res );
return res;
}
descriptor.iEthernetStatistics = 0;
descriptor.iMaxSegmentSize = KEthernetFrameSize;
descriptor.iNumberMCFilters = 0;
descriptor.iNumberPowerFilters = 0;
// NCM Functional Descriptor- NCM spec table 5-2
descriptor.iNcmFunLength = 0x06;
descriptor.iNcmFunType = 0x24;
descriptor.iNcmFunSubtype = 0x1A;
descriptor.iNcmVersion = 0x0100;
descriptor.iNetworkCapabilities = 0;
// Union functional descriptor- CDC spec table 16
descriptor.iUnSize = 0x05;
descriptor.iUnType = 0x24;
descriptor.iUnSubType = 0x06;
descriptor.iUnMasterInterface = 0;
descriptor.iUnSlaveInterface = aDataInterfaceNumber;
OstTrace0( TRACE_NORMAL, CNCMCLIENTMANAGER_SETUPCLASSSPECIFICDESCRIPTOR_PRE_SET_BLOCK, "About to call SetCSInterfaceDescriptorBlock" );
res = iCommLdd.SetCSInterfaceDescriptorBlock(0, descriptor.Des());
if (res != KErrNone)
{
OstTraceFunctionExitExt( CNCMCLIENTMANAGER_SETUPCLASSSPECIFICDESCRIPTOR_EXIT_DUP1, this, res );
return res;
}
OstTraceFunctionExitExt( CNCMCLIENTMANAGER_SETUPCLASSSPECIFICDESCRIPTOR_EXIT_DUP2, this, KErrNone );
return KErrNone;
}
/**
* Set the MAC address string in descriptor
* @param aStrIndex the MAC address string index
*/
TInt CNcmClientManager::SetMacAddressString(TUint8& aStrIndex)
{
OstTraceFunctionEntryExt( CNCMCLIENTMANAGER_SETMACADDRESSSTRING_ENTRY, this );
TBuf16<KMacAddrStringDescSize> str;
// Search for MAC address string from index 0x10
aStrIndex = 0x10;
TInt ret = KErrNone;
#ifndef OVERDUMMY_NCMCC
while (aStrIndex++ < 0xFF && ret != KErrNotFound)
{
ret = iDataLdd.GetStringDescriptor(aStrIndex, str);
}
#endif // OVERDUMMY_NCMCC
if (aStrIndex < 0xFF)
{
TBuf8<KEthernetAddressLength*2> macAddrStr;
_LIT8(KMacAddressFormat, "%02X%02X%02X%02X%02X%02X");
OstTraceExt1(TRACE_NORMAL, CNCMCLIENTMANAGER_SETMACADDRESSSTRING, "The MAC address is %s", iHostMacAddress);
macAddrStr.AppendFormat(KMacAddressFormat, iHostMacAddress[0],
iHostMacAddress[1], iHostMacAddress[2], iHostMacAddress[3],
iHostMacAddress[4], iHostMacAddress[5]);
str.Copy(macAddrStr);
ret = iDataLdd.SetStringDescriptor(aStrIndex, str);
}
OstTraceFunctionExitExt( CNCMCLIENTMANAGER_SETMACADDRESSSTRING_EXIT, this, ret );
return ret;
}
/**
* Get NCM data interface number
* @param aInterfaceNumber NCM data interface number
* @return Error.
*/
TInt CNcmClientManager::DataInterfaceNumber(TUint8& aInterfaceNumber)
{
OstTraceFunctionEntryExt( CNCMCLIENTMANAGER_DATAINTERFACENUMBER_ENTRY, this );
TInt interfaceSize = 0;
// 0 means the main interface in the LDD API
TInt res = iDataLdd.GetInterfaceDescriptorSize(0, interfaceSize);
if ( KErrNone == res )
{
OstTraceFunctionExitExt( CNCMCLIENTMANAGER_DATAINTERFACENUMBER_EXIT, this, res );
return res;
}
HBufC8* interfaceBuf = HBufC8::New(interfaceSize);
if ( !interfaceBuf )
{
OstTraceFunctionExitExt( CNCMCLIENTMANAGER_DATAINTERFACENUMBER_EXIT_DUP1, this, KErrNoMemory);
return KErrNoMemory;
}
TPtr8 interfacePtr = interfaceBuf->Des();
interfacePtr.SetLength(0);
// 0 means the main interface in the LDD API
res = iDataLdd.GetInterfaceDescriptor(0, interfacePtr);
if ( KErrNone == res )
{
delete interfaceBuf;
OstTraceFunctionExitExt( CNCMCLIENTMANAGER_DATAINTERFACENUMBER_EXIT_DUP2, this, res );
return res;
}
OstTrace1(TRACE_NORMAL, CNCMCLIENTMANAGER_DATAINTERFACENUMBER_INTERFACE_INFO, "***Interface length =% d", interfacePtr.Length());
for ( TInt i = 0 ; i < interfacePtr.Length() ; i++ )
{
OstTrace1(TRACE_NORMAL, CNCMCLIENTMANAGER_DATAINTERFACENUMBER_INTERFACE_INFO2, "***** %x", interfacePtr[i]);
}
const TUint8* buffer = reinterpret_cast<const TUint8*>(interfacePtr.Ptr());
// 2 is where the interface number is, according to the LDD API
aInterfaceNumber = buffer[2];
OstTraceExt1(TRACE_NORMAL, CNCMCLIENTMANAGER_DATAINTERFACENUMBER, "Interface number = %hhu", aInterfaceNumber);
delete interfaceBuf;
OstTraceFunctionExitExt( CNCMCLIENTMANAGER_DATAINTERFACENUMBER_EXIT_DUP3, this, KErrNone );
return KErrNone;
}