迷死她张

个人博客

微信公众号-迷死她张 个人微信号-roon93


Linux与vxWorks的设备驱动模型


最近一直从事VxWorks的驱动开发,其与Linux下设备驱动的开发有很大的相似性。正巧最近在csdn上看到刘盼的课程有介绍驱动模型,现结合Linux和vxWorks两者的设备驱动模型在此做一个总结。


设备驱动模型


设备驱动的主要功能就是操作硬件,而操作硬件最关键的便是设备的基地址和中断号了,而世界上的板子千千万,每个板子的信息也不一样,站在驱动开发的角度来看,每次重新更换板卡时,基地址和中断号不一样,则驱动程序也需要更改为相对应的参数,如此一来,千千万的板卡就需要千千万的驱动程序,这种方式肯定是不可取的。驱动程序要做的就是以不变应万变,这个时候,设备驱动模型就显得尤其重要了。

而驱动程序想要实现不变应万变,那么CPU板级信息就应与驱动程序分离,这是我们设计的前提。但是驱动是一定要去取基地址和中断号的,而基地址和中断号又与硬件又是密切相关的,中间这层耦合关系似乎很难隔离。

最简单的办法,轮询匹配,驱动程序满世界去询问各个板卡,你的基地址是多少,中断号是多少,或者通过设定的几个预设值去匹配。

VxWorks传统驱动模型Linux2.6之前便是通过这种类似的形式去匹配的,比如pci设备的配置空间查询接口pciFindDevice,pci是有这样的一个机制,而其他板卡呢?比如与pci对应的isa,还有其他板级总线,i2c、spi呢?他们与基地址和中断号的联系更紧密,而且在不知道基地址的情形下他们无法给你回应,这个时候一般在创建设备的接口中预留这两个参数作为输入。

其实,你可以发现,这还是一个耦合的情况。

那么,我们就换一个思路,设计一个接口适配器(adapter)的类去适配不同的板级信息,基地址、中断号等硬件信息全部放到adpter里去维护,而驱动则通过adpter的接口去获取相对应的硬件信息。vxWorks6.6之后vxBus驱动模型和Linux2.6之后采用的便是这种方法,只不过不叫adpter,而叫做总线。通过总线(软件意义上的虚拟总线)去匹配设备和驱动,把设备和驱动绑定起来。

项目 功能
设备 描述硬件信息(基地址、中断号、时钟等)
驱动 完成外设的具体功能
总线 完成设备和驱动的匹配

其中Linux下项目目录如下:板级设备信息arch,驱动drivers,总线drivers/base/platform.c,vxWorks下项目目录如下:板级设备信息target/config,驱动target/src/drv,总线target/src/hwif/vxbus


设备驱动模型的实例


按照我们上面的模型,首先就是硬件信息要录入到设备端,也就是在bsp中注册硬件设备相关信息,假设一个叫做roon的设备,Linux下示例如下:

static struct resource roon_resources[] =
{
	[0] = {
		.start = ...,
		.end = ...,
		.flags = IORESOURCE_IO,
	},
};

static struct platform_device roon_platform_device =
{
	.name = "roon",
	.id = 0,
	.num_resources = ARRAY_SIZE(roon_resources),
	.resource = roon_resources,
	.dev =
	{
		.release = roon_platform_release,
		.platform_data = NULL,
	},
};

然后让设备向总线注册:

if ((ret = platform_device_register(&roon_platform_device)) < 0)
{
	platform_device_put(&roon_platform_device);
	return ret;
}

如此一来,platform总线就知道了roon这个设备的硬件相关信息,但是驱动还是不知道,那我们同样把驱动注册进总线:

static int roon_probe(struct platform_device *pdev)
{
	int ret;
	struct resource *res_io;

	/* 从设备资源获取GPIO编号 */
	res_io = platform_get_resource(pdev, IORESOURCE_IO, 0);
	rts_io = res_io->start;
	...
}

现在通过上面的处理,设备向总线注册了硬件信息,驱动也向总线注册了驱动模块,但是总线又是怎样把驱动好设备绑定起来的呢?内核中代码如下:

