wince usb驱动中的CHub::AttachDevice函数

wince usb驱动中的CHub::AttachDevice函数

函数CHub::AttachDevice在HubStatusChangeThread中被调用。当有设备插入的时候,该函数被调用。函数的注释如下:

// This function is called when a new device is attached

// on port "port". After this procedure finishes, a configured

// device will be added to the hub's port array

该函数中,通过一个循环,判断当前的状态(configStatus),然后执行相应的操作。

状态为一个枚举类型DEVICE_CONFIG_STATUS。

0、初始化当前状态(configStatus)为DEVICE_CONFIG_STATUS_OPENING_ENDPOINT0_PIPE。

1、调用函数ReserveAddress,获取一个未使用的address。address通过一个DWORD数组(4个成员)来管理,每个bit表示一个address,共128个address。其中,address 0是指定被root hub使用的。

ReserveAddress( address )

如果获取失败,说明所有的address都被使用。则将状态(configStatus)设置为DEVICE_CONFIG_STATUS_DONE,不对插入的设备进行识别。

2、进入主循环,逐步进行操作,直到Attach device成功,或者遇到错误,attach 失败。

while ( configStatus != DEVICE_CONFIG_STATUS_DONE )

3、首先,判断线程结束标志(m_fHubThreadClosing),Pipe Halt标志(fPipeHalted,Control Pipe),配置失败计数(configFailures)。

if ( m_fHubThreadClosing || fPipeHalted || configFailures > CONFIG_FAILURE_NUM )

{

configStatus = DEVICE_CONFIG_STATUS_FAILED;

}

4、用switch语句,检查当前状态,然后进行相应的操作。

switch ( configStatus )

5、如果状态为DEVICE_CONFIG_STATUS_OPENING_ENDPOINT0_PIPE:

5.1 首先判断是否需要创建TT。

如果当前的hub是high speed,而插入的设备不是high speed,并且在当前hub对象中,插入设备对应的port口上没有TT对象,则创建一个TT对象。

if (m_fIsHighSpeed && !fIsHighSpeed && !m_pAddedTT[port-1]) { // Hi-Speed Hub and Low Speed device.

m_pAddedTT[port-1] = m_pDeviceGlobal->AddedTt(m_address,port);

}

AddedTt函数的实现在类CEhcd中。类间的继承关系如下:

CDeviceGlobal LockObject

/ / /

CHcd USB2lib

/ /

CHW /

/ /

CEhcd

virtual BOOL AddedTt( UCHAR uHubAddress,UCHAR uPort) { return USB2lib::AddedTt( uHubAddress,uPort); };

函数USB2lib::AddedTt中调用函数GetTT尝试获取TT,如果获取到的为空,则创建TT对象。然后将Root TT的指针(pTTRoot)指向新创建的对象。

