前面有篇文章说到如何获取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);

2、HCD_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;

6、CreateHCDObject函数中,直接创建一个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 );

11、AttachDevice函数中,根据当前的状态,判断下一步该作什么操作,逐步完成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的等待。

转载于:https://www.cnblogs.com/andriod-html5/archive/2011/04/01/2539597.html

USB Device Desctiptor 相关相关推荐

  1. S3C2440 WINCE6将USB DEVICE改成USB HOST,实现两个USB HOST

    S3C2440一般默认的是一个USB DEVICE,一个USB HOST,即一个主口,一个从口,先来看看USB Device与USB Host相关知识. USB Host: 最底层就是USB Host ...

  2. STM32驱动开发(二)--USB Device RNDIS虚拟网卡(USB2.0 基础概念讲解)

    STM32驱动开发(二)–USB Device RNDIS虚拟网卡(USB2.0基础概念讲解) 一.简介   本文基于stm32 Rndis实例,github开源, 使用STM32F407单板.结合协 ...

  3. 20200213ubuntu20.04下的笔记本USB摄像头的相关资料

    20200213ubuntu20.04下的笔记本USB摄像头的相关资料 rootroot@rootroot-HP-Laptop-14s-dp0xxx:~$  rootroot@rootroot-HP- ...

  4. USB device USB controller USB passthrough

    近期往 openstack 里倒腾 USB passthrough[1],遂把 USB 知识做较为全面的整理,以供分享. USB device 什么是 USB device, 上图机智的小萌狗就是 U ...

  5. UDC (usb device controller) Framework - USB gadget driver framework

    http://blog.csdn.net/u011279649/article/details/11059433 USB gadget driver的框架可分为三部分:UDC-core, compos ...

  6. Vmware提示:host usb device connections disabled-(vmware 主机已禁用 usb 设备连接)

    Vmware提示:host usb device connections disabled-(vmware 主机已禁用 usb 设备连接) VMware Workstation,提示提示Host US ...

  7. USB查看器 USB Device Tree Viewer(UsbTreeView.exe)的使用(重启Intel Realsense摄像头)

    文章目录 简介 打开后界面 测试U盘弹出后是否能找回 测试挽回掉线的摄像头 简介 USB Device Tree Viewer是一个非常实用的USB设备查看器,它可以发现所有的usb接口的使用情况,并 ...

  8. StackOverflow How to programmatically unplug replug an arbitrary USB device? 如何以编程方式拔出并重新插入任意USB设备

    文章目录 方法1:通过devcon工具重启usb hub 像这种情况,明明插了六个摄像头,它偏偏掉一个... 刚好,我们可以测试How to programmatically unplug & ...

  9. USB device如何进入suspend模式

    1. 当没有使能usb device(usb_conf DEVEN没有置1),device处于L3状态 2. 当使能了usb device,但是没有连接到host,device处于L2(suspend ...

  10. Start booting from USB device boot failed 解决办法(老机器问题)

    Start booting from USB device boot failed 解决办法 问题: 一般情况下,我们设置电脑BIOS USB 第一启动方式最重要的两步: 1.Removable De ...

最新文章

  1. 表格下划线怎么加粗_这招高!Excel签名栏的下划线随列宽变化,是不是感觉牛哄哄的?...
  2. String.format()【演示具体的例子来说明】
  3. 滴滴重磅发布:KDD2018大会187页人工智能+交通教程
  4. python画折线图代码-Python折线图的分析过程和画图的方法
  5. IE6 CSS的一个bug
  6. 面向新闻媒体的命名实体识别技术
  7. 简单的二叉树创建与遍历
  8. FreeRTOS+STM32F103中断中发送任务通知单片机死机问题
  9. wpf的控件style
  10. 一起来学习BERT常见的几个变体
  11. STM32F407 窗口看门狗 个人笔记
  12. matlab网格划分提取坐标,ANSYS-划分网格后导出单元和结点坐标等信息
  13. cad字体安装_为什么CAD图纸打开后会显示很多问号“???”,该怎么解决
  14. is 简写 缩写_为什么e.g.是for example的缩写?它和i.e.是什么关系?
  15. css中repeat用法,CSS background-repeat用法及代码示例
  16. 【直播教程】直播间没人看?5大技巧教你提升!
  17. 前端大佬谈国产开源:VUE 的成功在于社区运营
  18. iOS 内购 payment.applicationUsername 的坑
  19. 计算机应用1.2版,201303《计算机应用基础》在线作1_2.doc
  20. 论文笔记-建筑能源管理的强化模型预测控制

热门文章

  1. FISCO BCOS 区块链 学习开发步骤
  2. thinkphp 访问根目录文件
  3. 计算机科学与技术志愿意愿,高考志愿填报如何得高分
  4. java中修改对象类的数据_Java中创建对象的六个步骤 细分后(new关键字)对象头详细介绍...
  5. ORM框架使用优缺点
  6. Centos7.x 安装Kubernetes(K8s) 1.16.2 kubeadm kubelet kubectl 单机版 2019-10-20更新
  7. nginx 禁止访问配置,指定URL地址指定IP允许访问
  8. 2.4.PHP7.1 狐教程-【PHP常量】
  9. JMS 基本概念、消息结构、确认模式 acknowledgeMode
  10. 阶段3 3.SpringMVC·_06.异常处理及拦截器_2 SpringMVC异常处理之演示程序异常