diff -r 000000000000 -r c9bc50fca66e usbmgmt/usbmgr/usbman/server/SRC/CUsbDevice.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usbmgmt/usbmgr/usbman/server/SRC/CUsbDevice.cpp Tue Feb 02 02:02:59 2010 +0200 @@ -0,0 +1,1555 @@ +/* +* Copyright (c) 1997-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: +* Implements the main object of Usbman that manages all the USB Classes +* and the USB Logical Device (via CUsbDeviceStateWatcher). +* +*/ + +/** + @file +*/ + +#include "CUsbDevice.h" +#include "CUsbDeviceStateWatcher.h" +#include +#include "MUsbDeviceNotify.h" +#include "UsbSettings.h" +#include "CUsbServer.h" +#include +#include +#include "UsbUtils.h" +#include + +#ifdef USE_DUMMY_CLASS_CONTROLLER +#include "CUsbDummyClassController.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include //Publish & Subscribe header +#include "CPersonality.h" + +_LIT(KUsbLDDName, "eusbc"); //Name used in call to User::LoadLogicalDevice +_LIT(KUsbLDDFreeName, "Usbc"); //Name used in call to User::FreeLogicalDevice + +#ifdef __FLOG_ACTIVE +_LIT8(KLogComponent, "USBSVR"); +#endif + +// Panic category only used in debug builds +#ifdef _DEBUG +_LIT(KUsbDevicePanicCategory, "UsbDevice"); +#endif + +/** + * Panic codes for the USB Device Class + */ +enum TUsbDevicePanic + { + /** Class called while in an illegal state */ + EBadAsynchronousCall = 0, + EConfigurationError, + EResourceFileNotFound, + /** ConvertUidsL called with an array that is not empty */ + EUidArrayNotEmpty, + }; + + +CUsbDevice* CUsbDevice::NewL(CUsbServer& aUsbServer) +/** + * Constructs a CUsbDevice object. + * + * @return A new CUsbDevice object + */ + { + LOG_STATIC_FUNC_ENTRY + + CUsbDevice* r = new (ELeave) CUsbDevice(aUsbServer); + CleanupStack::PushL(r); + r->ConstructL(); + CleanupStack::Pop(r); + return r; + } + + +CUsbDevice::~CUsbDevice() +/** + * Destructor. + */ + { + LOG_FUNC + + // Cancel any outstanding asynchronous operation. + Cancel(); + + delete iUsbClassControllerIterator; + iSupportedClasses.ResetAndDestroy(); + iSupportedPersonalities.ResetAndDestroy(); + iSupportedClassUids.Close(); + + iExtensionPlugins.ResetAndDestroy(); + + if(iEcom) + iEcom->Close(); + REComSession::FinalClose(); + + // Free any memory allocated to the list of observers. Note that + // we don't want to call ResetAndDestroy, because we don't own + // the observers themselves. + iObservers.Reset(); + +#ifndef __WINS__ + LOGTEXT2(_L8("about to delete device state watcher @ %08x"), (TUint32) iDeviceStateWatcher); + delete iDeviceStateWatcher; + LOGTEXT(_L8("deleted device state watcher")); + + iLdd.Close(); + + LOGTEXT(_L8("Freeing logical device")); + TInt err = User::FreeLogicalDevice(KUsbLDDFreeName); + //Putting the LOGTEXT2 inside the if statement prevents a compiler + //warning about err being unused in UREL builds. + if(err) + { + LOGTEXT2(_L8(" User::FreeLogicalDevice returned %d"),err); + } +#endif + + delete iDefaultSerialNumber; + } + + +CUsbDevice::CUsbDevice(CUsbServer& aUsbServer) + : CActive(EPriorityStandard) + , iDeviceState(EUsbDeviceStateUndefined) + , iServiceState(EUsbServiceIdle) + , iUsbServer(aUsbServer) + , iPersonalityCfged(EFalse) +/** + * Constructor. + */ + { + CActiveScheduler::Add(this); + } + + +void CUsbDevice::ConstructL() +/** + * Performs 2nd phase construction of the USB device. + */ + { + LOG_FUNC + + iEcom = &(REComSession::OpenL()); + + iUsbClassControllerIterator = new(ELeave) CUsbClassControllerIterator(iSupportedClasses); + +#ifndef __WINS__ + LOGTEXT(_L8("About to load LDD")); + TInt err = User::LoadLogicalDevice(KUsbLDDName); + + if (err != KErrNone && err != KErrAlreadyExists) + { + LEAVEL(err); + } + + LOGTEXT(_L8("About to open LDD")); + LEAVEIFERRORL(iLdd.Open(0)); + LOGTEXT(_L8("LDD opened")); + + // hide bus from host while interfaces are being set up + iLdd.DeviceDisconnectFromHost(); + + // Does the USC support cable detection while powered off? If no, then + // call PowerUpUdc when RUsb::Start finishes, as is obvious. If yes, we + // delay calling PowerUpUdc until both the service state is 'started' and + // the device state is not undefined. This is to save power in the UDC + // when there's no point it being powered. + TUsbDeviceCaps devCapsBuf; + LEAVEIFERRORL(iLdd.DeviceCaps(devCapsBuf)); + if ( devCapsBuf().iFeatureWord1 & KUsbDevCapsFeatureWord1_CableDetectWithoutPower ) + { + LOGTEXT(_L8("\tUDC supports cable detect when unpowered")); + iUdcSupportsCableDetectWhenUnpowered = ETrue; + } + else + { + LOGTEXT(_L8("\tUDC does not support cable detect when unpowered")); + } + + TUsbcDeviceState deviceState; + LEAVEIFERRORL(iLdd.DeviceStatus(deviceState)); + SetDeviceState(deviceState); + LOGTEXT(_L8("Got device state")); + + iDeviceStateWatcher = CUsbDeviceStateWatcher::NewL(*this, iLdd); + iDeviceStateWatcher->Start(); + + // Get hold of the default serial number in the driver + // This is so it can be put back in place when a device that sets a + // different serial number (through the P&S key) is stopped + iDefaultSerialNumber = HBufC16::NewL(KUsbStringDescStringMaxSize); + TPtr16 serNum = iDefaultSerialNumber->Des(); + err = iLdd.GetSerialNumberStringDescriptor(serNum); + if (err == KErrNotFound) + { + delete iDefaultSerialNumber; + iDefaultSerialNumber = NULL; + LOGTEXT(_L8("No default serial number")); + } + else + { + LEAVEIFERRORL(err); +#ifdef __FLOG_ACTIVE + TBuf8 narrowString; + narrowString.Copy(serNum); + LOGTEXT2(_L8("Got default serial number %S"), &narrowString); +#endif //__FLOG_ACTIVE + } + + LOGTEXT(_L8("UsbDevice::ConstructL() finished")); +#endif + +#ifndef __OVER_DUMMYUSBDI__ + InstantiateExtensionPluginsL(); +#endif + } + +void CUsbDevice::InstantiateExtensionPluginsL() + { + LOGTEXT(_L8(">>CUsbDevice::InstantiateExtensionPluginsL")); + const TUid KUidExtensionPluginInterface = TUid::Uid(KUsbmanExtensionPluginInterfaceUid); + RImplInfoPtrArray implementations; + const TEComResolverParams noResolverParams; + REComSession::ListImplementationsL(KUidExtensionPluginInterface, noResolverParams, KRomOnlyResolverUid, implementations); + CleanupResetAndDestroyPushL(implementations); + LOGTEXT2(_L8("Number of implementations of extension plugin interface: %d"), implementations.Count()); + + for (TInt i=0; iImplementationUid(), *this); + CleanupStack::PushL(plugin); + iExtensionPlugins.AppendL(plugin); // transfer ownership to iExtensionPlugins + CleanupStack::Pop(plugin); + LOGTEXT2(_L8("Added extension plugin with UID 0x%08x"), + implementations[i]->ImplementationUid()); + } + + CleanupStack::PopAndDestroy(&implementations); + + LOGTEXT(_L8("< order(CUsbClassControllerBase::Compare); + + // For GT171 automated tests, create three instances of the dummy class + // controller, which will read their behaviour from an ini file. Do not + // make any other class controllers. + for ( TUint ii = 0 ; ii < 3 ; ii++ ) + { + AddClassControllerL(CUsbDummyClassController::NewL(*this, ii), order); + } + + LEAVEIFERRORL(iUsbClassControllerIterator->First()); + +#else + + // Add a class controller statically. + // The next line shows how to add a class controller, CUsbExampleClassController, + // statically + + // AddClassControllerL(CUsbExampleClassController::NewL(*this),order); + + // Load class controller plug-ins + + RImplInfoPtrArray implementations; + + const TEComResolverParams noResolverParams; + REComSession::ListImplementationsL(KUidUsbPlugIns, noResolverParams, KRomOnlyResolverUid, implementations); + CleanupResetAndDestroyPushL(implementations); + + LOGTEXT2(_L8("Number of implementations to load %d"), implementations.Count()); + + for (TInt i=0; iImplementationUid()); + const TUid uid = implementations[i]->ImplementationUid(); + LEAVEIFERRORL(iSupportedClassUids.Append(uid)); + } + + CleanupStack::PopAndDestroy(&implementations); + +#endif // USE_DUMMY_CLASS_CONTROLLER + } + +void CUsbDevice::AddClassControllerL(CUsbClassControllerBase* aClassController, + TLinearOrder aOrder) +/** + * Adds a USB class controller to the device. The controller will now be + * managed by this device. Note that the class controller, aClassController, is now + * owned by this function and can be destroyed by it. Calling functions do not need to + * destroy the class controller. + * + * @param aClassController Class to be managed + * @param aOrder Specifies order CUsbClassControllerBase objects are to be + * added + */ + { + LOG_FUNC + + + TInt rc = KErrNone; + + if(isPersonalityCfged()) // do not take into account priorities + { + rc = iSupportedClasses.Append(aClassController); + } + else + { + rc = iSupportedClasses.InsertInOrderAllowRepeats( + aClassController, aOrder); + } + + if (rc != KErrNone) + { + // Avoid memory leak by deleting class controller if the append fails. + delete aClassController; + LEAVEL(rc); + } + } + +void CUsbDevice::RegisterObserverL(MUsbDeviceNotify& aObserver) +/** + * Register an observer of the device. + * Presently, the device supports watching state. + * + * @param aObserver New Observer of the device + */ + { + LOG_FUNC + + LEAVEIFERRORL(iObservers.Append(&aObserver)); + } + + +void CUsbDevice::DeRegisterObserver(MUsbDeviceNotify& aObserver) +/** + * De-registers an existing device observer. + * + * @param aObserver The existing device observer to be de-registered + */ + { + LOG_FUNC + + TInt index = iObservers.Find(&aObserver); + + if (index >= 0) + iObservers.Remove(index); + } + + +void CUsbDevice::StartL() +/** + * Start the USB Device and all its associated USB classes. + * Reports errors and state changes via observer interface. + */ + { + LOG_FUNC + + Cancel(); + SetServiceState(EUsbServiceStarting); + + TRAPD(err, SetDeviceDescriptorL()); + if ( err != KErrNone ) + { + SetServiceState(EUsbServiceIdle); + LEAVEL(err); + } + + iLastError = KErrNone; + StartCurrentClassController(); + } + +void CUsbDevice::Stop() +/** + * Stop the USB device and all its associated USB classes. + */ + { + LOG_FUNC + + Cancel(); + SetServiceState(EUsbServiceStopping); + + iLastError = KErrNone; + StopCurrentClassController(); + } + +void CUsbDevice::SetServiceState(TUsbServiceState aState) +/** + * Change the device's state and report the change to the observers. + * + * @param aState New state that the device is moving to + */ + { + LOGTEXT3(_L8("Calling: CUsbDevice::SetServiceState [iServiceState=%d,aState=%d]"), + iServiceState, aState); + + if (iServiceState != aState) + { + // Change state straight away in case any of the clients check it + TUsbServiceState oldState = iServiceState; + iServiceState = aState; + TUint length = iObservers.Count(); + + for (TUint i = 0; i < length; i++) + { + iObservers[i]->UsbServiceStateChange(LastError(), oldState, + iServiceState); + } + + if (iServiceState == EUsbServiceIdle) + iUsbServer.LaunchShutdownTimerIfNoSessions(); + } + LOGTEXT(_L8("Exiting: CUsbDevice::SetServiceState")); + } + +void CUsbDevice::SetDeviceState(TUsbcDeviceState aState) +/** + * The CUsbDevice::SetDeviceState method + * + * Change the device's state and report the change to the observers + * + * @internalComponent + * @param aState New state that the device is moving to + */ + { + LOG_FUNC + LOGTEXT3(_L8("\taState = %d, iDeviceState = %d"), aState, iDeviceState); + + TUsbDeviceState state; + switch (aState) + { + case EUsbcDeviceStateUndefined: + state = EUsbDeviceStateUndefined; + break; + case EUsbcDeviceStateAttached: + state = EUsbDeviceStateAttached; + break; + case EUsbcDeviceStatePowered: + state = EUsbDeviceStatePowered; + break; + case EUsbcDeviceStateDefault: + state = EUsbDeviceStateDefault; + break; + case EUsbcDeviceStateAddress: + state = EUsbDeviceStateAddress; + break; + case EUsbcDeviceStateConfigured: + state = EUsbDeviceStateConfigured; + break; + case EUsbcDeviceStateSuspended: + state = EUsbDeviceStateSuspended; + break; + default: + return; + } + + if (iDeviceState != state) + { +#ifndef __WINS__ + if (iDeviceState == EUsbDeviceStateUndefined && + iUdcSupportsCableDetectWhenUnpowered && + iServiceState == EUsbServiceStarted) + { + // We just changed state away from undefined. Hence the cable must + // now be attached (if it wasn't before). If the UDC supports + // cable detection when unpowered, NOW is the right time to power + // it up (so long as usbman is fully started). + (void)PowerUpAndConnect(); // We don't care about any errors here. + } +#endif // __WINS__ + // Change state straight away in case any of the clients check it + TUsbDeviceState oldState = iDeviceState; + iDeviceState = state; + TUint length = iObservers.Count(); + + for (TUint i = 0; i < length; i++) + { + iObservers[i]->UsbDeviceStateChange(LastError(), oldState, iDeviceState); + } + } + } + +/** + * Callback called by CDeviceHandler when the USB bus has sucessfully + * completed a ReEnumeration (restarted all services). + */ +void CUsbDevice::BusEnumerationCompleted() + { + LOG_FUNC + + // Has the start been cancelled? + if (iServiceState == EUsbServiceStarting) + { + SetServiceState(EUsbServiceStarted); + } + else + { + LOGTEXT(_L8(" Start has been cancelled!")); + } + } + +void CUsbDevice::BusEnumerationFailed(TInt aError) +/** + * Callback called by CDeviceHandler when the USB bus has + * completed an ReEnumeration (Restarted all services) with errors + * + * @param aError Error that has occurred during Re-enumeration + */ + { + LOGTEXT2(_L8("CUsbDevice::BusEnumerationFailed [aError=%d]"), aError); + iLastError = aError; + + if (iServiceState == EUsbServiceStarting) + { + SetServiceState(EUsbServiceStopping); + StopCurrentClassController(); + } + else + { + LOGTEXT(_L8(" Start has been cancelled!")); + } + } + + +void CUsbDevice::StartCurrentClassController() +/** + * Called numerous times to start all the USB classes. + */ + { + LOG_FUNC + + iUsbClassControllerIterator->Current()->Start(iStatus); + SetActive(); + } + +void CUsbDevice::StopCurrentClassController() +/** + * Called numerous times to stop all the USB classes. + */ + { + LOG_FUNC + + iUsbClassControllerIterator->Current()->Stop(iStatus); + SetActive(); + } + +/** +Utility function to power up the UDC and connect the +device to the host. +*/ +TInt CUsbDevice::PowerUpAndConnect() + { + LOG_FUNC + LOGTEXT(_L8("\tPowering up UDC...")); + TInt res = iLdd.PowerUpUdc(); + LOGTEXT2(_L8("\tPowerUpUdc res = %d"), res); + res = iLdd.DeviceConnectToHost(); + LOGTEXT2(_L8("\tDeviceConnectToHost res = %d"), res); + return res; + } + +void CUsbDevice::RunL() +/** + * Called when starting or stopping a USB class has completed, successfully or + * otherwise. Continues with the process of starting or stopping until all + * classes have been completed. + */ + { + LOGTEXT2(_L8(">>CUsbDevice::RunL [iStatus=%d]"), iStatus.Int()); + + LEAVEIFERRORL(iStatus.Int()); + + switch (iServiceState) + { + case EUsbServiceStarting: + if (iUsbClassControllerIterator->Next() == KErrNotFound) + { +#ifndef __WINS__ + if (!iUdcSupportsCableDetectWhenUnpowered || iDeviceState != EUsbDeviceStateUndefined) + { + // We've finished starting the classes. We can just power up the UDC + // now: there's no need to re-enumerate, because we soft disconnected + // earlier. This will also do a soft connect. + LOGTEXT(_L8("Finished starting classes: powering up UDC")); + + // It isn't an error if this call fails. This will happen, for example, + // in the case where there are no USB classes defined. + (void)PowerUpAndConnect(); + } +#endif + // If we're not running on target, we can just go to "started". + SetServiceState(EUsbServiceStarted); + } + else + { + StartCurrentClassController(); + } + break; + + case EUsbServiceStopping: + if (iUsbClassControllerIterator->Previous() == KErrNotFound) + { + // if stopping classes, hide the USB interface from the host +#ifndef __WINS__ + iLdd.DeviceDisconnectFromHost(); + + // Restore the default serial number + if (iDefaultSerialNumber) + { + TInt res = iLdd.SetSerialNumberStringDescriptor(*iDefaultSerialNumber); + LOGTEXT2(_L8("Restore default serial number res = %d"), res); + } + else + { + TInt res = iLdd.RemoveSerialNumberStringDescriptor(); + LOGTEXT2(_L8("Remove serial number res = %d"), res); + } + +#endif + SetServiceState(EUsbServiceIdle); + } + else + { + StopCurrentClassController(); + } + break; + + default: + __ASSERT_DEBUG( EFalse, _USB_PANIC(KUsbDevicePanicCategory, EBadAsynchronousCall) ); + break; + } + LOGTEXT(_L8("<Current()->Cancel(); + break; + + default: + __ASSERT_DEBUG( EFalse, _USB_PANIC(KUsbDevicePanicCategory, EBadAsynchronousCall) ); + break; + } + } + +TInt CUsbDevice::RunError(TInt aError) +/** + * Standard active object RunError function. Handles errors which occur when + * starting and stopping the USB class objects. + * + * @param aError The error which occurred + * @return Always KErrNone, to avoid an active scheduler panic + */ + { + LOGTEXT2(_L8("CUsbDevice::RunError [aError=%d]"), aError); + + iLastError = aError; + + switch (iServiceState) + { + case EUsbServiceStarting: + case EUsbServiceStarted: + // An error has happened while we're either started or starting, so + // we have to stop all the classes which were successfully started. + if ((iUsbClassControllerIterator->Current()->State() == + EUsbServiceIdle) && + (iUsbClassControllerIterator->Previous() == KErrNotFound)) + { + SetServiceState(EUsbServiceIdle); + } + else + { + SetServiceState(EUsbServiceStopping); + StopCurrentClassController(); + } + break; + + case EUsbServiceStopping: + // Argh, we've got problems. Let's stop as many classes as we can. + if (iUsbClassControllerIterator->Previous() == KErrNotFound) + SetServiceState(EUsbServiceIdle); + else + StopCurrentClassController(); + break; + + default: + __ASSERT_DEBUG( EFalse, _USB_PANIC(KUsbDevicePanicCategory, EBadAsynchronousCall) ); + break; + } + + return KErrNone; + } + +CUsbClassControllerIterator* CUsbDevice::UccnGetClassControllerIteratorL() +/** + * Function used by USB classes to get an iterator over the set of classes + * owned by this device. Note that the caller takes ownership of the iterator + * which this function returns. + * + * @return A new iterator + */ + { + LOG_FUNC + + return new (ELeave) CUsbClassControllerIterator(iSupportedClasses); + } + +void CUsbDevice::UccnError(TInt aError) +/** + * Function called by USB classes to notify the device of a fatal error. In + * this situation, we should just stop all the classes we can. + * + * @param aError The error that's occurred + */ + { + LOG_FUNC + + RunError(aError); + } + + +#ifdef __FLOG_ACTIVE +void CUsbDevice::PrintDescriptor(CUsbDevice::TUsbDeviceDescriptor& aDeviceDescriptor) + { + LOGTEXT2(_L8("\tiLength is %d"), aDeviceDescriptor.iLength); + LOGTEXT2(_L8("\tiDescriptorType is %d"), aDeviceDescriptor.iDescriptorType); + LOGTEXT2(_L8("\tBcdUsb is: 0x%04x"), aDeviceDescriptor.iBcdUsb); + LOGTEXT2(_L8("\tDeviceClass is: 0x%02x"), aDeviceDescriptor.iDeviceClass); + LOGTEXT2(_L8("\tDeviceSubClass is: 0x%02x"), aDeviceDescriptor.iDeviceSubClass); + LOGTEXT2(_L8("\tDeviceProtocol is: 0x%02x"), aDeviceDescriptor.iDeviceProtocol); + LOGTEXT2(_L8("\tiMaxPacketSize is: 0x%02x"), aDeviceDescriptor.iMaxPacketSize); + + LOGTEXT2(_L8("\tVendorId is: 0x%04x"), aDeviceDescriptor.iIdVendor); + LOGTEXT2(_L8("\tProductId is: 0x%04x"), aDeviceDescriptor.iIdProduct); + LOGTEXT2(_L8("\tBcdDevice is: 0x%04x"), aDeviceDescriptor.iBcdDevice); + + LOGTEXT2(_L8("\tiManufacturer is: 0x%04x"), aDeviceDescriptor.iManufacturer); + LOGTEXT2(_L8("\tiSerialNumber is: 0x%04x"), aDeviceDescriptor.iSerialNumber); + LOGTEXT2(_L8("\tiNumConfigurations is: 0x%04x"), aDeviceDescriptor.iNumConfigurations); + } +#endif +// +void CUsbDevice::SetDeviceDescriptorL() +/** + * Modifies the USB device descriptor. + */ + { + LOG_FUNC + +#ifndef __WINS__ + + TInt desSize = 0; + iLdd.GetDeviceDescriptorSize(desSize); + LOGTEXT2(_L8("UDeviceDescriptorSize = %d"), desSize); + HBufC8* deviceBuf = HBufC8::NewLC(desSize); + TPtr8 devicePtr = deviceBuf->Des(); + devicePtr.SetLength(0); + + TInt ret = iLdd.GetDeviceDescriptor(devicePtr); + + if (ret != KErrNone) + { + LOGTEXT2(_L8("Unable to fetch device descriptor. Error: %d"), ret); + LEAVEL(ret); + } + + TUsbDeviceDescriptor* deviceDescriptor = reinterpret_cast( + const_cast(devicePtr.Ptr())); + + +#else + + // Create an empty descriptor to allow the settings + // to be read in from the resource file + TUsbDeviceDescriptor descriptor; + TUsbDeviceDescriptor* deviceDescriptor = &descriptor; + +#endif // __WINS__ + + if (iPersonalityCfged) + { + SetUsbDeviceSettingsFromPersonalityL(*deviceDescriptor); + } + else + { + SetUsbDeviceSettingsL(*deviceDescriptor); + } + +#ifndef __WINS__ + ret = iLdd.SetDeviceDescriptor(devicePtr); + + if (ret != KErrNone) + { + LOGTEXT2(_L8("Unable to set device descriptor. Error: %d"), ret); + LEAVEL(ret); + } + + CleanupStack::PopAndDestroy(deviceBuf); + +#endif // __WINS__ + } + +void CUsbDevice::SetUsbDeviceSettingsDefaultsL(CUsbDevice::TUsbDeviceDescriptor& aDeviceDescriptor) +/** + * Set the device settings defaults, as per the non-resource + * version of the USB manager + * + * @param aDeviceDescriptor The device descriptor for the USB device + */ + { + aDeviceDescriptor.iDeviceClass = KUsbDefaultDeviceClass; + aDeviceDescriptor.iDeviceSubClass = KUsbDefaultDeviceSubClass; + aDeviceDescriptor.iDeviceProtocol = KUsbDefaultDeviceProtocol; + aDeviceDescriptor.iIdVendor = KUsbDefaultVendorId; + aDeviceDescriptor.iIdProduct = KUsbDefaultProductId; + } + +void CUsbDevice::SetUsbDeviceSettingsL(CUsbDevice::TUsbDeviceDescriptor& aDeviceDescriptor) +/** + * Configure the USB device, reading in the settings from a + * resource file where possible. + * + * @param aDeviceDescriptor The device descriptor for the USB device + */ + { + LOG_FUNC + + // First, use the default values + LOGTEXT(_L8("Setting default values for the configuration")); + SetUsbDeviceSettingsDefaultsL(aDeviceDescriptor); + + // Now try to get the configuration from the resource file + RFs fs; + LEAVEIFERRORL(fs.Connect()); + CleanupClosePushL(fs); + + RResourceFile resource; + TRAPD(err, resource.OpenL(fs, KUsbManagerResource)); + LOGTEXT2(_L8("Opened resource file with error %d"), err); + + if (err != KErrNone) + { + LOGTEXT(_L8("Unable to open resource file: using default settings")); + CleanupStack::PopAndDestroy(&fs); + return; + } + + CleanupClosePushL(resource); + + resource.ConfirmSignatureL(KUsbManagerResourceVersion); + + HBufC8* id = resource.AllocReadLC(USB_CONFIG); + + // The format of the USB resource structure is: + // + // STRUCT usb_configuration + // { + // WORD vendorId = 0x0e22; + // WORD productId = 0x000b; + // WORD bcdDevice = 0x0000; + // LTEXT manufacturer = "Symbian Ltd."; + // LTEXT product = "Symbian OS"; + // } + // + // Note that the resource must be read in this order! + + TResourceReader reader; + reader.SetBuffer(id); + + aDeviceDescriptor.iIdVendor = static_cast(reader.ReadUint16()); + aDeviceDescriptor.iIdProduct = static_cast(reader.ReadUint16()); + aDeviceDescriptor.iBcdDevice = static_cast(reader.ReadUint16()); + + // Try to read device and manufacturer name from new SysUtil API + TPtrC16 sysUtilModelName; + TPtrC16 sysUtilManuName; + + // This method returns ownership. + CDeviceTypeInformation* deviceInfo = SysUtil::GetDeviceTypeInfoL(); + CleanupStack::PushL(deviceInfo); + TInt gotSysUtilModelName = deviceInfo->GetModelName(sysUtilModelName); + TInt gotSysUtilManuName = deviceInfo->GetManufacturerName(sysUtilManuName); + + TPtrC manufacturerString = reader.ReadTPtrC(); + TPtrC productString = reader.ReadTPtrC(); + + // If we succesfully read the manufacturer or device name from SysUtil API + // then set these results, otherwise use the values defined in resource file +#ifndef __WINS__ + if (gotSysUtilManuName == KErrNone) + { + LEAVEIFERRORL(iLdd.SetManufacturerStringDescriptor(sysUtilManuName)); + } + else + { + LEAVEIFERRORL(iLdd.SetManufacturerStringDescriptor(manufacturerString)); + } + + if (gotSysUtilModelName == KErrNone) + { + LEAVEIFERRORL(iLdd.SetProductStringDescriptor(sysUtilModelName)); + } + else + { + LEAVEIFERRORL(iLdd.SetProductStringDescriptor(productString)); + } +#endif // __WINS__ + +#ifdef __FLOG_ACTIVE + PrintDescriptor(aDeviceDescriptor); + TBuf8 narrowString; + narrowString.Copy(manufacturerString); + LOGTEXT2(_L8("Manufacturer is: '%S'"), &narrowString); + narrowString.Copy(productString); + LOGTEXT2(_L8("Product is: '%S'"), &narrowString); +#endif // __FLOG_ACTIVE + +#ifndef __WINS__ + //Read the published serial number. The key is the UID KUidUsbmanServer = 0x101FE1DB + TBuf16 serNum; + TInt r = RProperty::Get(KUidSystemCategory,0x101FE1DB,serNum); + if(r==KErrNone) + { +#ifdef __FLOG_ACTIVE + TBuf8 narrowString; + narrowString.Copy(serNum); + LOGTEXT2(_L8("Setting published SerialNumber: %S"), &narrowString); +#endif // __FLOG_ACTIVE + //USB spec doesn't give any constraints on what constitutes a valid serial number. + //As long as it is a string descriptor it is valid. + LEAVEIFERRORL(iLdd.SetSerialNumberStringDescriptor(serNum)); + } +#ifdef __FLOG_ACTIVE + else + { + LOGTEXT(_L8("SerialNumber has not been published")); + } +#endif // __FLOG_ACTIVE +#endif // __WINS__ + + + CleanupStack::PopAndDestroy(4, &fs); // deviceInfo, id, resource, fs + } + +void CUsbDevice::SetUsbDeviceSettingsFromPersonalityL(CUsbDevice::TUsbDeviceDescriptor& aDeviceDescriptor) +/** + * Configure the USB device from the current personality. + * + * @param aDeviceDescriptor The device descriptor for the USB device + */ + { + LOG_FUNC + + // First, use the default values + LOGTEXT(_L8("Setting default values for the configuration")); + SetUsbDeviceSettingsDefaultsL(aDeviceDescriptor); + + // Now try to get the configuration from the current personality + const CUsbDevice::TUsbDeviceDescriptor& deviceDes = iCurrentPersonality->DeviceDescriptor(); + aDeviceDescriptor.iDeviceClass = deviceDes.iDeviceClass; + aDeviceDescriptor.iDeviceSubClass = deviceDes.iDeviceSubClass; + aDeviceDescriptor.iDeviceProtocol = deviceDes.iDeviceProtocol; + aDeviceDescriptor.iIdVendor = deviceDes.iIdVendor; + aDeviceDescriptor.iIdProduct = deviceDes.iIdProduct; + aDeviceDescriptor.iBcdDevice = deviceDes.iBcdDevice; + aDeviceDescriptor.iSerialNumber = deviceDes.iSerialNumber; + aDeviceDescriptor.iNumConfigurations = deviceDes.iNumConfigurations; + +#ifndef __WINS__ + LEAVEIFERRORL(iLdd.SetManufacturerStringDescriptor(*(iCurrentPersonality->Manufacturer()))); + LEAVEIFERRORL(iLdd.SetProductStringDescriptor(*(iCurrentPersonality->Product()))); + + //Read the published serial number. The key is the UID KUidUsbmanServer = 0x101FE1DB + TBuf16 serNum; + TInt r = RProperty::Get(KUidSystemCategory,0x101FE1DB,serNum); + if(r==KErrNone) + { +#ifdef __FLOG_ACTIVE + TBuf8 narrowString; + narrowString.Copy(serNum); + LOGTEXT2(_L8("Setting published SerialNumber: %S"), &narrowString); +#endif // __FLOG_ACTIVE + //USB spec doesn't give any constraints on what constitutes a valid serial number. + //As long as it is a string descriptor it is valid. + LEAVEIFERRORL(iLdd.SetSerialNumberStringDescriptor(serNum)); + } +#ifdef __FLOG_ACTIVE + else + { + LOGTEXT(_L8("SerialNumber has not been published")); + } +#endif // __FLOG_ACTIVE +#endif // __WINS__ + + +#ifdef __FLOG_ACTIVE + PrintDescriptor(aDeviceDescriptor); + +#ifndef __WINS__ + TBuf16 wideString; + TBuf8 narrowString; + + LEAVEIFERRORL(iLdd.GetConfigurationStringDescriptor(wideString)); + narrowString.Copy(wideString); + LOGTEXT2(_L8("Configuration is: '%S'"), &narrowString); +#endif // __WINS__ + +#endif // __FLOG_ACTIVE + } + +void CUsbDevice::TryStartL(TInt aPersonalityId) +/** + * Start all USB classes associated with the personality identified + * by aPersonalityId. Reports errors and state changes via observer + * interface. + * + * @param aPersonalityId a personality id + */ + { + LOG_FUNC + SetCurrentPersonalityL(aPersonalityId); + + SelectClassControllersL(); + SetServiceState(EUsbServiceStarting); + + TRAPD(err, SetDeviceDescriptorL()); + if ( err != KErrNone ) + { + SetServiceState(EUsbServiceIdle); + LEAVEL(err); + } + + iLastError = KErrNone; + StartCurrentClassController(); + } + +TInt CUsbDevice::CurrentPersonalityId() const +/** + * @return the current personality id + */ + { + LOG_FUNC + return iCurrentPersonality->PersonalityId(); + } + +const RPointerArray& CUsbDevice::Personalities() const +/** + * @return a const reference to RPointerArray + */ + { + LOG_FUNC + return iSupportedPersonalities; + } + +const CPersonality* CUsbDevice::GetPersonality(TInt aPersonalityId) const +/** + * Obtains a handle to the CPersonality object whose id is aPersonalityId + * + * @param aPeraonalityId a personality id + * @return a const pointer to the CPersonality object whose id is aPersonalityId if found + * or 0 otherwise. + */ + { + LOG_FUNC + + TInt count = iSupportedPersonalities.Count(); + for (TInt i = 0; i < count; i++) + { + if (iSupportedPersonalities[i]->PersonalityId() == aPersonalityId) + { + return iSupportedPersonalities[i]; + } + } + + return 0; + } + +void CUsbDevice::SetCurrentPersonalityL(TInt aPersonalityId) +/** + * Sets the current personality to the personality with id aPersonalityId + */ + { + LOG_FUNC + const CPersonality* personality = GetPersonality(aPersonalityId); + if (!personality) + { + LOGTEXT(_L8("Personality id not found")); + LEAVEL(KErrNotFound); + } + + iCurrentPersonality = personality; + } + +void CUsbDevice::ValidatePersonalitiesL() +/** + * Verifies all class controllers associated with each personality are loaded. + * Leave if validation fails. + */ + { + LOG_FUNC + + TInt personalityCount = iSupportedPersonalities.Count(); + for (TInt i = 0; i < personalityCount; i++) + { + const RArray& classUids = iSupportedPersonalities[i]->SupportedClasses(); + TInt uidCount = classUids.Count(); + for (TInt j = 0; j < uidCount; j++) + { + TInt ccCount = iSupportedClassUids.Count(); + TInt k; + for (k = 0; k < ccCount; k++) + { + if (iSupportedClassUids[k] == classUids[j]) + { + break; + } + } + if (k == ccCount) + { + LOGTEXT(_L8("personality validation failed")); + LEAVEL(KErrAbort); + } + } + } + } +/** +Converts text string with UIDs to array of Uint + +If there is an error during the conversion, this function will not clean-up, +so there may still be UIDs allocated in the RArray. + +@param aStr Reference to a string containing one or more UIDs in hex +@param aUIDs On return array of UIDs parsed from the input string +@panic EUidArrayNotEmpty if the RArray passed in is not empty +*/ +void CUsbDevice::ConvertUidsL(const TDesC& aStr, RArray& aUidArray) + { + // Function assumes that aUIDs is empty + __ASSERT_DEBUG( aUidArray.Count() == 0, _USB_PANIC(KUsbDevicePanicCategory, EUidArrayNotEmpty) ); + + TLex input(aStr); + + // Scan through string to find UIDs + // Need to do this at least once, as no UID in the string is an error + do + { + // Find first hex digit + while (!(input.Eos() || input.Peek().IsHexDigit())) + { + input.Inc(); + } + + // Convert and add to array + TUint val; + LEAVEIFERRORL(input.Val(val,EHex)); + aUidArray.AppendL(val); + } + while (!input.Eos()); + } + +void CUsbDevice::ReadPersonalitiesL() +/** + * Reads configured personalities from the resource file + */ + { + LOG_FUNC + iPersonalityCfged = EFalse; + // Now try to connect to file server + RFs fs; + LEAVEIFERRORL(fs.Connect()); + CleanupClosePushL(fs); + + TFileName resourceFileName; + ResourceFileNameL(resourceFileName); + RResourceFile resource; + TRAPD(err, resource.OpenL(fs, resourceFileName)); + LOGTEXT2(_L8("Opened resource file with error %d"), err); + + if (err != KErrNone) + { + LOGTEXT(_L8("Unable to open resource file")); + CleanupStack::PopAndDestroy(&fs); + return; + } + + CleanupClosePushL(resource); + + TInt resourceVersion = resource.SignatureL(); + LOGTEXT2(_L8("Resource file signature is %d"), resourceVersion); + // Check for the version is valid(EUsbManagerResourceVersionOne, EUsbManagerResourceVersionTwo + // or EUsbManagerResourceVersionThree). + if(resourceVersion > EUsbManagerResourceVersionThree) + { + LOGTEXT2(_L8("Version of resource file is valid (>%d)"), EUsbManagerResourceVersionThree); + User::LeaveIfError(KErrNotSupported); + } + + resource.ConfirmSignatureL(resourceVersion); + + HBufC8* personalityBuf = 0; + TRAPD(ret, personalityBuf = resource.AllocReadL(DEVICE_PERSONALITIES)); + // If personalities resource is not found, swallow the error and return + // as no specified personalities is a valid configuration + if (ret == KErrNotFound) + { + LOGTEXT(_L8("Personalities are not configured")); + CleanupStack::PopAndDestroy(2, &fs); + return; + } + // Otherwise leave noisily if the AllocRead fails + LEAVEIFERRORL(ret); + CleanupStack::PushL(personalityBuf); + + // The format of the USB resource structure is: + // + // STRUCT PERSONALITY + // { + // WORD bcdDeviceClass; + // WORD bcdDeviceSubClass; + // WORD protocol; + // WORD numConfigurations; + // WORD vendorId; + // WORD productId; + // WORD bcdDevice; + // LTEXT manufacturer; + // LTEXT product; + // WORD id; // personality id + // LTEXT class_uids; + // LTEXT description; // personality description + // LTEXT detailedDescription; //detailed description. This is in version 2 + // LONG Property; + // } + // + // Note that the resource must be read in this order! + + TResourceReader reader; + reader.SetBuffer(personalityBuf); + + TUint16 personalityCount = static_cast(reader.ReadUint16()); + + // Read the manufacturer and device name (product) here from SysUtil class + TPtrC16 sysUtilModelName; + TPtrC16 sysUtilManuName; + + // This method returns ownership. + CDeviceTypeInformation* deviceInfo = SysUtil::GetDeviceTypeInfoL(); + CleanupStack::PushL(deviceInfo); + TInt gotSysUtilModelName = deviceInfo->GetModelName(sysUtilModelName); + TInt gotSysUtilManuName = deviceInfo->GetManufacturerName(sysUtilManuName); + + for (TInt idx = 0; idx < personalityCount; idx++) + { + // read a personality + TUint8 bDeviceClass = static_cast(reader.ReadUint8()); + TUint8 bDeviceSubClass = static_cast(reader.ReadUint8()); + TUint8 protocol = static_cast(reader.ReadUint8()); + TUint8 numConfigurations = static_cast(reader.ReadUint8()); + TUint16 vendorId = static_cast(reader.ReadUint16()); + TUint16 productId = static_cast(reader.ReadUint16()); + TUint16 bcdDevice = static_cast(reader.ReadUint16()); + TPtrC manufacturer = reader.ReadTPtrC(); + TPtrC product = reader.ReadTPtrC(); + TUint16 id = static_cast(reader.ReadUint16()); + TPtrC uidsStr = reader.ReadTPtrC(); + TPtrC description = reader.ReadTPtrC(); + + RArray uids; + CleanupClosePushL(uids); + ConvertUidsL(uidsStr, uids); + // creates a CPersonality object + CPersonality* personality = CPersonality::NewL(); + CleanupStack::PushL(personality); + + personality->SetVersion(resourceVersion); + + // populates personality object + personality->SetId(id); + + for (TInt uidIdx = 0; uidIdx < uids.Count(); uidIdx++) + { + LEAVEIFERRORL(personality->AddSupportedClasses(TUid::Uid(uids[uidIdx]))); + } + + // gets a handle to iDeviceDescriptor of personality + CUsbDevice::TUsbDeviceDescriptor& dvceDes = personality->DeviceDescriptor(); + if (gotSysUtilManuName == KErrNone) + { + personality->SetManufacturer(&sysUtilManuName); + } + else + { + personality->SetManufacturer(&manufacturer); + } + + if (gotSysUtilModelName == KErrNone) + { + personality->SetProduct(&sysUtilModelName); + } + else + { + personality->SetProduct(&product); + } + + personality->SetDescription(&description); + dvceDes.iDeviceClass = bDeviceClass; + dvceDes.iDeviceSubClass = bDeviceSubClass; + dvceDes.iDeviceProtocol = protocol; + dvceDes.iIdVendor = vendorId; + dvceDes.iIdProduct= productId; + dvceDes.iBcdDevice = bcdDevice; + dvceDes.iNumConfigurations = numConfigurations; + + //detailedDescription is only supported after EUsbManagerResourceVersionTwo + if(resourceVersion >= EUsbManagerResourceVersionTwo) + { + TPtrC detailedDescription = reader.ReadTPtrC(); + personality->SetDetailedDescription(&detailedDescription); +#ifdef __FLOG_ACTIVE + TBuf8 narrowLongBuf; + narrowLongBuf.Copy(detailedDescription); + LOGTEXT2(_L8("detailed description = '%S'"), &narrowLongBuf); +#endif // __FLOG_ACTIVE + } + + //Property is only supported after EUsbManagerResourceVersionThree + if(resourceVersion >= EUsbManagerResourceVersionThree) + { + TUint32 property = static_cast(reader.ReadUint32()); + personality->SetProperty(property); +#ifdef __FLOG_ACTIVE + LOGTEXT2(_L8("property = %d\n"), property); +#endif // __FLOG_ACTIVE + } + + // Append personality to iSupportedPersonalities + iSupportedPersonalities.AppendL(personality); + // Now pop off personality + CleanupStack::Pop(personality); +#ifdef __FLOG_ACTIVE + // Debugging + LOGTEXT2(_L8("personalityCount = %d\n"), personalityCount); + LOGTEXT2(_L8("bDeviceClass = %d\n"), bDeviceClass); + LOGTEXT2(_L8("bDeviceSubClass = %d\n"), bDeviceSubClass); + LOGTEXT2(_L8("protocol = %d\n"), protocol); + LOGTEXT2(_L8("numConfigurations = %d\n"), numConfigurations); + LOGTEXT2(_L8("vendorId = %d\n"), vendorId); + LOGTEXT2(_L8("productId = %d\n"), productId); + LOGTEXT2(_L8("bcdDevice = %d\n"), bcdDevice); + TBuf8 narrowBuf; + narrowBuf.Copy(manufacturer); + LOGTEXT2(_L8("manufacturer = '%S'"), &narrowBuf); + narrowBuf.Copy(product); + LOGTEXT2(_L8("product = '%S'"), &narrowBuf); + LOGTEXT2(_L8("id = %d\n"), id); + LOGTEXT(_L8("ClassUids{")); + for (TInt k = 0; k < uids.Count(); k++) + { + LOGTEXT2(_L8("%d"), uids[k]); + } + LOGTEXT(_L8("}")); + narrowBuf.Copy(description); + LOGTEXT2(_L8("description = '%S'"), &narrowBuf); +#endif // __FLOG_ACTIVE + CleanupStack::PopAndDestroy(&uids); // close uid array + } + + CleanupStack::PopAndDestroy(4, &fs); // deviceInfo, personalityBuf, resource, fs + iPersonalityCfged = ETrue; + } + +void CUsbDevice::SelectClassControllersL() +/** + * Selects class controllers for the current personality + */ + { + LOG_FUNC + + CreateClassControllersL(iCurrentPersonality->SupportedClasses()); + } +#ifdef USE_DUMMY_CLASS_CONTROLLER +void CUsbDevice::CreateClassControllersL(const RArray& /* aClassUids*/) +#else +void CUsbDevice::CreateClassControllersL(const RArray& aClassUids) +#endif +/** + * Creates a class controller object for each class uid + * + * @param aClassUids an array of class uids + */ + { + LOG_FUNC + +#ifndef USE_DUMMY_CLASS_CONTROLLER + + //create a TLinearOrder to supply the comparison function, Compare(), to be used + //to determine the order to add class controllers + TLinearOrder order(CUsbClassControllerBase::Compare); + + TInt count = aClassUids.Count(); + + // destroy any class controller objects in iSupportedClasses and reset it for reuse + iSupportedClasses.ResetAndDestroy(); + LOGTEXT2(_L8("aClassUids.Count() = %d\n"), count); + for (TInt i = 0; i < count; i++) + { + CUsbClassControllerPlugIn* plugIn = CUsbClassControllerPlugIn::NewL(aClassUids[i], *this); + AddClassControllerL(reinterpret_cast(plugIn), order); + } +#endif // USE_DUMMY_CLASS_CONTROLLER + + LEAVEIFERRORL(iUsbClassControllerIterator->First()); + } + +void CUsbDevice::SetDefaultPersonalityL() +/** + * Sets default personality. Used for Start request. + */ + { + LOG_FUNC + + TInt smallestId = iSupportedPersonalities[0]->PersonalityId(); + TInt count = iSupportedPersonalities.Count(); + + for (TInt i = 1; i < count; i++) + { + if(iSupportedPersonalities[i]->PersonalityId() < smallestId) + { + smallestId = iSupportedPersonalities[i]->PersonalityId(); + } + } + + SetCurrentPersonalityL(smallestId); + SelectClassControllersL(); + } + +void CUsbDevice::LoadFallbackClassControllersL() +/** + * Load class controllers for fallback situation: + * no personalities are configured. + * This method inserts all class controllers to + * the list from which they will be either all started + * or stopped + */ + { + LOG_FUNC + SetDeviceDescriptorL(); + CreateClassControllersL(iSupportedClassUids); + } + +void CUsbDevice::ResourceFileNameL(TFileName& aFileName) +/** + * Gets resource file name + * + * @param aFileName Descriptor to populate with resource file name + */ + { + LOG_FUNC + + RFs fs; + LEAVEIFERRORL(fs.Connect()); + CleanupClosePushL(fs); + +#ifdef __WINS__ + // If we are running in the emulator then read the resource file from system drive. + // This makes testing with different resource files easier. + _LIT(KPrivatePath, ":\\Private\\101fe1db\\"); + aFileName.Append(RFs::GetSystemDriveChar()); //get the name of system drive + aFileName.Append(KPrivatePath); +#else + const TDriveNumber KResourceDrive = EDriveZ; + + TDriveUnit driveUnit(KResourceDrive); + TDriveName drive=driveUnit.Name(); + aFileName.Insert(0, drive); + // append private path + TPath privatePath; + fs.PrivatePath(privatePath); + aFileName.Append(privatePath); +#endif //WINS + + // Find the nearest match of resource file for the chosen locale + aFileName.Append(_L("usbman.rsc")); + BaflUtils::NearestLanguageFile(fs, aFileName); // if a match is not found, usbman.rsc will be used + + CleanupStack::PopAndDestroy(&fs); // fs no longer needed + } + +RDevUsbcClient& CUsbDevice::MuepoDoDevUsbcClient() +/** + * Inherited from MUsbmanExtensionPluginObserver - Function used by plugins to + * retrieve our handle to the LDD + * + * @return The LDD handle + */ + { + return iLdd; + } + +void CUsbDevice::MuepoDoRegisterStateObserverL(MUsbDeviceNotify& aObserver) +/** + * Inherited from MUsbmanExtensionPluginObserver - Function used by plugins to + * register themselves for notifications of device/service state changes. + * + * @param aObserver New Observer of the device + */ + { + LOGTEXT2(_L8("CUsbDevice::MuepoDoRegisterStateObserverL aObserver = 0x%08x"),&aObserver); + RegisterObserverL(aObserver); + }