static int platform_match(struct device *dev, struct device_driver *drv)
{
    struct platform_device *pdev = to_platform_device(dev);
    struct platform_driver *pdrv = to_platform_driver(drv);

    /* When driver_override is set, only bind to the matching driver */
    if (pdev->driver_override)
        return !strcmp(pdev->driver_override, drv->name);

    /* Attempt an OF style match first */
    if (of_driver_match_device(dev, drv))
        return 1;

    /* Then try ACPI style match */
    if (acpi_driver_match_device(dev, drv))
        return 1;

    /* Then try to match against the id table */
    if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;

    /* fall-back to driver name match */
    return (strcmp(pdev->name, drv->name) == 0);
}

从代码中可以看出,platform总线下,设备与驱动的匹配是通过总线去匹配他们的名字来实现的,如果是相同的名字,则匹配成功。

vxWorks下也大同小异,示例如下:

const struct hcfResource roonResources[] =
{
    { "regBase", HCF_RES_INT, {(void *)ROON_BASE_ADR} },
    { "irq",     HCF_RES_INT, {(void *)(INUM_TO_IVEC(INT_NUM_ROON))} },
    { "clkFreq", HCF_RES_INT, {(void *)PCI_CLK_FREQ} },
    ...
};

#define roonNum NELEMENTS(roonResources)

struct hcfDevice hcfDeviceList[] =
{
	{ "roon", 0, VXB_BUSID_PLB, 0, roonNum, roonResources },
	...
}

通过代码不难分析,硬件设备信息已经注册到PLB总线上,同样,接下来是驱动也要注册进总线:

LOCAL void roonvxbInstInit(struct vxbDev * pDev)
{
	if (pDev->busID == VXB_BUSID_PLB)
	{
		/* get the HCF_DEVICE address */
		pHcf = hcfDeviceGet(pDev);
		...
		if (devResourceGet (pHcf, "irq", HCF_RES_INT, (void *)&val) == OK)
		pChan->irq = (UINT16) val;
		...
	}
}

LOCAL struct drvBusFuncs roonvxbFuncs =
{
  roonvxbInstInit,        /* devInstanceInit */
  roonvxbInstInit2,       /* devInstanceInit2 */
  roonvxbInstConnect      /* devConnect */
};

LOCAL struct vxbDevRegInfo roonDrvRegistration =
{
    NULL,					/* pNext */
    VXB_DEVID_DEVICE,		/* devID */
    VXB_BUSID_PLB,		/* busID = PLB */
    VXB_VER_4_0_0,		/* vxbVersion */
    "roon",				/* drvName */
    &roonvxbFuncs,		/* pDrvBusFuncs */
    NULL,					/* pMethods */
    roonProbe,			/* devProbe */
    NULL					/* pParamDefaults */
};

void vxbRoonDrvRegister(void)
{
  /* call the vxBus routine to register the driver */
  vxbDevRegister ((struct vxbDevRegInfo *) &roonDrvRegistration);
}

同理,总线匹配其实也是通过名字去搜索匹配的,增加设备只需要在hcfDeviceList数组中添加即可,匹配后,驱动与设备形成一个实例,通过vxbDevMethodGet()接口来查询系统中的每一个实例。


总结


如上面分析的,最底层是不同硬件的板级文件,中间层是内核的虚拟总线,上面才是设备的驱动程序,现在板级文件与设备驱动程序已经解耦,但是随着时代的发展,设备越来越多,如果每次设备改动都要去修改板级文件(bsp),那维护板级文件又是一个很大头的问题了。

时代在发展,设备在增多,人类的智慧也在增长,所以我们是绝对不允许这样的问题存在的,其实现在Linuxarm体系内核版本3.x后引入了原本power pc下的设备树(DTS)模型。vxWorks7.0也已经采用DTS来配置硬件设备了。关于设备树,有机会再详谈。

在软件编程当中,高内聚、低耦合一直是指导思想,所谓高内聚低耦合是模块内各元素联系越紧密就代表内聚性越高,模块间联系越不紧密就代表耦合性越低。所以高内聚、低耦合强调的就是内部要紧紧抱团。如上分析,设备和驱动就是基于这种模型去实现彼此隔离不相干的。


转载请注明原地址,迷死她张:www.roon.pro 谢谢!

作者 迷死她张

微信公众号 迷死她张

个人微信号 roon93

2018 年 06月 28日

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝/微信扫一扫,即可进行扫码打赏哦