Wince 5.0 获取U盘描述符
最近由于项目需求,需要获取U盘的描述符。而现有的usb otg驱动中没有提供这样的接口。没有办法,只能自己动手来修改了。本人刚开始接触USB 驱动,在代码的海洋中还比较迷茫。以下代码的实现是公司内一位大牛所作,本人只是借来学习一用。也算对USB驱动有个初步的了解。
先罗嗦两句。刚开始看到文件夹usbotg,还有点不太理解是什么意思。当时在想,usb驱动为什么不直接放在usb文件夹下,而是放在usbotg文件夹下?otg是嘛意思?资料当然也很好找,usb家族里面,对该成员有详细介绍(http://www.usb.org/developers/onthego)。原来otg就是On The Go。
传统的usb协议中分为host 和function,一个设备,要么是host,要么是function。例如,一般PC是作为host,而其他的设备,U盘,打印机,相机等是作为function存在的。这样就有一个问题,对PC的依赖过大,离开了host PC,两个usb function在一起,只能干瞪眼,没办法进行交互。当然,两个host,通过usb cable接起来,也是互相不认识的。为了解决该问题,在usb 2.0的基础上,出现了otg的概念。
usbotg中,一个设备既可以作为host,也可以作为function。两个usb 设备连接到一起,根据功能的需要,其中一个(usb1)作为host,另外一个(usb2)作为function。如果功能改变了,需要把usb2作为host,而把usb1作为function,可以通过HNP(Host Negotiation Protocol)来实现。具体怎么实现,可以到usb老家里面看看协议。
由于懒惰,本人还没详细看过协议。此处只是介绍个皮毛。若有不妥之处,还望各位大虾指正。
好了,废话不多说了,一步步来看实现吧。
1、首先当然是考察可行性。
也就是驱动中是否能够提供这样的接口。调查发现,在otg driver中保存的g_OtgCoreInfo(OTG_CORE_INFO)变量中有一个m_dwHostContext成员,保存的是Host Controller Driver的Context。m_dwHostContext会在OTG_Init函数中被初始化,代码如下:
g_OtgCoreInfo.m_dwHostContext = HCD_Init((DWORD)&hostSettings);
Host Controller Driver中保存有一个g_dwPddContext(DWORD,其实是SEHCDPdd结构体的指针)变量,在函数HCD_Init(在MDD中)中会调用HcdPdd_Init函数来初始化该变量,并最终把该
变量作为函数返回值返回。代码如下:
g_dwPddContext =HcdPdd_Init(dwContext);
...
return g_dwPddContext;
HcdPdd_Init(在HDD中)函数中首先创建一个SEHCDPdd对象,然后调用InitializeEHCI函数初始化该对象,最后将该对象的指针返回。
SEHCDPdd * pPddObject = malloc(sizeof(SEHCDPdd));
...
fRet = InitializeEHCI(pPddObject, (POTG_HOST_SETTINGS)dwContext);
...
return (DWORD)pPddObject;
InitializeEHCI(在HDD中)函数中,初始化pPddObject变量中的各个成员,我们本次只关注成员lpvEHCDMddObject的初始化:
pobEHCD = HcdMdd_CreateHcdObject(pPddObject, pobMem, NULL, (PUCHAR)pHostSetting->m_dwRegBase, 0)
...
pPddObject->lpvEHCDMddObject = pobEHCD;
HcdMdd_CreateHcdObject函数(在MDD中)中调用CreateHCDObject函数创建HCD Object。并将对象指针返回。
CHcd * pobUhcd = CreateHCDObject(lpvUhcdPddObject,
(CPhysMem *)lpvMemoryObject,szRegKey,ioPortBase,dwSysIntr);
...
return pobUhcd;
CreateHCDObject函数(在HDD中)中,创建一个CEhcd对象,并返回。
return new CEhcd (pvUhcdPddObject, pCPhysMem,szDriverRegistryKey,portBase,dwSysIntr);
CEhcd类继承自CHW类,CHW类继承自CHcd类。CHcd类中包含一个CRootHub*类型的成员。
CRootHub* m_pCRootHub; // pointer to CRootHub object, which represents // the built-in hardware USB ports
CRootHub类继承自CHub类,CHub类继承自CDevice类。
CHub类中有函数获取当前hub上连接设备的列表。
CDevice** GetDeviceList() {return m_ppCDeviceOnPort;};
CDevice类包含一个m_deviceInfo(USB_DEVICE_INFO)成员变量,USB_DEVICE_INFO结构体中包含一个Descriptor(USB_DEVICE_DESCRIPTOR)成员,该成员就是用来保存USB设备的Descriptor信息的。USB_DEVICE_DESCRIPTOR结构体的内容是在USB协议中规定的。
2、既然找到了描述符的位置所在,接下来想办法把它提供出去就OK了。
首先,要追加个函数,把Descriptor的内容返回出去。该接口可以加在CDevice类,或者CHub类中。如果放在CDevice类中,只能获取该设备的Descriptor,如果想获取hub上连接的所有usb设备的descritor,还需要在CHub类,或其他地方追加接口,来对hub上连接的usb设备的list进行控制。这样比较麻烦,此处选择了一种比较懒省事的方法,在CHub类中追加接口函数(GetDeviceDescriptor),一次将hub上连接的所有usb设备的Descriptor全部取出。
遍历hub的所有port,也就是变量CHub类的成员变量m_ppCDeviceOnPort
(范围取min(m_usbHubDescriptor.bNumberOfPorts, MAX_DEVICE_PER_HUB))
判断port上是否有设备,若有,将设备的Descripror赋值给output buffer。
(lpudd[i] = m_ppCDeviceOnPort[i]->m_deviceInfo.Descriptor;)
3、最底层的实现有了,接下来就是如何把函数一步步输出去了。
此处有个疑问,既然上面有了底层实现,我们直接在IOControl函数中追加个IOConrol Code,然后调用底层的实现不就行了?相信有些初学者也有同样的疑问。看一下第一步中的分析,就应该比较清楚了。在第一步的分析中,可以发现,不同的对象,是在不同的层中创建的。对相应对象的操作,也都在对应的层中。如果我们跳过中间层,直接访问底层实现,就破坏了分层驱动的结构。
m_pCRootHub(CRootHub)是CEHcd类的一个成员变量,所以可以通过CEHcd的对象,来最终实现对上述函数的调用。CEHcd的对象是在函数HcdMdd_CreateHcdObject中创建的,所以我们可以在HcdMdd_CreateHcdObject函数定义的地方,追加一个函数(HcdMdd_GetHubConnectedDeviceDescriptor),来实现对底层实现的调用。假设Root hub上只接了我们的hub。首先通过m_pCRootHub获取root hub上设备的列表(CHub),然后调用列表中对象的GetDeviceDescriptor函数。
CDevice **lppcd = pobHcd->m_pCRootHub->GetDeviceList();
((CHub *)lppcd[0])->GetDeviceDescriptor(lpudd);
4、从第一步的分析中可知,HcdMdd_CreateHcdObject函数的返回值,被保存在了pPddObject(SEHCDPdd)的lpvEHCDMddObject成员中。pPddObject的创建和初始化是在HcdPdd_Init函数和InitializeEHCI函数中实现的。所以在HcdPdd_层中,我们可以追加函数(HcdPdd_GetHubConnectedDeviceDescriptor),实现对函数HcdMdd_GetHubConnectedDeviceDescriptor的调用。
HcdMdd_GetExtHubConnectedDeviceDesc(pPddObject->lpvEHCDMddObject, lpudd);
5、由步骤1的分析可知,HcdPdd_Init函数在HCD_Init函数中被调用。用来给全局变量g_dwPddContext赋值。所以,可以在HCD_层中,追加函数(HCD_GetHubConnectedDeviceDescriptor),通过保存的全局变量,实现对函数HcdPdd_GetHubConnectedDeviceDescriptor的调用。
6、继续往上,函数OTG_Init中调用了函数HCD_Init。终于到头了,此时,我们可以在OTG_IOControl函数中追加一个case,来调用函数HCD_GetHubConnectedDeviceDescriptor。当然,要追加相应的IOCTL Code的定义。
如果不需要Descriptor的全部信息,可以定义一个自己的结构体,在GetDeviceDescriptor函数中,取出Descriptor的部分成员,赋值给自己定义的结构体即可。
IOCTL 和结构体的定义,可以放在文件USB_def.h文件中。
参考:
typedef struct __OTG_CORE_INFO {
DWORD m_dwRegBase; // @field Base address of core registers
HINTR m_hSysIntr; // @field Handle returned by the OS_RegisterInterrupt
DWORD m_dwSysIntr; // @field System interrupt number of OTG controller
DWORD m_dwOpenCnt; // @field Open counter of the driver
DWORD m_dwHostContext; // @field Context of host controller driver
DWORD m_dwFuncContext; // @field Context of function controller driver
DWORD m_dwFuncBusContext; // @field context of function controller bus
BOOL bInSleepOrSM_Mode;
BOOL bInPowerHandler;
// Note: Implementation of the message queue is OS-dependent
HPROC m_hSrcProc; // @field Handle to a source process that owns the m_hMsgQ
HMSGQ m_hMsgQ; // @field Handle to opened message queue
DWORD m_dwPortNum;
DWORD m_dwPortMode;
} OTG_CORE_INFO, *POTG_CORE_INFO;
typedef struct _SEHCDPdd
{
LPVOID lpvMemoryObject;
LPVOID lpvEHCDMddObject;
PVOID pvVirtualAddress; // DMA buffers as seen by the CPU
DWORD dwPhysicalMemSize;
PHYSICAL_ADDRESS LogicalAddress; // DMA buffers as seen by the DMA controller and bus interfaces
DMA_ADAPTER_OBJECT AdapterObject;
TCHAR szDriverRegKey[MAX_PATH];
PUCHAR ioPortBase;
DWORD dwSysIntr;
CRITICAL_SECTION csPdd; // serializes access to the PDD object
HANDLE IsrHandle;
HANDLE hParentBusHandle;
} SEHCDPdd;
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;