USB Device Desctiptor 相关

前面有篇文章说到如何获取U盘描述符,最终我们读取描述符的时候是通过以下语句实现的:

lpudd[i] = m_ppCDeviceOnPort[i]->m_deviceInfo.Descriptor;

将驱动中保存的Desctiptor信息,赋值到传入的buffer中。因此,引出来一个问题,驱动中保存的Descriptor信息是什么时候产生的?宏观上来讲,应该是USB Device插入的时候,驱动从USB Device读取相关信息,并最终生成Descriptor。但具体是怎么实现的呢?今天就来分析一下这个过程。

 

首要的工作当然还是顺藤摸瓜,一步步找到入口。一步步跟踪下来,发现最初的启动点上函数OTG_Init。这次采用自上而下的方式来分析该过程。

 

1、OTG_Init函数中调用函数HCD_Init来初始化host controller。

g_OtgCoreInfo.m_dwHostContext = HCD_Init((DWORD)&hostSettings);

 

2HCD_Init函数中,首先从输入参数host setting中获取中断线程优先级,并保存在全局变量中。接下来,调用函数HcdPdd_Init。

POTG_HOST_SETTINGS pHostSettings = (POTG_HOST_SETTINGS)dwContext;

...

g_IstThreadPriority = pHostSettings->m_dwISTPriority;

...

g_dwPddContext =HcdPdd_Init(dwContext);

...

return g_dwPddContext;

可见,通过调用函数HcdPdd_Init获取PDD上下文环境,在本层中保存一份到全局变量,同时返回给上一层。

 

3、HcdPdd_Init函数中首先创建一个SEHCDPdd结构体对象。然后以该对象的指针,和host setting指针为参数,调用函数InitializeEHCI。函数InitializeEHCI的实现在本层中。

SEHCDPdd * pPddObject = malloc(sizeof(SEHCDPdd));

...

fRet = InitializeEHCI(pPddObject, (POTG_HOST_SETTINGS)dwContext);

...

return (DWORD)pPddObject;

 

4、InitializeEHCI函数中,主要初始化SEHCDPdd结构体对象的各个成员。这次来看看各个成员的初始过程吧,逃避解决不了问题,好歹先混个脸熟。

// 指定HCI driver使用的共享内存的大小,以及其中高优先级内存的大小。

// MSDN的HcdMdd_CreateMemoryObject函数的说明中,建议共享内存的大小为64KB,高优先级内存的大小为20KB。若

// 高优先级内存小于20KB,可能产生由于内存分配失败导致的同步传输错误。低优先级内存小于44KB,不会产生同步传输错误

// 不过,可能会降低性能。

pPddObject->dwPhysicalMemSize = gcTotalAvailablePhysicalMemory;

dwHPPhysicalMemSize = gcHighPriorityPhysicalMemory;

 

// 初始化DMA Adapter的属性

pPddObject->AdapterObject.ObjectSize = sizeof(DMA_ADAPTER_OBJECT);

pPddObject->AdapterObject.InterfaceType = PCIBus;

pPddObject->AdapterObject.BusNumber = 0;

 

// 分配DMA用的共享buffer

// Allocates a shared buffer (physically contiguous pages, locked) and returns

// the virtual address (to be used by the DMA device driver) and the logical

// address (to be used by the DMA adapter) for DMA operations.

pPddObject->pvVirtualAddress =  // 共DMA device driver使用的虚拟地址

HalAllocateCommonBuffer(

&pPddObject->AdapterObject, // Pointer to DMA adapter descriptor. pPddObject->dwPhysicalMemSize, // Size of buffer to allocate.

&pPddObject->LogicalAddress, // Pointer to logical (bus-relative) address buffer

FALSE // Flag to choose cached or uncached buffer allocation.

)

 

// 创建host controller interface使用的共享内存

// HcdMdd_CreateMemoryObject函数具体介绍请参考msdn

pobMem = // host controller driver memory object

HcdMdd_CreateMemoryObject(

pPddObject->dwPhysicalMemSize, // Total size of the host controller device shared memory area.

dwHPPhysicalMemSize, // Specifies the bytes reserved for high-priority isochronous or interrupt      // transfers and the host controller interface (HCI) communication area.

  (PUCHAR) pPddObject->pvVirtualAddress, // Pointer to the virtual address of a memory area, which   // might be NULL.

  (PUCHAR) pPddObject->LogicalAddress.LowPart // Pointer to the physical address of a memory area,     // which might be NULL.

)

 

// 接下来到我们这次分析的重点了

// 创建并初始化host controller interface driver对象

pobEHCD = HcdMdd_CreateHcdObject(pPddObject, pobMem, NULL, (PUCHAR)pHostSetting->m_dwRegBase, 0)

 

// 将创建的对象赋值到成员变量中

pPddObject->lpvMemoryObject = pobMem;

pPddObject->lpvEHCDMddObject = pobEHCD;

 

// 其他成员初始化

pPddObject->ioPortBase = (PUCHAR)pHostSetting->m_dwRegBase;

