vxBus 总线结构的分析与应用
vxWorks驱动框架目前提供两种不同的版本,分别是:VxBus 模式 和传统模式。
对于 SMP(多处理器架构)系统,vxWorks建议采用 VxBus 模式注册设备驱动。因为,VxBus 可提供统一,安全的标准接口。
VxBus 6.2 版本之前以组件形式使用;6.2 版本之后则绑定到内核中。
设备初始化分为 5 个阶段:
注册 --> 探测/匹配 --> 设备实例初始化 --> 设备实例初始化 2 --> 设备实例关联
上述 5 个阶段在系统初始流程中进行。
sysInit --> usrInit --> sysHwInit
sysHwInit --> hardWareInterFaceInit --> hardWareInterFaceBusInit
vxbDevRegister --> vxbDeviceAnnounce --> probe --> vxbDevInitRun --> devInstanceInit
sysHwInit2 --> vxbDevInit --> devInstanceConnect
驱动注册结构体:
struct vxbDevRegInfo {
struct vxbDevRegInfo *pNext; //注意:vxWorks 允许驱动中的同一个操作存在多个入口。
UINT32 devID;
UINT32 busID; //总线 ID,vxWorks 设计为总线关联多个驱动与设备,通过总线设备可匹配到正确的驱动,反之亦然。
UINT32 vxbVersion;
char drvName[MAX_DRV_NAME_LEN + 1];
struct drvBusFuncs *pDrvBusFuncs;
struct vxbDeviceMethod *pMethods;
BOOL (*devProbe)(struct vxbDev *pDevInfo); // probe 函数存在时,需确保驱动探测设备成功;probe 函数不存在,系统默认返回正常,后续设备可链接驱动
struct vxbParams *pParamDefaults;
};
/*
* vxWorks 提供了虚拟的 processor local bus,其作用类似于 Linux 中设备树所使用的 platform bus。
* vxWorks 与 Linux 相同,将总线也视为设备。
*/
void hardWareInterFaceBusInit(void)
{
vxbLibInit();
...
#ifdef INCLUDE_PLB_BUS
plbRegister(); //注册 PLB 总线
#endif
...
vxbInit();
}
//全局变量声明
LOCAL struct vxbDevRegInfo plbRegInfo =
{
NULL,
VXB_DEVID_BUSCTRL, // PLB 视为总线控制器
VXB_BUSID_PLB,
VXB_VER_5_0_0,
"plbCtrl",
&plbBusfFuncs, //提供总线操作接口
NULL,
NULL
};
struct vxbDeviceMethod *pSysPlbMethods = NULL;
/*
* PLB 总线初始化
*/
void plbRegister(void)
{
int plbMethodCount = NELEMENTS(plbMethodList);
int sysMethodCount;
struct vxbDeviceMethod *pSysMeth;
struct vxbDeviceMethod *pSysMethDest;
struct vxbDeivceMethod *pSysMethHead = NULL;
int i = 0;
if (plbBusLibInitialized) //检测 PLB 总线是否已完成注册
return;
plbInitStage = 1;
plbBusLibInitialized = TRUE;
...
vxbDevRegister(&plbRegInfo); //注册 PLB 虚拟总线驱动
(void)vxbBusTypeRegitser(&plbBusType); //注册 PLB 虚拟总线
vxbDeviceAnnounce(&plbCtrl); //以设备形式注册 PLB 虚拟总线控制器并关联驱动
(void)vxbBusAnnounce(&plbCtrl, VXB_BUSID_PLB);
...
return;
}
/*
* 注册设备驱动
*/
STATUS vxbDevRegister(struct vxbRegInfo *pDevInfo)
{
struct vxbDevRegInfo *pListEnd;
struct vxbDevRegInfo *pCurrent;
VXB_ASSERT(pDevInfo != NULL, ERROR);
if (vxbDrvVerCheck(pDevInfo) != OK)
return (ERROR);
pListEnd = pDevInfo;
while (pListEnd->pNext != NULL)
pListEnd = pListEnd->pNext;
VXB_DEBUG_MSG(2, "vxbDevRegister() regitsering new driver @ %p\n", pDevInfo, 2, 3, 4, 5, 6);
vxbLockTake(&vxbGlobalListsLock, VXB_LOCK_WRITER); /* 操作全局变量时使用读写锁,加锁 */
pListEnd->pNext = pDriverListHead; /* 注册驱动时,将现有的驱动链以头插方式插入到 pDriverListHead 全局链表中 */
pDriverListHead = pDevInfo;
vxbLockGive(&vxbGlobalListsLock, VXB_LOCK_WRITER); /* 操作全局变量时使用读写锁,解锁 */
pListEnd = pListEnd->pNext;
if (vxbInitPhase < 1) /* 如果处于 vxBus 初始化第一初始阶段,退出并返回正常 */
return OK;
VXB_DEBUG_MSG(2, "vxbDevRegister(): pDevInfo @ %p, pListEnd @ %p\n", pDevInfo, pListEnd, 3, 4, 5, 6);
pCurrent = pDevInfo;
while (pCurrent != pListEnd) {
VXB_DEBUG_MSG(2, "vxbDevRegister() checking for orphans on %s\n", vxbBusTypeString(pCurrent->busID), 2, 3, 4, 5, 6);
vxbDevIterate(vxbNewDriver, pCurrent, VXB_ITERATE_ORPHANS);
pCurrent = pCurrent->pNext;
}
return OK;
}
/*
* 注册总线
*/
STATUS vxbBusTypeRegister(struct vxbBusTypeInfo *pBusType)
{
struct vxbBusTypeInfo *pTempPtr;
VXB_ASSERT(pBusType != NULL, ERROR)
vxbLockTake(&vxbGlobalListsLock, VXB_LOCK_WRITER);
pTempPtr = pBusListHead;
if (pBusListHead != NULL) { /* 检查总线是否已经注册 */
while (pTempPtr != NULL) {
if (pTempPtr->busID == pBusType->busID) {
vxbLockGive(&vxbGlobalListsLock, VXB_LOCK_WRITER);
return ERROR;
}
pTempPtr = pTempPtr->pNext;
}
}
pBusType->pNext = pBusListHead; /* 以头插方式将总线注册到 pBusListHead 全局链表中 */
pBusListHead = pBusType;
vxbLockGive(&vxbGlobalListsLock, VXB_LOCK_WRITER);
return OK;
}
/*
* 声明设备,vxWorks 系统开始为设备匹配对应的驱动
*/
STATUS vxbDeviceAnnounce(struct vxbDev *pDev)
{
struct vxbBusTypeInfo *pBusEntry;
struct vxbBusTypeInfo *busMatch = NULL;
BOOL drvFound = FALSE;
struct vxbDevRegInfo *pDrv;
struct vxbBusPresent *pBus;
FUNCPTR pMethod;
if (pPlbDev == NULL && pDev->busID == VXB_BUSID_LOCAL) { //若注册的设备为 PLB,则直接赋值全局变量并返回
pPlbDev = pDev;
return (OK);
}
VXB_DEBUG_MSG(1, "vxbDeviceAnnounce(%p(%s))\n", pDev, pDev->pName, 3, 4, 5, 6);
if (pPlbDev != NULL) {
pMethod = vxbDevMethodGet(pPlbDev, DEVMETHOD_CALL(sysBspDevFilter));
if (pMethod != NULL) {
if ((*pMethod)(pDev) != OK) {
VXB_DEBUG_MSG(1, "vxbDeviceAnnounce(%p(%s)) excluded by BSP\n", pDev, pDev->pName, 3, 4, 5, 6);
return ERROR;
}
}
}
if (pDev->pParentBus == NULL) { /* 设备为孤立设备时,注册到 pLostDevHead 全局链表中 */
#ifdef VXB_PERFORM_SANITY_CHECKS
vxbLockTake(&vxbLostDevListLock, VXB_LOCK_WRITER);
vxbInstInsert(&pLostDevHead, pDev);
vxbLockGive(&vxbLostDevListLock, VXB_LOCK_WRITER);
#endif
return ERROR;
}
vxbLockTake(&vxbGlobalListsLock, VXB_LOCK_READER);
for (pBusEntry = pBusListHead; pBusEntry != NULL; pBusEntry = pBusEntry->pNext) { /* 遍历总线,获取设备挂载的总线对象 */
if (pBusEntry->busID != pDev->busID)
continue;
for (pDrv = pDriverListHead; pDrv != NULL; pDrv = pDrv->pNext) { /* 遍历驱动,获取设备对应的驱动 */
VXB_DEBUG_MSG(1, "vxbDeviceAnnounce(): checking %p (%s) against %s\n", pDev, pDev->pName, &pDrv->drvName[0], 4, 5, 6);
if (pDrv->busID != pDev->busID) {
VXB_DEBUG_MSG(1, "vxbDeviceAnnounce(): %s@%p failed type check\n", pDev->pName, pDev, 3, 4, 5, 6);
continue;
}
drvFound = (*(pBusEntry->vxbDevMatch))(pDrv, pDev); //执行总线匹配接口,验证设备与驱动的匹配性
if (!drvFound) {
VXB_DEBUG_MSG("vxbDeviceAnnounce(): %s@%p failed bus match\n", pDev->pName, pDev, 3, 4, 5, 6);
continue;
}
busMatch = pBusEntry;
if (pDrv->devProbe == NULL) {
VXB_DEBUG_MSG(1, "vxbDeviceAnnounce(): no driver probe available\n", 1, 2, 3, 4, 5, 6);
drvFound = TRUE;
} else {
VXB_DEBUG_MSG(1, "vxbDeviceAnnounce(): calling driver probe\n", 1, 2, 3, 4, 5, 6);
drvFound = (*(pDrv->devProbe))(pDev); //执行驱动提供的 probe 接口,需确保返回值为 TRUE;probe 为空也可。
if (drvFound == FALSE) {
VXB_DEBUG_MSG(1, "vxbDeviceAnounce(): driver probe failed\n", 1, 2, 3, 4, 5, 6);
continue;
}
}
VXB_DEBUG_MSG(1, "vxbDeviceAnnounce(): found match, driver @ %p\n", pDrv, 2, 3, 4, 5, 6);
pDev->pDriver = pDrv;
if (pDev->pName == NULL)
pDev->pName = &pDrv->drvName[0];
pBus = (struct vxbBusPresent *)pDev->pParentBus; /* 为总线创建设备的实例化对象 */
vxbLockTake(&pBus->listLock, VXB_LOCK_WRITER);
vxbInstInsert(&pBus->instList, pDev);
vxbLockGive(&pBus->listLock, VXB_LOCK_WRITER);
vxbDevInitRun(pDev, pDrv);
break;
}
}
vxbLockGive(&vxbGlobalListsLock, VXB_LOCK_READER);
if (drvFound == FALSE) {
pBus = (struct vxbBusPresent *)pDev->pParentBus;
pDev->pDriver = NULL;
vxbLockTake(&pBus->listLock, VXB_LOCK_WRITER);
vxbInstInsert(&pBus->devList, pDev);
vxbLockGive(&pBus->listLock, VXB_LOCK_WRITER);
}
return OK;
}
/*
* 总线声明
*/
STATUS vxbBusAnnounce(struct vxbDev *pBusDev, UINT32 busID)
{
struct vxbBusPresent *pBusEnt;
struct vxbBusPresent *pParent;
struct vxbBusTypeInfo *pType;
struct vxbDev *pTempDev;
pParent = pBusDev->pParentBus;
if (parent != NULL) {
vxbLockTake(&pParent->listLock, VXB_LOCK_WRITER);
if (pBusDev->pDriver != NULL)
pTempDev = pParent->instList;
else
pTempDev = pParent->devList;
while (pTempDev != NULL) {
if (pTempDev == pBusDev)
break;
pTempDev = pTempDev->pNext;
}
vxbLockGive(&pParent->listLock, VXB_LOCK_WRITER);
if (pTempDev == NULL)
return ERROR;
}
pBusEnt = (struct vxbBusPresent *)hwMemAlloc(sizeof(*pBusEnt));
if (pBusEnt == NULL)
return (ERROR);
if (pPlbBus == NULL && busID == VXB_BUSID_LOCAL) {
pPlbBus = pBusEnt;
pPlbDev->pParentBus = NULL;
}
if (vxbInitPhase > 1)
vxbLockInit(&pBusEnt->listLock);
pBusEnt->pBusType = NULL;
vxbLockTake(&vxbGlobalListsLock, VXB_LOCK_READER);
for (pType = pBusListHead; pType != NULL; pType = pType->pNext) {
if (pType->busID == busID) {
pBusEnt->pBusType = pType;
break;
}
}
vxbLockGive(&vxbGlobalListsLock, VXB_LOCK_READER);
if (pType == NULL) {
if (pPlbBus == pBusEnt)
pPlbBus = NULL;
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pBusEnt);
#endif
return ERROR;
}
pBusEnt->pCtrl = pBusDev;
pBusEnt->instList = NULL;
pBusEnt->devList = NULL;
pParent = pBusEnt->pCtrl->pParentBus;
if (pParent != NULL) {
void *pParentDevId = (void *)&pParent;
vxbLockTake(&vxbBusListLock, VXB_LOCK_WRITER);
vxbInstInsert((VXB_DEVICE_ID *)pParentDevId, (VXB_DEVICE_ID)pBusEnt);
vxbLockGive(&vxbBusListLock, VXB_LOCK_WRITER);
}
pBusDev->u.pSubordinateBus = pBusEnt;
if ((pParent == NULL) && (pPlbDev != pBusDev)) {
#ifdef VXB_PERFORM_SANITY_CHECKS
vxbLockTake(&vxbLostDevListLock, VXB_LOCK_WRITER);
pBusEnt->pNext = pLostBusHead;
pLostBusHead = pBusEnt;
vxbLockGive(&vxbLostDevListLock, VXB_LOCK_WRITER);
#endif
return ERROR;
}
return (OK);
}
// PLB 虚拟总线初始化结束,开始遍历 hcfDeviceList 数组,关联设备与驱动
STATUS vxbInit(void)
{
vxbInitPhase = 1;
plbInit1(plbDev);
return (ok);
}
STATUS plbInit1(struct vxbDev *pCtrl)
{
int i, j;
char regBase[] = "regBase0";
struct vxbDev *pDev;
HCF_DEVICE *pHcf;
UINT32 regBase32;
VXBPLB_DEBUG_MSG("plbInit1() called\n", 1, 2, 3, 4, 5, 6);
#ifdef VXB_LEGACY_ACCESS
plbAccessInit();
#endif
for (i = 0; i < hcfDeviceNum; i++) {
if (hcfDeviceList[i].busType != VXB_BUSID_PLB) {
VXBPLB_DEBUG_MSG1("plbInit1(): skipping non-PLB device %s\n", (int)hcfDeviceList[i].devName, 2, 3, 4, 5, 6);
} else {
VXBPLB_DEBUG_MSG1("plbInit1(): processing device %s\n", (int)hcfDeviceList[i].devName, 2, 3, 4, 5, 6);
pHcf = &hcfDeviceList[i];
pDev = vxbDevStructAlloc(WAIT_FOREVER);
if (pDev == NULL) {
return (ERROR);
}
pDev->busId = VXB_BUSID_PLB;
pDev->pNext = NULL;
pDev->pBusSpecificDevInfo = (void *)&hcfDeviceList[i];
#ifdef VXB_LEGACY_ACCESS
pDev->pAccess = (pVXB_ACCESS_LIST)hwMemAlloc(sizeof(VXB_ACCESS_LIST));
if (pDev->pAccess == NULL) {
vxbDevStructFree(pDev);
return (ERROR);
}
vxbPlbAccessCopy(pDev->pAccess);
#endif
if (devResourceGet(pHcf, "regBase", HCF_RES_INT, (void *)®Base32) == OK)
pDev->pRegBase[0] = (void *)((ULONG)regBase32);
else
(void)devResourceGet(pHcf, "regBase", HCF_RES_ADDR, (void *)&(pDev->pRegBase[0]));
for (j = 0; j < VXB_MAXBARS; j++) {
regBase[7] = (char)('0' + j);
if (devResourceGet(pHcf, regBase, HCF_RES_INT, (void *)®Base32) == OK)
pDev->pRegBase[j] = (void *)((ULONG)regBase32);
else
devResourceGet(pHcf, regBase, HCF_RES_ADDR, (void *)&(pDev->pRegBase[j]));
...
}
...
vxbDeviceAnnounce(pDev);
}
}
}
/*
* PLB 虚拟总线提供的设备与驱动匹配接口
*/
LOCAL BOOL plbDevMatch(struct vxbDevRegInfo *pDriver, struct vxbDev *pDev)
{
VXPLB_DEBUG_MSG1("plbDevMatch((0x%08x, %s), (0x%08x, 0x%08x)) called\n",
(int)pDriver, (int)pDriver->drvName,
(int)pDev, (int)pDev->pRegBase[0],
4, 5);
if (pDev->busID != VXB_BUSID_PLB) { /* 检查设备总线是否为 plb */
if (plbInitStage > 2)
VXBPLB_DEBUG_MSG();
return (FALSE);
}
if (strcmp(pDriver->drvName, pDev->pName) != 0) { /* 检查驱动名称是否与设备名称一致 */
if (plbInitStage > 2)
VXBPLB_DEBUG_MSG
return (FALSE);
}
VXBPLB_DEBUG_MSG1("\tcheck OK, dev=%s drv=%s\n", (int)pDev->pName, (int)pDriver->drvName, 3, 4, 5, 6);
return (TRUE);
}
vxBus 的使用如下:
LOCAL struct drvBusFuncs xxxDrvFuncs =
{
...
};
LOCAL struct vxbDeviceMethod xxxDrv_methods[] =
{
...
};
LOCAL struct vxbDevRegInfo xxxDrvRegistration =
{
...,
xxxDrvFuncs,
...
};
void xxxDrvRegister (void) {
vxbDevRegister(xxxDrvRegistration);
}
/* 注意事项 */
LOCAL struct vxbDev xxxDev =
{
...
};
/*
* 接口与 hcfDeviceList 全局变量相关,如果在 hcfDeviceList 变量中修改,则确保驱动注册在 PLB 初始化之前即可。否则,需进行特殊处理并进行调用。
*/
vxbDevAnnounce(&xxxDev);