diff -r a232af6b0b1f -r a5496987b1da kernel/eka/drivers/usbho/usbdescriptors/usbdescparser.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kernel/eka/drivers/usbho/usbdescriptors/usbdescparser.cpp Thu Jul 01 17:57:33 2010 +0100 @@ -0,0 +1,379 @@ +// Copyright (c) 2007-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. +// +// Description: +// Symbian USBDI Descriptor Parsing Framework. +// +// + +/** + @file + @internalComponent +*/ + +#include +#include "usbdescutils.h" + + +// --------------------- +// UsbDescriptorParser +// --------------------- + +/** +The main parsing function of the USB descriptor parsing framework. + +This will perform a best effort parse of a USB descriptor tree. It is best effort in the +fact that upon encountering a form of syntatic corruption in the source data it will error +the parse attempt, but also return the incomplete descriptor tree up to the parsing error. + +@param aUsbDes The source data that will be parsed. +@param aDesc The pointer that will be updated to the top-level descriptor. + +@return KErrNone if successful, a system-wide error code otherwise. + +@publishedPartner +@prototype +*/ +EXPORT_C /*static*/ TInt UsbDescriptorParser::Parse(const TDesC8& aUsbDes, TUsbGenericDescriptor*& aDesc) + { + TInt ret = KErrNone; + aDesc = NULL; + TPtrC8 des(aUsbDes); + + // First we must find the top level descriptor (the one we will return to the caller). + TRAP(ret, aDesc = FindParserAndParseAndCheckL(des, NULL)); + if(ret == KErrNone) + { + if(!aDesc) + { + ret = KErrNotFound; + } + else + { + // Now we have a top level descriptor - we now try to build up the descriptor + // tree if there are more descriptors available. + TRAP(ret, ParseDescriptorTreeL(des, *aDesc)); + } + } + + // Ensure that all the data has been parsed if successful. + if(ret == KErrNone && des.Length() > 0) + { + // If no parser was found for some data then we should have been errored with KErrNotFound. + __ASSERT_DEBUG(EFalse, UsbDescFault(UsbdiFaults::EUsbDescSuccessButDataLeftUnparsed)); + ret = KErrUnknown; + } + + // release the allocated descriptor if there was an error + if(ret != KErrNone && aDesc) + { + delete aDesc; + aDesc = NULL; + } + + return ret; + } + +/** +The function to register a custom parsing routine in the USB descriptor parser framework. + +The routine is registered locally to the current thread, and so if an application wishes +to perform the same custom parsing in multiple threads, it must call this function with +the appropriate routine in each thread context. + +If the custom routine becomes unapplicable after being registered, the application may +unregister it using the UsbDescriptorParser::UnregisterCustomParser function. +@see UsbDescriptorParser::UnregisterCustomParser + +@param aParserFunc The routine which will be added to the USB descriptor parsing framework. + +@publishedPartner +@prototype +*/ +EXPORT_C /*static*/ void UsbDescriptorParser::RegisterCustomParserL(TUsbDescriptorParserL aParserFunc) + { + TBool newlyCreatedList = EFalse; + CUsbCustomDescriptorParserList* parserList = static_cast(Dll::Tls()); + if(!parserList) + { + parserList = CUsbCustomDescriptorParserList::NewL(); + newlyCreatedList = ETrue; + CleanupStack::PushL(parserList); + } + + parserList->RegisterParserL(aParserFunc); + + if(newlyCreatedList) + { + Dll::SetTls(parserList); + CleanupStack::Pop(parserList); + } + } + +/** +The function to unregister a custom parsing routine in the USB descriptor parser framework. + +This routine will only unregister the routine from the current thread context. If the routine +is registered in multiple threads and it is no longer wanted in any thread, an application +must call this function in each thread context that the routine is registered. + +It is safe to call this function even if RegisterCustomParserL has never been called successfully. + +@see UsbDescriptorParser::RegisterCustomParserL + +@param aParserFunc The routine which will be removed from the USB descriptor parsing framework. + +@publishedPartner +@prototype +*/ +EXPORT_C /*static*/ void UsbDescriptorParser::UnregisterCustomParser(TUsbDescriptorParserL aParserFunc) + { + CUsbCustomDescriptorParserList* parserList = static_cast(Dll::Tls()); + if(parserList) + { + parserList->UnregisterParser(aParserFunc); + if(parserList->NumOfRegisteredParsers() <= 0) + { + Dll::FreeTls(); + delete parserList; + } + } + } + +/*static*/ TUsbGenericDescriptor* UsbDescriptorParser::FindParserAndParseAndCheckL(TPtrC8& aUsbDes, TUsbGenericDescriptor* aPreviousDesc) + { + TUsbGenericDescriptor* ret = FindParserAndParseL(aUsbDes, aPreviousDesc); + // We need to ensure that the parsers have correctly initialised the USB descriptor objects. + // It is important that we check as it is possible that a custom parser did the parsing. + __ASSERT_ALWAYS(!ret || (!ret->iParent && !ret->iFirstChild && !ret->iNextPeer), + UsbDescPanic(UsbdiPanics::EUsbDescNonNullPointersAfterParsing)); + return ret; + } + +// Utility macro to tidy up the parsing routine. +#define RETURN_IF_PARSEDL(aRet, aParserL, aUsbDes, aPreviousDesc)\ + {\ + aRet = aParserL(aUsbDes, aPreviousDesc);\ + if(aRet)\ + {\ + return aRet;\ + }\ + } + +/*static*/ TUsbGenericDescriptor* UsbDescriptorParser::FindParserAndParseL(TPtrC8& aUsbDes, TUsbGenericDescriptor* aPreviousDesc) + { + // Special termination case. + if(aUsbDes.Length() == 0) + { + return NULL; + } + + TUsbGenericDescriptor* des; + + // Try the default parsing routines. + RETURN_IF_PARSEDL(des, TUsbDeviceDescriptor::ParseL, aUsbDes, aPreviousDesc); + RETURN_IF_PARSEDL(des, TUsbDeviceQualifierDescriptor::ParseL, aUsbDes, aPreviousDesc); + RETURN_IF_PARSEDL(des, TUsbConfigurationDescriptor::ParseL, aUsbDes, aPreviousDesc); + RETURN_IF_PARSEDL(des, TUsbOtherSpeedDescriptor::ParseL, aUsbDes, aPreviousDesc); + RETURN_IF_PARSEDL(des, TUsbInterfaceAssociationDescriptor::ParseL, aUsbDes, aPreviousDesc); + RETURN_IF_PARSEDL(des, TUsbInterfaceDescriptor::ParseL, aUsbDes, aPreviousDesc); + RETURN_IF_PARSEDL(des, TUsbEndpointDescriptor::ParseL, aUsbDes, aPreviousDesc); + RETURN_IF_PARSEDL(des, TUsbOTGDescriptor::ParseL, aUsbDes, aPreviousDesc); + RETURN_IF_PARSEDL(des, TUsbStringDescriptor::ParseL, aUsbDes, aPreviousDesc); + + // Then we try the custom parsers that have been registered. + const CUsbCustomDescriptorParserList* parserList = static_cast(Dll::Tls()); + if(parserList) + { + TInt numOfParsers = parserList->NumOfRegisteredParsers()-1; + for(TInt index=0; indexRegisteredParser(index); + RETURN_IF_PARSEDL(des, parserL, aUsbDes, aPreviousDesc); + } + } + + // Then we try the unknown descriptor parser. + RETURN_IF_PARSEDL(des, UnknownUsbDescriptorParserL, aUsbDes, aPreviousDesc); + + // Otherwise we haven't found anybody to parse the binary data. + User::Leave(KErrNotFound); // inform caller that there is no parser for the data. + return NULL; + } + +/*static*/ void UsbDescriptorParser::ParseDescriptorTreeL(TPtrC8& aUsbDes, TUsbGenericDescriptor& aPreviousDesc) + { + TUsbGenericDescriptor* desc = &aPreviousDesc; + while(desc) + { + TUsbGenericDescriptor* preDesc = desc; + desc = FindParserAndParseAndCheckL(aUsbDes, desc); + if(desc) + { + CleanupStack::PushL(desc); + BuildTreeL(*desc, *preDesc); + CleanupStack::Pop(desc); + } + } + } + +/*static*/ void UsbDescriptorParser::BuildTreeL(TUsbGenericDescriptor& aNewDesc, TUsbGenericDescriptor& aPreviousDesc) + { + // We assume that the new descriptor has been properly initialised with NULL pointers. + __ASSERT_DEBUG(!aNewDesc.iFirstChild && !aNewDesc.iNextPeer && !aNewDesc.iParent, + UsbDescFault(UsbdiFaults::EUsbDescTreePointersAlreadySet)); + + // Find first "top" parent claiming this new descriptor as a child. + TUsbGenericDescriptor* parent = &aPreviousDesc; + TUsbGenericDescriptor* topLevel = &aPreviousDesc; + while(parent) + { + if(aNewDesc.IsParent(*parent) || parent->IsChild(aNewDesc)) + { + break; // we have found a parent. + } + topLevel = parent; // Save the current one for use if we cannot find a parent + parent = parent->iParent; // Scroll back up the tree. + } + __ASSERT_DEBUG(topLevel, UsbDescFault(UsbdiFaults::EUsbDescNoTopLevelDescriptorFound)); + + if(parent) + { + // We should be able to place the descriptor directly as a child of this descriptor, + // however it is not that simple because of IADs (Interface Association Descriptors). + // The ECN states "All of the interface numbers in the set of associated interfaces must be + // contiguous" meaning that if an IAD has two interfaces starting at 1 then the configuration + // bundle may have interface descriptors in '1 then 3 then 2' order. As such we need to be able + // to go backwards to find the most suitable binding. The general way for doing this is to + // find the right-most, lowest descriptor that descriptor considers a parent. + // Where the tree is arranged with peers horizontally linked left to + // right, with children linked vertically top to bottom. + TUsbGenericDescriptor& suitableParent = FindSuitableParentL(aNewDesc, *parent); + + TUsbGenericDescriptor* peer = suitableParent.iFirstChild; + if(peer) + { + TUsbGenericDescriptor* lastPeer; + do + { + lastPeer = peer; + peer = peer->iNextPeer; + } + while(peer); + lastPeer->iNextPeer = &aNewDesc; + } + else + { + // we are the first child so just update. + suitableParent.iFirstChild = &aNewDesc; + } + aNewDesc.iParent = &suitableParent; + } + else if(aNewDesc.IsPeer(*topLevel) || topLevel->IsPeer(aNewDesc)) + { + // There is no explicit parent in the tree so, we may just have a group of top-level peers + // in the bundle. If the previous descriptor is a peer then we shall just tag on its tier. + TUsbGenericDescriptor* lastPeer; + TUsbGenericDescriptor* peer = topLevel; + do + { + lastPeer = peer; + peer = peer->iNextPeer; + } + while(peer); + lastPeer->iNextPeer = &aNewDesc; + } + else + { + // The descriptor could not be bound into the tree, indicating that the bundle of descriptors + // is unvalid. + User::Leave(KErrUsbBadDescriptorTopology); + } + } + +/*static*/ TUsbGenericDescriptor& UsbDescriptorParser::FindSuitableParentL(TUsbGenericDescriptor& aNewDesc, TUsbGenericDescriptor& aTopParent) + { + // This implements the algorithm to search down from the top parent found in the tree to the right most, lowest descriptor + // that will accept the new descriptor as a child. + + TUsbGenericDescriptor* bestMatch = &aTopParent; + + TUsbGenericDescriptor* desc = aTopParent.iFirstChild; + if(desc) + { + // Do a depth first search. + FOREVER + { + // First see if the descriptor is suitable. + __ASSERT_DEBUG(desc, UsbDescFault(UsbdiFaults::EUsbDescRunOffTree)); + if(aNewDesc.IsParent(*desc) || desc->IsChild(aNewDesc)) + { + bestMatch = desc; + } + // Now walk to the next point in the tree. + if(desc->iFirstChild) + { + desc = desc->iFirstChild; + } + else if(desc->iNextPeer) + { + desc = desc->iNextPeer; + } + else + { + // We've run to the end of a bottom tier, so go back up. + do + { + __ASSERT_DEBUG(desc->iParent, UsbDescFault(UsbdiFaults::EUsbDescTreeMemberHasNoParent)); + desc = desc->iParent; + } + while(!desc->iNextPeer && desc != &aTopParent); + if(desc == &aTopParent) + { + // This means that we must have got back to the original + // parent. So we don't do any more. + break; + } + desc = desc->iNextPeer; + } + } + } + return *bestMatch; + } + +/*static*/ TUsbGenericDescriptor* UsbDescriptorParser::UnknownUsbDescriptorParserL(TPtrC8& aUsbDes, TUsbGenericDescriptor* /*aPreviousDesc*/) + { + TUsbGenericDescriptor* unknownDes = NULL; + + const TInt KMinUnknownDesLength = 2; // Length and type fields + if( aUsbDes.Length() >= KMinUnknownDesLength) + { + // We require unknown descriptors to have at least the length and type fields. + // Any more exotic descriptors should have a custom parser for the framework to use. + TUint8 unknownDesLen = aUsbDes[TUsbGenericDescriptor::KbLengthOffset]; + + // Robustness check - check the length field is valid. + if(aUsbDes.Length() < unknownDesLen || unknownDesLen < KMinUnknownDesLength) + { + User::Leave(KErrCorrupt); + } + + unknownDes = new(ELeave) TUsbGenericDescriptor; + // Set the standard fields + unknownDes->ibLength = unknownDesLen; + unknownDes->ibDescriptorType = aUsbDes[TUsbGenericDescriptor::KbDescriptorTypeOffset] ; + // Set the blob appropriately + unknownDes->iBlob.Set(aUsbDes.Left(unknownDesLen)); + // Update the data-left-to-parse Symbian descriptor + aUsbDes.Set(aUsbDes.Mid(unknownDesLen)); + } + + return unknownDes; + }