if (GetTT( uHubAddress,uPort) == NULL) {

TransactionTrasnlate * pNewTT = new TransactionTrasnlate(uHubAddress,uPort, pTTRoot);

if ( pNewTT) {

pTTRoot=pNewTT;

bReturn=TRUE;

}

函数USB2lib::GetTT中遍历TT列表,看是否存在address和port都匹配的TT对象。

TransactionTrasnlate * pFoundTT = pTTRoot;

while (pFoundTT) {

if (pFoundTT->GetHubAddress() == uHubAddress && pFoundTT->GetHubPort() == uHubPort)

break;

else

pFoundTT = pFoundTT->GetNextTT();

}

TransactionTrasnlate的构造函数中,将传入的root TT指针,赋值给新对象的Next TT指针。

可以,驱动中,是同过一个链表来管理TT对象的。新创建的TT对象放在List头部,并用Root TT指针指向头部的TT对象。若要对TT对象进行操作,从Root TT指针中获取头部的TT对象,然后查找到相应的TT对象,进行操作即可。

5.2、定义一个USB_ENDPOINT_DESCRIPTOR对象,并初始化其各个成员。

5.3、调用函数CDevice::GetUSB2TT,沿着hub向上,寻找TT对象。

函数CDevice::GetUSB2TT中,首先判断当前设备是否为high speed。

若当前设备为high speed,则不用找了,TT对象就在当前的hub中,即为步骤5.1中创建的TT对象。返回NULL。

若当前hub不为high speed,则沿着当前hub依次往上查找,直到找到一个high speed的hub。则TT对象就在那个high speed的hub中。

CHub * pHub = m_pAttachedHub;

UCHAR sAttachedPort = m_sAttachedPort;

while (pHub!=NULL && pHub->m_fIsHighSpeed!=TRUE) {

sAttachedPort = pHub->m_sAttachedPort;

pHub = pHub->m_pAttachedHub;

}

取出TT对象中标识TT对象的address和port。并返回TT对象所在的hub。

if (pHub) {

if (pTTAddr)

*pTTAddr = pHub->m_address;

if (pTTPort)

*pTTPort = sAttachedPort;

}

return pHub;

5.4、创建Control Pipe。通过调用全局函数CreateControlPipe来实现。

pControlPipe = CreateControlPipe( &usbEndpointZeroDescriptor,

fIsLowSpeed, fIsHighSpeed ,0,

uTTHubAddr,uTTHubPort,

m_pCHcd);

从代码中可以看出,在创建Control Pipe对象时,除了传入EndPoint 0的Descriptor信息之外,还传入了uTTHubAddr,uTTHubPort,m_pCHcd。

其中,m_pCHcd是Root Hub中的m_pCHcd,也就是说,所有的Hub共用一个Hcd对象。该Hcd对象是在Root Hub对象创建时,作为参数传入的。Root Hub的对象在函数CEhcd::DeviceInitialize中创建。传入的Hcd对象,是this指针。

uTTHubAddr和uTTHubPort是通过步骤5.3调用函数CDevice::GetUSB2TT来获取的。可见,当有设备插入时,就会创建一个Control Pipe。该Control Pipe对应的TTHubAddr和TTHubPort,可以分以下三种情况:

1、当前插入的设备是High Speed的:TTHubAddr为当前Hub的Address,TTHubPort为当前插入设备对应的Port。

2、当前插入的设备不High Speed,但当前的Hub为High Speed:TTHubAddr为当前Hub的Address,TTHubPort为当前插入设备对应的Port。

3、当前插入的设备和当前的Hub都不是High Speed:TTHubAddr和TTHubPort为通过调用函数CDevice::GetUSB2TT获取的Address和Port。即为从当前的Hub依次往Root Hub的方向查找,查找到的第一个High Speed Hub的Address和对应的Port。

函数CreateControlPipe的实现在文件cpipe.cpp中。直接创建一个CControlPipe对象并返回。

return new CControlPipe(lpEndpointDescriptor,fIsLowSpeed,fIsHighSpeed,bDeviceAddress,bHubAddress,bHubPort,(CEhcd * const)pChcd);

CControlPipe的构造函数中,首先调用了CQueuedPipe的构造函数,然后打了些MSG,并做了些Check工作。

Pipe类间的继承关系如下:

// CPipe (ADT)

// / /

// CQueuedPipe (ADT) CIsochronousPipe

// / | /

// / | /

// CControlPipe CInterruptPipe CBulkPipe

5.5、调用Control Pipe对象的OpenPipe函数。

函数中,开始是一些检查操作。

接下来判断m_pPipeQHead是否为空,若非空,不进行操作。

若为空:

创建CQH对象:

// 刚看到这句代码有点迷糊,后来查了些关于new的资料。不明白的同学可以参考下我转过来的介绍new的文章。

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

函数GetPhysMem的实现在类CHW中实现:

CPhysMem * GetPhysMem() { return m_pMem; };

请参考前文中的HCD类间的继承关系。

可见,只是将成员m_pMem返回。该成员在CEHcd对象被创建时初始化。

跟踪发现,CEhcd对象在文件CeHcd.cpp文件中的全局函数CreateHCDObject中被创建:

return new CEhcd (pvUhcdPddObject, pCPhysMem,szDriverRegistryKey,portBase,dwSysIntr);

函数CreateHCDObject在文件Hcddrv.cpp文件中的全局函数HcdMdd_CreateHcdObject中被调用:

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

函数HcdMdd_CreateHcdObject在文件System.c文件中的全局函数InitializeEHCI中被调用:

pobEHCD = HcdMdd_CreateHcdObject(pPddObject, pobMem, NULL,

(PUCHAR)pHostSetting->m_dwRegBase, 0)

其中的参数pobMem也是在函数InitializeEHCI中被创建:

pobMem = HcdMdd_CreateMemoryObject(pPddObject->dwPhysicalMemSize, dwHPPhysicalMemSize,

(PUCHAR) pPddObject->pvVirtualAddress,

(PUCHAR) pPddObject->LogicalAddress.LowPart)

函数InitializeEHCI在文件System.c文件中的全局函数HcdPdd_Init中被调用:

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

函数HcdPdd_Init在文件Hcddrv.cpp文件中的全局函数HCD_Init中被调用:

g_dwPddContext =HcdPdd_Init(dwContext);

函数HCD_Init在文件Otgdrv.cpp中的全局函数OTG_Init中被调用:

// Init host controller

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

类CQH继承CNextLinkPointer和QH。QH其实是一个结构体,类CQH中增加了对该结构体的操作函数。类CNextLinkPointer中其实是包含了一个NextLinkPointer结构体类型的成员,以及对该结构体的操作函数。

如果创建CQH对象成功,调用CQH对象的SetDTC函数:

m_pPipeQHead->SetDTC(TRUE); // Self Data Toggle for Control

函数SetDTC中改变了成员qH_StaticEndptState的属性,qH_StaticEndptState是结构体QH的成员。

void SetDTC(BOOL bSet) {CheckStructure (); qH_StaticEndptState.qH_SESContext.DTC = (bSet?1:0); };

如果成员变量m_fIsHighSpeed为FALSE,调用函数SetControlEnpt:

m_pPipeQHead->SetControlEnpt(TRUE) ;

函数SetControlEnpt的实现:

void SetControlEnpt(BOOL bSet) { CheckStructure ();qH_StaticEndptState.qH_SESContext.C = (bSet?1:0);};

成员变量m_pPipeQHead在Control Pipe对象对象创建时被初始化。跟踪发现,其表示当前插入的设备是否是High Speed。

调用函数CHW::AsyncQueueQH。

m_pCEhcd->AsyncQueueQH( m_pPipeQHead )

函数CHW::AsyncQueueQH的实现:

CQH * AsyncQueueQH(CQH * pQHead) { return m_cAsyncMgr.QueueQH(pQHead); };

m_cAsyncMgr中CHW的构造函数中被初始化(初始化的值与m_pMem一样:m_pMem = pCPhysMem;):

m_cAsyncMgr(pCPhysMem)

跟过去发现,类CAsyncMgr的构造函数中只有一个CPhysMem类型的参数。所以,此处应该是以pCPhysMem为参数构造了一个CAsyncMgr类的对象。

函数CAsyncMgr::QueueQH的实现主体如下:

pQHead->QueueQHead( m_pStaticQHead ->GetNextQueueQHead(m_pCPhysMem));

m_pStaticQHead ->QueueQHead(pQHead);

函数CQH::QueueQHead的实现:

m_pNextQHead = pNextQH;

return SetNextPointer(pNextQH->GetPhysAddr(), TYPE_SELECT_QH, TRUE);

函数SetNextPointer的实现在类CNextLinkPointer中。首先定义一个CNextLinkPointer对象,并设置其参数。最后获取CNextLinkPointer对象的LinkPointer赋值给成员变量nextLinkPointer的dwLinkPointer成员。

CNextLinkPointer cNext;

cNext.SetLinkValid(bValid);

cNext.SetLinkType(nLinkType);

cNext.SetPointer(dwPhysAddr);

nextLinkPointer.dwLinkPointer = cNext.GetDWORD();

函数CQH::GetPhysAddr返回了成员m_dwPhys:

DWORD GetPhysAddr() { CheckStructure ();return m_dwPhys; };

类CQH的成员变量m_dwPhys在其构造函数中被初始化:

m_dwPhys = (pPipe->GetCPhysMem())-> VaToPa((PBYTE)this);

pPipe是构造函数的参数,即前面创建CQH对象时,传入的Control Pipe对象的this指针。

函数GetCPhysMem的实现在类CPipe中:

return m_pCEhcd->GetPhysMem();

关于函数m_pCEhcd->GetPhysMem(),前文已介绍过,此处不再重复。

函数CPhysMem::VaToPa的功能是将虚拟地址转换成物理地址,实现如下:

return ULONG(ULONG(virtAddr) + m_PaVaConversion);

类CAsyncMgr的成员变量m_pStaticQHead的初始化在函数CAsyncMgr::Init中:

m_pStaticQHead = new (m_pCPhysMem) CQH(m_pCDumpPipe);

if (m_pStaticQHead) {

m_pStaticQHead->SetReclamationFlag(TRUE);

m_pStaticQHead ->QueueQHead(m_pStaticQHead); // Point to itself.

Unlock();

return TRUE;

}

可见,此处创建了一个CQH对象,然后将其next link pointer指向了自己。

函数CQH::GetNextQueueQHead的实现调用了其父类CNextLinkPointer的函数GetNextLinkPointer。

return (CQH *) GetNextLinkPointer(pCPhysMem);

函数CNextLinkPointer::GetNextLinkPointer的实现如下:

return (CNextLinkPointer * )pPhysMem->PaToVa(GetPointer());

函数CPhysMem::PaToVa的功能是将物理地址转换成虚拟地址,实现如下:

return PUCHAR(physAddr - m_PaVaConversion);

函数CNextLinkPointer::GetPointer返回了成员变量next link pointer的link pointer:

DWORD GetPointer() { return ( nextLinkPointer.lpContext.LinkPointer <<5); };

函数CAsyncMgr::Init在函数CHW::Initialize中被调用。

函数CEhcd::DeviceInitialize中调用了函数CHW::Initialize。

函数CEhcd::DeviceInitialize在文件Hcddrv.cpp文件中的全局函数HcdMdd_CreateHcdObject中被调用。

函数HcdMdd_CreateHcdObject首先调用函数CreateHCDObject创建了一个CHcd对象,然后调用其DeviceInitialize函数。

可见,对CQH对象的管理可以总结如下:

1、在Device initialize的时候,创建一个CQH对象(作为头CQH),并将其next link pointer指向自身。

2、当有设备插入时,创建一个新的CQH对象,将该对象的next link pointer指向头CQH对象的next link pointer指向的对象。

3、将头CQH对象的next link pointer指向新的CQH对象。

也就是通过一个单向环形链表来管理CQH对象。开始只有头CQH对象指向其本身。当有设备插入时,将新的CQH对象插入到头CQH对象后面。

6、如果状态为DEVICE_CONFIG_STATUS_USING_ADDRESS0:

首先检查address 0是否在使用。

然后设置address 0使用标志:

fUsingAddr0 = TRUE;

调用函数CDeviceGlobal::Addr0LockEntry:

m_pDeviceGlobal->Addr0LockEntry(INFINITE)

成员变量m_pDeviceGlobal在父类CDevice中被初始化。其初始化值其实是CEhcd对象的指针。

函数函数CDeviceGlobal::Addr0LockEntry的实现如下:

CritSec_Status Addr0LockEntry(ULONG ulTimeout) { return m_csAddress0Lock.EnterCritSec_Ex(ulTimeout); };

成员变量m_csAddress0Lock在函数CDeviceGlobal::Initialize中初始化:

m_csAddress0Lock.Initialize();

函数CritSec_Ex::Initialize的实现:

EnterCriticalSection(&m_cs);

m_hOwnerThread = 0; m_cOwnerReferences = 0; m_cWaitingThreads = 0;

m_fClosing = FALSE;

LeaveCriticalSection(&m_cs);

函数CritSec_Ex::EnterCritSec_Ex中,由于if else语句很多,把代码列出来,逐行注释比较好。

// 获取当前的TickCount,以备下面wait的时候使用

tStart = GetTickCount();

r = ! WAIT_TIMEOUT; // anything other than WAIT_TIMEOUT will suffice

// 获取当前Thread的id,以判断当前占用address 0的thread是否为当前的thread

me = GetCurrentThreadId();

// 如果将要删除当前CritSec_Ex对象,直接返回。

// m_fClosing在函数CritSec_Ex::PrepareDeleteCritSec_Ex中被设置为TRUE。

if (m_fClosing)

return CSS_DESTROYED;

EnterCriticalSection(&m_cs);

do {

// 先将wait标志设置为false

fWaiting = FALSE;

// 此处再次检查是否要删除当前CritSec_Ex对象

// 此处多了一个SetEvent的操作,是为了通知等待使用address 0的其他线程。

// 上面判断m_fClosing的时候,没有调用SetEvent,是因为本线程还没进入临界区,也就不可能存在因为本线程造成的阻塞。

if (m_fClosing) {

SetEvent(m_hev);

retval = CSS_DESTROYED;

}

// 若wait event时time out,直接返回

else if (r == WAIT_TIMEOUT) {

retval = CSS_TIMEOUT;

}

// 若m_hOwnerThread为0,也就是当前没有线程占用address 0,则本线程可以使用address 0

else if (m_hOwnerThread == 0) {

m_hOwnerThread = me;

m_cOwnerReferences = 1;

retval = CSS_SUCCESS;

}

// 若本线程正在使用address 0,则将引用计数加1

else if (m_hOwnerThread == me) {

++m_cOwnerReferences;

retval = CSS_SUCCESS;

}

// 到了这一步,说明address 0被其他线程占用,我们能做的只有等待。同时将等待线程的计数加1

else {

// Oh well, we've got to wait.

++m_cWaitingThreads;

fWaiting = TRUE;

}

// 不需要访问临界区保护的区域,及时离开

LeaveCriticalSection(&m_cs);

if (fWaiting) {

if (ulTimeout != INFINITE) {

tNow = GetTickCount();

// 传入的等待时间,是用户的等待时间,所以要把前面代码执行的耗时减掉

if (tNow - tStart < ulTimeout)

tLeft = ulTimeout - (tNow - tStart);

else

tLeft = 0; // poll one more time

} else {

tLeft = INFINITE;

}

r = WaitForSingleObject(m_hev, tLeft);

// 因为要继续do-while操作,所以需要再次进入临界区

EnterCriticalSection(&m_cs);

--m_cWaitingThreads;

}

} while (fWaiting);

return retval;

7、如果状态为DEVICE_CONFIG_STATUS_RESET_AND_ENABLE_PORT:

这一步没进行什么操作,只是改变了当前状态,以进入下一步操作。

8、如果状态为DEVICE_CONFIG_STATUS_SCHEDULING_GET_DEVICE_DESCRIPTOR_TEST:

调用函数GetDescriptor获取设备的Device Desctiptor。

此处使用的address为address 0。

函数GetDescriptor中根据传入参数中的Descriptor type,设置相应参数,然后调用函数CControlPipe::IssueTransfer。

关于函数CControlPipe::IssueTransfer,请参考另外一篇文章:IssueTransfer调用相关

如果调用函数GetDescriptor成功,将状态设置为:DEVICE_CONFIG_STATUS_SCHEDULING_SET_ADDRESS。

9、如果状态为DEVICE_CONFIG_STATUS_SCHEDULING_SET_ADDRESS:

调用函数CControlPipe::IssueTransfer,设置设备的Address。

设备当前使用的是Address 0。需要将设备的Address设置为自己的Address。

由于设备当前的Address是0,所以,调用函数CControlPipe::IssueTransfer时,使用的Address还是0。

若设置Address成功:

如果当前的设备上RootHub:

设置OTG Device的Address:m_uiOtgDeviceAddress = address;

设置状态信息为DEVICE_CONFIG_STATUS_LEAVE_ADDRESS0。

10、 如果状态为DEVICE_CONFIG_STATUS_LEAVE_ADDRESS0:

调用函数CDeviceGlobal::Addr0LockLeave。

函数CDeviceGlobal::Addr0LockLeave的实现:

m_csAddress0Lock.LeaveCritSec_Ex ();

函数CritSec_Ex::LeaveCritSec_Ex中:

首先将引用计数减一。

若引用计数为0,说明当前线程已经使用完该临界区:

释放临界区,并set event m_hev,以通知其他的等待线程:

m_hOwnerThread = 0;

SetEvent(m_hev);

11、 如果状态为DEVICE_CONFIG_STATUS_SCHEDULING_GET_INITIAL_DEVICE_DESCRIPTOR:

以ENDPOINT_ZERO_MIN_MAXPACKET_SIZE为Buffer size调用函数GetDescriptor获取设备的Device Desctiptor。

此处使用的address为步骤9中设置的Address。

若hub的层次已经达到了最大的USB_MAXIMUM_HUB_TIER(5)层,并且刚插入的设备为Hub,则将状态设置为错误。

12、如果状态为DEVICE_CONFIG_STATUS_SCHEDULING_GET_DEVICE_DESCRIPTOR:

以sizeof( deviceInfo.Descriptor )为Buffer size调用函数GetDescriptor获取设备的Device Desctiptor。

13、如果状态为DEVICE_CONFIG_STATUS_SETUP_CONFIGURATION_DESCRIPTOR_ARRAY:

根据Descriptor中的Configuration个数,创建Configuration数组,并将数组指针赋值给Device Infor的成员。

const UINT numConfigurations = deviceInfo.Descriptor.bNumConfigurations;

deviceInfo.lpConfigs = new NON_CONST_USB_CONFIGURATION[ numConfigurations ];

初始化数组中,每个成员的size成员。

deviceInfo.lpConfigs[ config ].dwCount = sizeof( NON_CONST_USB_CONFIGURATION );

接下来的两步是循环获取所有的config desctiptor。

14、如果状态为DEVICE_CONFIG_STATUS_SCHEDULING_GET_INITIAL_CONFIG_DESCRIPTOR:

调用函数GetDescriptor获取一个config descriptor。

这一步只是初步获取config descriptor信息。主要是为了取得整个config descriptor的大小。

15、如果状态为DEVICE_CONFIG_STATUS_SCHEDULING_GET_CONFIG_DESCRIPTOR:

根据上一步中得到的config descriptor的total length,调用函数GetDescriptor获取完整的config descriptor。

将获得的数据copy到device infor中。

memcmp( &deviceInfo.lpConfigs[ currentConfigDescriptorIndex ].Descriptor, pDataBuffer,

sizeof( USB_CONFIGURATION_DESCRIPTOR ) )

调用函数CreateUsbConfigurationStructure创建configuration 结构体。

CreateUsbConfigurationStructure( deviceInfo.lpConfigs[ currentConfigDescriptorIndex ],

pDataBuffer, wTotalLength )

函数CreateUsbConfigurationStructure中使用传入buffer中的数据初始化device infor中的config结构体。

对传入的数据进行判断,若合法:

如果存在config descriptor的扩展数据,将扩展数据copy到device infor中。

config descriptor的扩展数据放在传入的buff中起始地址到出现第一个interface descriptor的地方。

若有扩展数据,根据扩展数据的大小,创建一个buffer,并让config中的lpbExtended成员指向创建的buff。

然后将扩展数据copy到buff中。

接下来,将传入buff中的interface descriptor数据copy到device infor中。

统计interface descriptor的个数。

统计方法是依次取出PUSB_COMMON_DESCRIPTOR结构体,

判断结构体的bDescriptorType成员是否为USB_INTERFACE_DESCRIPTOR_TYPE。

创建interface descriptor结构体数组,并将config descriptor中的lpInterfaces成员指向该数组。

接下来,依次将interface descriptor结构体,interface descriptor的扩展数据,

和endpoint descriptor结构体,endpoint descriptor的扩展数据copy到config descriptor中。

copy interface descriptor结构体。

判断是否存在interface descriptor扩展数据。

判断方法是该interface descriptor结构体数据结束点到下一个endpoint descriptor

或者interface descriptor之间是否有数据。

若有扩展数据,创建buff,并将interface descriptor结构体中的lpbExtended成员指向该buff,

然后将扩展数据copy到buff。

判断该interface descriptor是否存在endpoint descriptor,

若存在,创建endpoint descriptor数组并copy endpoint descriptor。

若该endpoint descriptor存在扩展数据,创建buff,并copy扩展数据。

若buff中还有数据,copy开始的三个字节到成员变量m_bOtgDesc中作为OTG descriptor的识别符。

判断是否还有config descriptor需要获取,

若有,将状态设置为DEVICE_CONFIG_STATUS_SCHEDULING_GET_INITIAL_CONFIG_DESCRIPTOR,即回到步骤14。

若无,将状态设置为DEVICE_CONFIG_STATUS_DETERMINE_CONFIG_TO_CHOOSE,即开始下一步操作。

16、如果状态为DEVICE_CONFIG_STATUS_DETERMINE_CONFIG_TO_CHOOSE:

如果在函数CDeviceGlobal::Initialize中成功获取了USBD中的HcdSelectConfiguration函数:

m_hUSBDInstance = LoadDriver(TEXT("USBD.DLL"));

m_pUSBDSelectConfigurationProc = (LPUSBD_SELECT_CONFIGURATION_PROC)GetProcAddress(m_hUSBDInstance,

TEXT("HcdSelectConfiguration"));

则调用函数HcdSelectConfiguration来选择合适的config。

(*m_pCHcd->GetpUSBDSelectConfigurationProc())(LPCUSB_DEVICE(&deviceInfo),&bConfig)

选择出的config编号放在bConfig变量中。

如果不存在HcdSelectConfiguration函数,或者调用函数失败,或者取得的config编号大于config个数,则bConfig = 0。

根据选定的config,判断插入的设备是否是自供电的。

if (deviceInfo.lpConfigs[bConfig].Descriptor.bmAttributes & SELF_POWERED)

若是自供电:

不作特殊操作。

若不是自供电:

定义局部变量dwCfgPower。

DWORD dwCfgPower = deviceInfo.lpConfigs[bConfig].Descriptor.MaxPower * 2;

若deviceInfo.lpConfigs[bConfig].Descriptor.MaxPower为0:

将dwCfgPower赋值为500。

定义局部变量isOK:

BOOL isOK;

(if)如果成员变量m_deviceInfo中的lpConfigs为空,即当前的对象不存在config,说明当前设备为root hub。

调用PDD中的函数HcdPdd_CheckConfigPower来判断是否可正常供电:

isOK = HcdPdd_CheckConfigPower(port, dwCfgPower, 0);

(else if)如果当前的设备为自供电:

else if (m_deviceInfo.lpActiveConfig->Descriptor.bmAttributes & SELF_POWERED)

说明我们可以attach高电压需求的设备,将isOK设置为TRUE。

(else if)执行到这儿,说明当前设备为bus-powered,因此we can only attach low-powered devices。

判断插入设备的电压需求是否小于100,若是,将isOK设置为TRUE。

else if (dwCfgPower <= 100)

(else)若以上条件都不成立,将isOK设置为FALSE。

如果isOK为FALSE,将状态设置为DEVICE_CONFIG_STATUS_FAILED。

如果状态不为DEVICE_CONFIG_STATUS_FAILED:

将选定的config设置为active config:

deviceInfo.lpActiveConfig = &deviceInfo.lpConfigs[bConfig]。

将状态设置为DEVICE_CONFIG_STATUS_SCHEDULING_SET_CONFIG。

17、如果状态为DEVICE_CONFIG_STATUS_SCHEDULING_SET_CONFIG:

调用函数CControlPipe::IssueTransfer设置config。

若设置成功:

if(m_bOtgDesc[2] == USB_OTG_DESCRIPTOR_TYPE)

// Identify OTG device

IdentifyOTGDevice(port, address, pControlPipe);

函数IdentifyOTGDevice中:

如果当前设备为root hub,并且为OTG中的A device:

if ((m_pAttachedHub==NULL) && ::OTGHNP_IsADevice())

如果当前工作中HNP mode:

if (port == 1)

如果hub thread没有关闭,并且当前设备支持HNP:

if (!m_fHubThreadClosing && (m_bOtgDescriptor & OTG_ATTR_HNP_SUPPORT))

调用函数CControlPipe::IssueTransfer告诉另外一个OTG设备,我们支持HNP。

若调用成功:

::OTGHNP_EventNotify(HNP_EVENT_HOST_OTGDESC);

如果为Legacy host port,调用函数CControlPipe::IssueTransfer设置a_alt_hnp_support,但不关心结果。

接着,进入下一步。如果插入的设备为hub,将状态设置为DEVICE_CONFIG_STATUS_SCHEDULING_GET_INITIAL_HUB_DESCRIPTOR。

否则,即为function,将状态设置为DEVICE_CONFIG_STATUS_CREATE_NEW_FUNCTION。

18、接下来是调用两次GetDescriptor函数来获取HUB descriptor。

如果状态为DEVICE_CONFIG_STATUS_SCHEDULING_GET_INITIAL_HUB_DESCRIPTOR

或DEVICE_CONFIG_STATUS_SCHEDULING_GET_HUB_DESCRIPTOR,

调用函数GetDescriptor获取hub descriptor。

如果状态为DEVICE_CONFIG_STATUS_SCHEDULING_GET_INITIAL_HUB_DESCRIPTOR,

将状态设置为DEVICE_CONFIG_STATUS_SCHEDULING_GET_HUB_DESCRIPTOR。

如果状态为DEVICE_CONFIG_STATUS_SCHEDULING_GET_HUB_DESCRIPTOR,

将状态设置为DEVICE_CONFIG_STATUS_CREATE_NEW_HUB进入下一步操作。

19、如果状态为DEVICE_CONFIG_STATUS_CREATE_NEW_HUB,创建一个CExternalHub对象:

pNewDevice = new CExternalHub( address,

deviceInfo,

fIsLowSpeed,fIsHighSpeed,

m_tierNumber + 1,

usbHubDescriptor,

m_pCHcd,

this,port);

20、如果状态为DEVICE_CONFIG_STATUS_CREATE_NEW_FUNCTION,创建一个CFunction对象:

pNewDevice = new CFunction( address,

deviceInfo,

fIsLowSpeed,fIsHighSpeed,

m_tierNumber + 1,

m_pCHcd,

this,port);

21、如果状态为DEVICE_CONFIG_STATUS_INSERT_NEW_DEVICE_INTO_UPSTREAM_HUB_PORT_ARRAY,

将步骤19或者20中创建的对象添加到device列表:

m_ppCDeviceOnPort[ port - 1 ] = pNewDevice;

22、如果状态为DEVICE_CONFIG_STATUS_SIGNAL_NEW_DEVICE_ENTER_OPERATIONAL_STATE:

调用新对象的EnterOperationalState函数。

如果调用成功,将状态设置为DEVICE_CONFIG_STATUS_DONE,即结束对函数AttachDevice函数的调用。

函数CExternalHub::EnterOperationalState中,启动了一个线程,监视设备插入。

函数CFunction::EnterOperationalState中,调用USBD中的函数HcdDeviceAttached,对设备进行attach。

详细请参考另一篇介绍函数EnterOperationalState的文章。

+++++++++++++参考概念+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

1、关于TransactionTrasnlate(wikipedia)

To allow high-speed devices to operate in their fastest mode all hubs between the devices and the computer must be high speed. High-speed devices should fall back to full-speed when plugged in to a full-speed hub (or connected to an older full-speed computer port). While high-speed hubs support all device speeds, low and full-speed traffic is combined and segregated from high-speed traffic through a transaction translator. Each transaction translator segregates lower speed traffic into its own pool, essentially creating a virtual full-speed bus. Some designs use a single transaction translator, while other designs have multiple translators. Having multiple translators is only a significant benefit when connecting multiple high-bandwidth full-speed devices.

2、关于interrupt endpoint和control pipe (wikipedia)

Hubs are not transparent when dealing with changes in the status of downstream ports, such as insertion or removal of devices. In particular, if a downstream port of a hub changes status, this change is dealt with an interaction between the host and this hub; the hubs between them act as transparent in this case.

To this aim, each hub has a single interrupt endpoint "1 IN" (endpoint address 1, hub-to-host direction) used to signal changes in the status of the downstream ports. When a device is attached, the hub detects the device pull-up resistor on either D+ or D- and signals the insertion to the host via this interrupt endpoint. When the host polls this interrupt endpoint, it is informed of the presence of the new device. It then instructs the hub (via the default control pipe) to reset the port where the new device is connected. This reset makes the new device assume address 0, and the host can then interact with it directly; this interaction will result in the assignment of a new (non-zero) address to the device.

3、EHCI协议的调度思想

EHCI协议规定了3个接口空间:PCI配置空间、HC寄存器空间和调度接口空间。支持两种数据传输类型:异步传输和周期性传输。

PCI配置空间描述了开发作为PCI设备的USB主机控制器时需要的寄存器。HC寄存器空间由性能寄存器和操作寄存器组成;性能寄存器用于记录主机控制器的性能参数值;操作寄存器包括命令、状态、中断和根集线器端口寄存器,是进行USB控制的最主要接口空间。调度接口空间包括四种传输描述符和对应调度策略。

周期性数据传输主要包括中断和同步传输,对应描述符为siTD、iTD;异步数据传输包括批量和控制传输,对应描述符为QH、qTD。所有异步传输数据包通过队列头QH链接成一个环形链表,一个QH代表一个端点,利用操作寄存器Asynlistaddr进行初始访问,QH下链接多个传输描述符 qTD,qTD作为一个传输单元对应一个或多个事务处理,可最大寻址20K数据量。创建的传输链表结构如图1所示。

(插入图片)

图1中三个QH分别代表三个不同类型的端点,而qTD编号则代表了传输描述符的调度顺序。整个环形链表在生存期内一直位于系统内存中。

EHCI协议描述的调度思想为:客户软件用USB请求块(URB)的形式向管道请求数据传输,主机控制器驱动程序(EHCD)负责跟踪URB进程并根据数据量的大小生成一条qTD链,激活qTD并链接在对应端点的QH下,形成图1所示结构。随后,EHCD根据USB协议规定的“帧带宽对于端点均衡分配”原则,按照端点轮流读取的顺序将一个激活的qTD信息映射到QH的工作区中,随之启动硬件操作,EHC(高速主机控制器)读取QH工作区信息开始进行qTD 的相关事务处理。处理完毕,回写状态信息到QH工作区,触发特定中断重新将控制权交还给EHCD,EHCD从QH工作区中读取状态信息回写到对应qTD 中,结束一次qTD的传输,并激活下一个,以此循环操作。当URB所关联的qTDs全部处理完毕后,更新URB状态信息,通过回调函数带回给应用程序。这部分调度思想主要在驱动程序中实现。

+++++++++++++参考STRUCT+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

typedef struct _USB_DEVICE_INFO {

DWORD dwCount;

USB_DEVICE_DESCRIPTOR Descriptor;

LPNON_CONST_USB_CONFIGURATION lpConfigs;

LPNON_CONST_USB_CONFIGURATION lpActiveConfig;

} USB_DEVICE_INFO, * LPUSB_DEVICE_INFO;

typedef struct _USB_DEVICE_DESCRIPTOR

{

BYTE bLength;

BYTE bDescriptorType;

BYTE bcdUSBLo;

BYTE bcdUSBHi;

BYTE bDeviceClass;

BYTE bDeviceSubClass;

BYTE bDeviceProtocol;

BYTE bMaxPacketSize0;

BYTE idVendorLo;

BYTE idVendorHi;

BYTE idProductLo;

BYTE idProductHi;

BYTE bcdDeviceLo;

BYTE bcdDeviceHi;

BYTE iManufacturer;

BYTE iProduct;

BYTE iSerialNumber;

BYTE bNumConfigurations;

} USB_DEVICE_DESCRIPTOR, * PUSB_DEVICE_DESCRIPTOR;

typedef struct _NON_CONST_USB_CONFIGURATION {

DWORD dwCount;

USB_CONFIGURATION_DESCRIPTOR Descriptor;

LPBYTE lpbExtended;

// Total number of interfaces (including alternates)

DWORD dwNumInterfaces;

LPNON_CONST_USB_INTERFACE lpInterfaces;

} NON_CONST_USB_CONFIGURATION, * LPNON_CONST_USB_CONFIGURATION;

typedef struct _NON_CONST_USB_INTERFACE {

DWORD dwCount;

USB_INTERFACE_DESCRIPTOR Descriptor;

LPBYTE lpbExtended;

LPNON_CONST_USB_ENDPOINT lpEndpoints;

} NON_CONST_USB_INTERFACE, * LPNON_CONST_USB_INTERFACE;

typedef struct _NON_CONST_USB_ENDPOINT {

DWORD dwCount;

USB_ENDPOINT_DESCRIPTOR Descriptor;

LPBYTE lpbExtended;

} NON_CONST_USB_ENDPOINT, * LPNON_CONST_USB_ENDPOINT;

typedef struct _USB_CONFIGURATION_DESCRIPTOR

{

BYTE bLength;

BYTE bDescriptorType;

BYTE wTotalLengthLo;

BYTE wTotalLengthHi;

BYTE bNumInterfaces;

BYTE bConfigurationValue;

BYTE iConfiguration;

BYTE bmAttributes;

BYTE bMaxPower; // In 2ma increments

} USB_CONFIGURATION_DESCRIPTOR, * PUSB_CONFIGURATION_DESCRIPTOR;

typedef struct _USB_INTERFACE_DESCRIPTOR

{

BYTE bLength;

BYTE bDescriptorType;

BYTE bInterfaceNumber;

BYTE bAlternateSetting;

BYTE bNumEndpoints;

BYTE bInterfaceClass;

BYTE bInterfaceSubClass;

BYTE bInterfaceProtocol;

BYTE iInterface;

} USB_INTERFACE_DESCRIPTOR, * PUSB_INTERFACE_DESCRIPTOR;

typedef struct _USB_ENDPOINT_DESCRIPTOR

{

BYTE bLength; // 0x00

BYTE bDescriptorType; // 0x01

BYTE bEndpointAddress; // 0x02 Direction in bit 7

BYTE bmAttributes; // 0x03 CTRL, ISOC, BULK, INTR

BYTE wMaxPacketSizeLo; // 0x04

BYTE wMaxPacketSizeHi; // 0x05

BYTE bInterval; // 0x06 Polling interval (Interrupt, Isoch)

} USB_ENDPOINT_DESCRIPTOR, * PUSB_ENDPOINT_DESCRIPTOR;

typedef struct _USB_HUB_DESCRIPTOR {

UCHAR bDescriptorLength; // Length of this descriptor

UCHAR bDescriptorType; // Hub configuration type

UCHAR bNumberOfPorts; // number of ports on this hub

USHORT wHubCharacteristics; // Hub Charateristics

UCHAR bPowerOnToPowerGood; // port power on till power good in 2ms

UCHAR bHubControlCurrent; // max current in mA

//

// room for 255 ports power control and removable bitmask

UCHAR bRemoveAndPowerMask[64];

} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR;

// this enum is used in AttachNewDevice

enum DEVICE_CONFIG_STATUS

{

//

// KEEP THIS ARRAY IN SYNC WITH cszCfgStateStrings array below!

//

// these steps are common for both hubs and functions

DEVICE_CONFIG_STATUS_OPENING_ENDPOINT0_PIPE,

DEVICE_CONFIG_STATUS_USING_ADDRESS0,

DEVICE_CONFIG_STATUS_RESET_AND_ENABLE_PORT,

DEVICE_CONFIG_STATUS_SCHEDULING_GET_DEVICE_DESCRIPTOR_TEST,

DEVICE_CONFIG_STATUS_SCHEDULING_SET_ADDRESS,

DEVICE_CONFIG_STATUS_LEAVE_ADDRESS0,

DEVICE_CONFIG_STATUS_SCHEDULING_GET_INITIAL_DEVICE_DESCRIPTOR,

DEVICE_CONFIG_STATUS_SCHEDULING_GET_DEVICE_DESCRIPTOR,

DEVICE_CONFIG_STATUS_SETUP_CONFIGURATION_DESCRIPTOR_ARRAY,

DEVICE_CONFIG_STATUS_SCHEDULING_GET_INITIAL_CONFIG_DESCRIPTOR,

DEVICE_CONFIG_STATUS_SCHEDULING_GET_CONFIG_DESCRIPTOR,

DEVICE_CONFIG_STATUS_DETERMINE_CONFIG_TO_CHOOSE,

DEVICE_CONFIG_STATUS_SCHEDULING_SET_CONFIG,

// if ( device is a hub ) {

DEVICE_CONFIG_STATUS_SCHEDULING_GET_INITIAL_HUB_DESCRIPTOR,

DEVICE_CONFIG_STATUS_SCHEDULING_GET_HUB_DESCRIPTOR,

DEVICE_CONFIG_STATUS_CREATE_NEW_HUB,

// } else {

DEVICE_CONFIG_STATUS_CREATE_NEW_FUNCTION,

// }

DEVICE_CONFIG_STATUS_INSERT_NEW_DEVICE_INTO_UPSTREAM_HUB_PORT_ARRAY,

DEVICE_CONFIG_STATUS_SIGNAL_NEW_DEVICE_ENTER_OPERATIONAL_STATE,

DEVICE_CONFIG_STATUS_FAILED,

DEVICE_CONFIG_STATUS_DONE,

DEVICE_CONFIG_STATUS_INVALID // must be last item in list

};

+++++++++++++++参考WEB+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

http://en.wikipedia.org/wiki/USB_hub

http://www.linux-usb.org/usb2.html

你可能感兴趣的:(device)