资源描述
Vxworks usb mouse 驱动分析
概述:
最近在做嵌入式vxworks下usb触摸屏设计,参考了vxworks里面的usb mouse驱动的代码,分析了usb mouse驱动,根据自己的理解,写这个文档。如有任何问题请联系yxj_5421@,转载请标明出处。
系统资源:
Vxworks6.7
1、USB主机驱动栈模型原理
USB主机驱动栈模型下图所示:
1.1硬件层
在USB主机驱动栈的最底部是USB主控制器(USB HC,即USB Host Controller)。目前,USB主控制器可分为三类:通用型主控制器接口(UHCI)、开放型主控制器接口(OHCI)和增强型主控制接口和增强型主控制接口(EHCI)。其中,UHCI和OHCl支持USBl.1低速接口,EHCl支持USB2.0高速接口。
1.2主控制器驱动层
对予每一类型的主控制器都有一个与硬件独立的USB主控制器驱动(Host Controller Driver,简称 HCD)。WindRiver提供了两个驱动:usbHedUheiLib (UHCI主控制器库)和usbHcdohciLib(OHCl主控制器库)。该模块向下直接与USB主控制器硬件进行交互操作,向上与USBD层的功能接口,提供各种功能函数的调用,支持对上层模块封装具体的HCD驱动的实现。
1.3 USB核心驱动层
USB核心驱动(USB host driver,简称USBD)模块实现USB的核心驱动,包括USB总线的枚举、总线带宽分配、传输控制等操作。该模块向下调用HCD接口模块,实现与HCD层的通信,将设备驱动的功能请求转化为相应功能模块的调用。USBD管理每一个与主机相连的USB设备,还负责自动处理USB电力管理和带宽管理及USB hub和设备的动态插拔。
1.4 USB设备驱动层
USB设备驱动层负责管理连接到USB上的不同类型的设备,它们依靠USBD来提供与每个设备的通信路径,主要实现USB设备的一些特定初始化工作,并将上层用户应用程序的请求转化为对相应的USBD驱动程序的调用。该层通过对应用层提供API函数,从而屏蔽USB实现的细节。
1.5应用层
应用层为用户编写的对USB设备进行读、写控制等指定功能的应用程序,应用层通过设备驱动程序和USB主机驱动程序完成指定的功能。
2、USB mouse 驱动分析
Usb mouse驱动属于设备驱动层,在vxworks里面,把usb mouse驱动设计成两层,即文件层+驱动层,文件层对上提供read、write、ioctl等接口,对下调用驱动层提供函数进行硬件初始化,对应的源代码是usrUsbMseInit.c (target\config\comps\src)。驱动层则针对具体usb设备初始化,获取数据等。
2.1 文件层usrUsbMseInit.c分析
在usrRoot (usrConfig.c (target\config\all)) 调用
#ifdef INCLUDE_USB_MOUSE_INIT
usrUsbMseInit (); /* Mouse Driver Initialization */
#endif
usrUsbMseInit (usrUsbMseInit.c (target\config\comps\src))驱动程序入口点,
首先检查driver是否已经安装,如果安装就退出
if (usbMseDrvNum > 0)
{
printf ("Mouse already initilaized.\n");
return ERROR;
}
接着调用
usbMseDrvNum = iosDrvInstall ((FUNCPTR) NULL,
(FUNCPTR) usbMseDevDelete,
(FUNCPTR) usbMseOpen,
(FUNCPTR) usbMseClose,
(FUNCPTR) usbMseRead,
(FUNCPTR) usbMseWrite,
(FUNCPTR) usbMseIoctl);
安装驱动的I/O函数,添加驱动到驱动表里面
接着调用驱动层初始化函数
if (usbMouseDevInit () == OK) usbMouseLib.c (target\src\drv\usb)
{
printf ("usbMouseDevInit() returned OK\n");
这个函数如果成功,会输出打印上面的信息表明驱动已经加载,并且和usb设备已经匹配上。
接着调用驱动层动态注册函数
if (usbMouseDynamicAttachRegister (usbMseDrvAttachCallback, (void *) NULL) != OK) usbMouseLib.c (target\src\drv\usb)
这个函数会把usbMseDrvAttachCallback保存在驱动层创建的一个队列里面,当usb设备热插拔时,都会调用这个函数。
如果已经插入了usb mouse,就会马上调用usbMseDrvAttachCallback,参数为USB_MSE_ATTACH, 如果没有插入,那么驱动加载已经完成了。
看usbMseDrvAttachCallback (usrUsbMseInit.c (target\config\comps\src))
因为第一次参数是USB_MSE_ATTACH,就会进入if (attachCode == USB_MSE_ATTACH)这个分支:
首先调用驱动层的if (usbMouseSioChanLock (pChan) != OK),标记SIO_CHAN structure已经在使用
接着调用
sprintf (mseName, "%s%d", USB_MSE_NAME, mseCount);
if (usbMseDevCreate (mseName, pChan) != OK)
创建设备文件,即创建"/usbMo/X",X由mseCount决定,同时创建文件层设备结构,并放在链表里面,调用iosDevAdd将设备添加到系统设备列表里面
接着调用
if (usbMseDevFind (pChan, &pUsbMseDev) != OK)
{
printf("usbMseDevFind() returned ERROR\n");
return;
}
根据pchan在链表里面找到这个usb设备的文件层设备结构
接着调用
if ((*pChan->pDrvFuncs->callbackInstall)
(pChan,
SIO_CALLBACK_PUT_MOUSE_REPORT,
(STATUS (*) (void *, ...)) usbMseRptCallback,
(void *) pUsbMseDev)
!= OK)
pChan->pDrvFuncs->callbackInstall是usbMouseLib.c里面的usbMouseCallbackInstall函数,它安装回调函数,当usb接受到数据后就会调用usbMseRptCallback这个函数。
如果上面都执行成功,会输出打印下面信息
printf("USB Mouse attached as %s\n", mseName);
至此,驱动加载完成,并且设备文件也已经创建好了。
2.2 驱动层usbMouseLib.c分析
在文件层usrUsbMseInit会调用
usbMouseDevInit----驱动层初始化入口
if (initCount == 0) 表明还没有进行初始化,初始化内部结构体,并链接在usbd上
{
if (usbdClientRegister (MSE_CLIENT_NAME, &usbdHandle) != OK ||
usbdDynamicAttachRegister (usbdHandle, USB_CLASS_HID,
USB_SUBCLASS_HID_BOOT, USB_PROTOCOL_HID_BOOT_MOUSE,FALSE,
usbMouseAttachCallback) != OK)
{
return doShutdown (S_usbMouseLib_USBD_FAULT);
}
}
usbdClientRegister (usbTransUnitInit.c (target\src\usb) )生成usb设备驱动的usbdHandle
if ( !usbtuInitCount)
{
/* usbdInitialize() not called */
USBTU_LOG("usbdClientRegister returns ERROR:usbdInitialize not called\n");
return ERROR;
}
检查usbd是否初始化了,如果没有就出错退出。
/* allocate structure for client */
if ( !(pDriver = OSS_CALLOC ( sizeof (USBTU_DEVICE_DRIVER))))
{
USBTU_LOG ( "usbdClientRegister returns ERROR : malloc failed \n");
return ERROR;
}
创建client driver结构体
if (pClientHandle != NULL)
*pClientHandle = pDriver;
将client driver赋给usbdHandle
usbdDynamicAttachRegister (usbTransUnitInit.c (target\src\usb) )填充usb设备驱动的usbdHandle,并且注册usbMouseAttachCallback函数,当有热插拔发生时,会调用这个函数,并且将usb设备client注册到usbd上。
这三个参数USB_CLASS_HID,USB_SUBCLASS_HID_BOOT, USB_PROTOCOL_HID_BOOT_MOUSE是类代码、子类代码和协议代码,会被填充在usbdHandle上,后面usb驱动就会根据这三个参数把驱动和相应的设备联系起来。
pDriverData->bFlagVendorSpecific = vendorSpecific;
pDriverData->uVendorIDorClass = deviceClass;
pDriverData->uProductIDorSubClass = deviceSubClass;
pDriverData->uBCDUSBorProtocol = deviceProtocol;
pDriverData->addDevice = usbtuInitDeviceAdd;
pDriverData->removeDevice = usbtuInitDeviceRemove;
pDriverData->suspendDevice = usbtuInitDeviceSuspend;
pDriverData->resumeDevice = usbtuInitDeviceResume;
我在网上看到好多人不想用这三个参数来识别usb硬件设备,想用vid和pid来设备,那也可以,秘密就在bFlagVendorSpecific这个标志上,上面函数调用时传入的vendorSpecific参数为false,意思就是不用vid和pid,如果传入是TRUE,就是要用vid和pid来识别,看看uVendorIDorClass和uProductIDorSubClass名字就知道意思了,可能是vid或者类代码,另一个可能是pid或者子类代码,都是由bFlagVendorSpecific决定
接着调用usbHstDriverRegister (Usbd.c (target\src\hwif\usb))将usb设备client注册到usbd上
if ((NULL == pDeviceDriverInfo) ||
(NULL == pDeviceDriverInfo->addDevice) ||
(NULL == pDeviceDriverInfo->removeDevice) ||
(NULL == pDeviceDriverInfo->resumeDevice) ||
(NULL == pDeviceDriverInfo->suspendDevice))
{
OS_LOG_MESSAGE_MEDIUM(
USBD,
"usbHstRegisterDriver() Failed: Invalid parameter Driver info.\n",
0,
0,
0,
0);
return USBHST_INVALID_PARAMETER;
}
判断pDeviceDriverInfo里面一些处理函数是否存在,如果没有就出错返回,从上面看到调用这个函数的函数已经把这些变量赋值了。
下面是一段处理usb hub的代码,就不分析了
/* Store the global driver list in pOldDriverList*/
pOldDriverList = gpDeviceDriverList;
while (NULL != pOldDriverList)
{
/*
* Check if the bFlagVendorSpecific, VendorIDorClass,
* uProductIDorSubClass are same.
*/
if ((pOldDriverList->pDriver->bFlagVendorSpecific ==
pDeviceDriverInfo->bFlagVendorSpecific) &&
(pOldDriverList->pDriver->uVendorIDorClass ==
pDeviceDriverInfo->uVendorIDorClass) &&
(pOldDriverList->pDriver->uProductIDorSubClass ==
pDeviceDriverInfo->uProductIDorSubClass) &&
(pOldDriverList->pDriver->uBCDUSBorProtocol ==
pDeviceDriverInfo->uBCDUSBorProtocol))
{
OS_LOG_MESSAGE_HIGH(
USBD,
"usbHstRegisterDriver() Failed: Driver is already registered.\n",
0,
0,
0,
0);
return USBHST_FAILURE;
}
/* Get the driver in the list */
pOldDriverList = pOldDriverList->pNextDriver;
}
遍历整个gpDeviceDriverList 链表,检查pDeviceDriverInfo是否已经存在,如果存在就错误返回。
pNewDriverList =
(pUSBD_DEVICE_DRIVER_LIST)OS_MALLOC(sizeof(USBD_DEVICE_DRIVER_LIST));
如果不存在,新创建一个pNewDriverList
pNewDriverList->pDriver = pDeviceDriverInfo;
将pDeviceDriverInfo放在pNewDriverList里面
/* make pVxbDriverInfo point to the pDeviceDriverInfo :: vxbDriverInfo */
pVxbDriverInfo = &(pDeviceDriverInfo->vxbDriverInfo);
memset (pVxbDriverInfo, 0, sizeof (DRIVER_REGISTRATION));
/* populate struct vxbDevRegInfo. The devId should be VXB_DEVID_BUSCTRL for
* hub driver and VXB_DEVID_DEVICE for all other class drivers.
* The vxbDevRegInfo :: drvName should be determined for the class code
*/
/* parent bus ID. Should always be hub bus type. */
pVxbDriverInfo->busID = VXB_BUSID_USB_HUB;
if (USBHST_HUB_CLASS == pDeviceDriverInfo->uVendorIDorClass)
pVxbDriverInfo->devID = VXB_DEVID_BUSCTRL;
else
pVxbDriverInfo->devID = VXB_DEVID_DEVICE;
/* version number */
pVxbDriverInfo->vxbVersion = VXB_VER_4_0_0;
/* driver name. The name of the driver should not be greater than
* MAX_DRV_NAME_LEN
*/
for (size = 0; pDrvName[size]!= '\0'; size++)
pVxbDriverInfo->drvName[size] = pDrvName[size];
pVxbDriverInfo->drvName[size] = '\0';
/* function pointer to add device */
pVxbDriverInfo->pDrvBusFuncs = &usbVxbDeviceDriverInitFuncs;
/* methods */
pVxbDriverInfo->pMethods = &usbVxbDeviceDriverMethods[0];
填充pVxbDriverInfo
/* register the driver with vxBus */
if (vxbDevRegister (pVxbDriverInfo) == ERROR)
{
OS_LOG_MESSAGE_HIGH(
USBD,
"usbHstRegisterDriver() Failed: vxBus Registration failed\n",0,0,
0,0);
/* free allocated memory */
OS_FREE (pNewDriverList);
return USBHST_FAILURE;
}
调用vxbDevRegister将usb驱动注册在vxBus上
pNewDriverList->pNextDriver = gpDeviceDriverList;
/* Assign the new driver to global driver list */
gpDeviceDriverList = pNewDriverList;
将pNewDriverList放在gpDeviceDriverList链表的头部。
至此usbdDynamicAttachRegister执行完成,返回到usbMouseDevInit里面,这个函数也执行完成。驱动加载完成。
再看vxbDevRegister(vxBus.c (target\src\hwif\vxbus))函数调用
if ( vxbDrvVerCheck(pDevInfo) != OK )
return(ERROR);
先检查vxbus drv版本,vxbus drv版本上面填充VXB_VER_4_0_0
pListEnd->pNext = pDriverListHead;
pDriverListHead = pDevInfo;
将pDevInfo放在链表头部
/* Check existing devices, to see if this driver matches */
pCurrent = pDevInfo;
while ( pCurrent != pListEnd )
{
VXB_DEBUG_MSG(2,"vxbDevRegister() checking for orphans on %s\n",
(int)vxbBusTypeString(pCurrent->busID), 2,3,4,5,6);
vxbDevIterate(vxbNewDriver, pCurrent, VXB_ITERATE_ORPHANS);
pCurrent = pCurrent->pNext;
}
这里就检查存在的usb device,看看这个驱动是否匹配。
看vxbDevIterate函数
pBusPres = pPlbBus;
/* start with PLB controller */
(*func)(pBusPres->pCtlr, pArg);
Func就是vxbNewDriver,pArg就是上面的pDevInfo,而pBusPres->pCtlr就是vxbus上usb设备的信息结构体
看vxbNewDriver函数
先检查dev和drv一些相关的变量
/* get bus type of current device */
pBusEntry = pDev->pParentBus->pBusType;
/* check bus-specific match routine */
drvFound = (*(pBusEntry->vxbDevMatch))(pDriver, pDev);
if ( drvFound == FALSE )
return(ERROR);
调用vxbus上的dev匹配函数去匹配dev和drv,vxbDevMatch 这个是vxbus注册时用usbVxbRegHubBusType结构体填充的,这个函数就是usbdFindDriver,如果匹配不成功,就停止匹配,驱动返回。
看usbdFindDriver函数
pUSBD_DEVICE_INFO pUSBDeviceInfo = (pUSBD_DEVICE_INFO)
pDev->pBusSpecificDevInfo;
/* extract the device information from vxBus */
/* device information */
pUSBHST_DEVICE_DRIVER pDriverInfo = (pUSBHST_DEVICE_DRIVER)pDriver;
得到pUSBD_DEVICE_INFO结构体,这是usb设备信息,这个结构体在usbHstDeviceNew里面创建,并且赋值。
/*
* Check if the device has multiple configurations and is vendor
* specific. If so, return error
*/
if ((1 < pUSBDeviceInfo->uNumConfigurations) &&
(USBHST_VENDOR_SPECIFIC == pUSBDeviceInfo->uDeviceClass))
{
OS_LOG_MESSAGE_HIGH(
USBD,
"usbdFindDriver() Failed: Vendor specific \
device with multiple configurations not \
supported.\n",
0,
0,
0,
0);
return FALSE;
}
检查如果这个设备有多个配置,但是又指明vid,这是不支持的,出错返回。
if (USBHST_VENDOR_SPECIFIC == pUSBDeviceInfo->uDeviceClass)
{
if (0 == pUSBDeviceInfo->uVendorID)
{
OS_LOG_MESSAGE_MEDIUM(
USBD,
"usbdFindDriver() Failed: Invalid parameter Device Info.\n",
0, 0, 0, 0);
return FALSE;
}
/* Find the matching driver for the device */
if (usbdMatchDriver (TRUE,
pUSBDeviceInfo->uVendorID,
pUSBDeviceInfo->uDeviceID,
pUSBDeviceInfo->uBCDDevice,
pDriverInfo) == TRUE)
{
/* Store the driver info in the device info*/
pUSBDeviceInfo->pDriver = pDriverInfo;
return TRUE;
}
}
if (0 != pUSBDeviceInfo->uDeviceClass)
{
/* Find the matching driver for the device */
if (usbdMatchDriver (FALSE,
pUSBDeviceInfo->uDeviceClass,
pUSBDeviceInfo->uDeviceSubClass,
pUSBDeviceInfo->uDeviceProtocol,
pDriverInfo) == TRUE)
{
/* Store the driver info in the device info*/
pUSBDeviceInfo->pDriver = pDriverInfo;
return TRUE;
}
}
如果usb 设备类代码是0xff,就必须有vid,如果vid为0,出错返回,如果不为0,调用usbdMatchDriver用设备的vid和pid去匹配driver,driver也得提供vid和pid,如果类代码不是0xff,也不为0,调用usbdMatchDriver用设备的类代码和子类代码,协议代码去匹配driver,driver也得提供相应的代码,如果类代码等于0,接着判断interface描述符的类代码是否为0xff,如果是0xff,调用usbdMatchDriver用设备的vid和pid去匹配driver,driver也得提供vid和pid,如果不是0xff,调用usbdMatchDriver用设备的类代码和子类代码,协议代码去匹配driver,driver也得提供相应的代码。
前面讲到有网友不想用类代码来进行设备和驱动的匹配,那就要满足两个条件:
usb硬件满足类代码为0xff,vid不为0,或者类代码为0,接口描述符类代码为0xff,vid不为0
usb驱动调用usbdDynamicAttachRegister时,第五个参数为true,第二个参数为usb硬件的vid,第三个参数为usb硬件的pid
看usbdMatchDriver
这个函数就是根据dev和drv的类代码或者id进行匹配,如果匹配成功,就返回true,同时把drv结构体放在pUSBDeviceInfo->pDriver或者pInterfaceInfo->pDriver里面。
if ( pDriver->devProbe == NULL )
{
drvFound = TRUE;
}
else
{
drvFound = (*(pDriver->devProbe))(pDev);
if ( drvFound == FALSE )
return(ERROR);
}
执行drv的probe函数,这也是个空函数
/* attach driver */
pDev->pDriver = pDriver;
/* adjust name */
pDev->pName = &pDriver->drvName[0];
把drv赋给dev
/* perform initialization */
vxbDevInitRun(pDev, pDriver);
执行usb dev的初始化
看vxbDevInitRun函数
/* first pass */
if (!(devID->flags & VXB_INST_INIT_DONE))
{
if ( pDrv->pDrvBusFuncs->devInstanceInit != NULL )
(*(pDrv->pDrvBusFuncs->devInstanceInit))(devID);
devID->flags |= VXB_INST_INIT_DONE;
}
/* second pass */
if (vxbInitPhase >= 2 && !(devID->flags & VXB_INST_INIT2_DONE))
{
if ( pDrv->pD
展开阅读全文