pPddObject->dwSysIntr = 0;

HcdMdd_SetCapability(pobEHCD,pHostSetting->m_dwCapability);

 

5、HcdMdd_CreateHcdObject函数中首先调用CreateHCDObject函数创建host controller interface driver对象。然后调用host controller interface driver对象的DeviceInitialize函数初始化该对象。

CHcd * pobUhcd =

CreateHCDObject(lpvUhcdPddObject,(CPhysMem *)lpvMemoryObject,szRegKey,ioPortBase,dwSysIntr);

...

pobUhcd->DeviceInitialize( )

...

return pobUhcd;

 

6CreateHCDObject函数中,直接创建一个CEhcd对象并返回。

 

7、DeviceInitialize函数的注释如下:

// Set up the Host Controller hardware, associated data structures,

// and threads so that schedule processing can begin.

 

// Re初始化内存

m_pMem ->ReInit();

 

// 调用父类初始函数

BOOL fCDeviceInitOK = CDeviceGlobal::Initialize(this);

BOOL fCHWInitOK = CHW::Initialize( );

 

// 设置root hub的相关属性,set up the root hub object

...

// 设置CEhcd对象的root hub

SetRootHub( new CRootHub( deviceInfo, FALSE,TRUE, usbHubDescriptor,this ));

 

// Signal root hub to start processing port changes // The root hub doesn't have any pipes, so we pass NULL as the // endpoint0 pipe

GetRootHub()->EnterOperationalState( NULL )

 

8、函数CRootHub::EnterOperationalState中创建event, device array,线程。

// 创建hub status change event

// m_hHubStatusChangeEvent - Auto Reset, and Initial State = non-signaled

m_hHubStatusChangeEvent = CreateEvent( NULL, FALSE, FALSE, NULL );

 

// Allocate memory for the device array of this hub based on

// the number of ports given in the m_usbHubDescriptor structure

AllocateDeviceArray()

 

// 创建线程,并设置线程优先级

m_hHubStatusChangeThread = CreateThread( 0, 0, HubStatusChangeThreadStub, this, 0, NULL );

CeSetThreadPriority( m_hHubStatusChangeThread, g_IstThreadPriority);

 

此处说明一下,上面列出的EnterOperationalState函数的实现,是属于类CRootHub的。类CExternalHub中也有函数EnterOperationalState的实现。类的继承关系:

 

//                  CDevice (ADT)

//                /               /

//            CFunction        CHub (ADT)

//                            /             /

//                        CRootHub   CExternalHub

 

 

9、函数CHub::HubStatusChangeThreadStub中将传入的指针转化为CHub类的指针,并调用其HubStatusChangeThread函数。

return ((CHub*)context)->HubStatusChangeThread();

 

10、HubStatusChangeThread函数,处理hub上port状态的改变。

// before we can process port changes, we need

// to power all ports

// 跟过去发现,函数CRootHub::PowerAllHubPorts中除了打了两句log,返回了个TRUE,其他什么也没干。

// 不过,函数CExternalHub::PowerAllHubPorts中还是有具体动作的。

fSuccess = PowerAllHubPorts();

 

// 延迟

Sleep( 100 + 2 * m_usbHubDescriptor.bPowerOnToPowerGood );

 

// Set or clear a remove wakeup feature External HUB.

// 从注释可见,该函数主要是针对External HUB的。root hub中该函数为空。

SetOrClearRemoteWakup(TRUE);

 

...

// 本来想把该Thread函数中的每条语句仔细阅读一遍,不过发现这不是短时间可以完成的。

// 此处先不细看了,留个作业,改天再细细品味

 

// 等待port状态改变事件

fSuccess = WaitForPortStatusChange( port, hubStatus );

 

// 判断是否是连接状态改变

if ( hubStatus.change.port.ConnectStatusChange )

 

// Reset port and get speed infomation.

ResetAndEnablePort( port );

Sleep(20);

GetStatus(port , hubStatus);

 

// 判断Port是否连接

if ( hubStatus.status.port.PortConnected )

 

//调用函数AttachDevice

AttachDevice( port, hubStatus.status.port.DeviceIsLowSpeed, m_fIsHighSpeed?hubStatus.status.port.DeviceIsHighSpeed:FALSE );

 

11AttachDevice函数中,根据当前的状态,判断下一步该作什么操作,逐步完成device 的attach操作。

// 在状态DEVICE_CONFIG_STATUS_SCHEDULING_SET_CONFIG的时候,会去判断连接的设备是hub还是function。

if ( deviceInfo.Descriptor.bDeviceClass == USB_DEVICE_CLASS_HUB )

 

// 如果是hub,会创建一个CExternalHub对象

pNewDevice = new CExternalHub( address, deviceInfo, fIsLowSpeed,fIsHighSpeed, m_tierNumber + 1, usbHubDescriptor, m_pCHcd, this,port);

 

// 如果是Function,会创建一个Function对象

pNewDevice = new CFunction( address, deviceInfo, fIsLowSpeed,fIsHighSpeed, m_tierNumber + 1, m_pCHcd, this,port);

 

// 将新创建的对象对象追加到DeviceOnPort对象列表

m_ppCDeviceOnPort[ port - 1 ] = pNewDevice;

 

// 然后调用该对象的EnterOperationalState函数。

pNewDevice->EnterOperationalState( pControlPipe )

 

在创建hub或者Function对象的时候,构造函数中有一个参数deviceInfo。在父类CDevice的构造函数中,该参数被赋值给了成员变量m_deviceInfo(USB_DEVICE_INFO)。结构体USB_DEVICE_INFO中有一个成员Descriptor,记述的就是设备的描述符。

在函数AttachDevice函数中,状态为DEVICE_CONFIG_STATUS_SCHEDULING_GET_DEVICE_DESCRIPTOR的时候,会调用函数GetDescriptor获取设备的描述符。

函数GetDescriptor中通过调用函数CPipeAbs::IssueTransfer来获取描述符。

status = pControlPipe->IssueTransfer( address, // address of device TransferDoneCallbackSetEvent, // callback routine m_hHubStatusChangeEvent, // callback param USB_IN_TRANSFER | USB_SEND_TO_DEVICE, // transfer flags &usbRequest, // control request 0, // dwStartingFrame (not used) 0, // dwFrames (not used) NULL, // aLengths (not used) wDescriptorSize, // buffer size pBuffer, // buffer 0, // phys addr of buffer (not used) this, // cancel ID NULL, // adwIsochErrors (not used) NULL, // adwIsochLengths (not used) &m_fTransferDone, // OUT status param &m_dwBytesTransferred, // OUT status param &m_dwErrorFlags ); // OUT status param


CPipeAbs的对象,在函数AttachDevice中状态为DEVICE_CONFIG_STATUS_OPENING_ENDPOINT0_PIPE时被创建。

pControlPipe = CreateControlPipe( &usbEndpointZeroDescriptor, fIsLowSpeed, fIsHighSpeed ,0, uTTHubAddr,uTTHubPort, m_pCHcd);

函数CControlPipe创建一个CControlPipe对象。

return new CControlPipe(lpEndpointDescriptor,fIsLowSpeed,fIsHighSpeed,bDeviceAddress,

bHubAddress,bHubPort,(CEhcd * const)pChcd);

Pipe相关类的继承关系:

//   CPipe (ADT) // /              / // CQueuedPipe (ADT) CIsochronousPipe // /            |        / // /             |        / // CControlPipe CInterruptPipe CBulkPipe

 

函数CControlPipe::IssueTransfer函数中,调用函数CQueuedPipe::IssueTransfer来实现具体功能。

HCD_REQUEST_STATUS status = CQueuedPipe::IssueTransfer( address, lpStartAddress,lpvNotifyParameter, dwFlags,lpvControlHeader, dwStartingFrame, dwFrames, aLengths, dwBufferSize, lpvClientBuffer, paBuffer, lpvCancelId, adwIsochErrors, adwIsochLengths, lpfComplete, lpdwBytesTransferred, lpdwError );

函数CQueuedPipe::IssueTransfer中创建一个CQTransfer对象,并将其加到m_pUnQueuedTransfer队列中。接下来调用函数ScheduleTransfer。

函数CQueuedPipe::ScheduleTransfer中设置m_pQueuedTransfer。从m_pUnQueuedTransfer队列中取出第一个,赋值给m_pQueuedTransfer。然后Queue Transfer。

// 调用函数GetQHead获取m_pPipeQHead。

GetQHead()

// m_pPipeQHead在CControlPipe::OpenPipe函数中被创建

m_pPipeQHead = new( m_pCEhcd->GetPhysMem()) CQH (this);

// 接下来调用函数CQH::QueueTD

GetQHead()->QueueTD(pCurTransfer->GetCQTDList())

函数CQH::QueueTD中存在以下语句:

// This will activate the TD trasnfer.

nextQTDPointer.dwLinkPointer =pCurTD->GetPhysAddr();

函数CQueuedPipe::ScheduleTransfer中若不能Queue Transfer,则调用函数CQueuedPipe::CheckForDoneTransfers检查传输是否完成。

函数CQueuedPipe::CheckForDoneTransfers中调用函数CQTransfer::DoneTransfer。

函数CQTransfer::DoneTransfer中回调函数m_sTransfer.lpStartAddress

( *m_sTransfer.lpStartAddress )(m_sTransfer.lpvNotifyParameter );

追溯可以发生,m_sTransfer.lpStartAddress指向的是函数CDevice::TransferDoneCallbackSetEvent。其实现如下:

SetEvent( (HANDLE) context );

此处set的event,是函数GetDescriptor调用函数CPipeAbs::IssueTransfer时传入的参数m_hHubStatusChangeEvent。

函数CRootHub::WaitForPortStatusChange中有对事件m_hHubStatusChangeEvent的等待。


posted @ 2011-04-01 18:19  andriod2012  阅读(569)  评论(0编辑  收藏  举报