设备模型六(bus, device, driver)

  • 前言
    前面讨论已经知道, kset, kobject相当于c++中的基类,会有上层容器去继承他们,c语言里面的继承也就是包含其数据结构的意思,而bus(总线), device(设备),驱动(驱动), 他们三个壮汉继承了他们,从而引出了设备模型的高级部分,这里我们要搞明白以下几点
    1.bus, device, driver的数据结构
    2.三者间的关系
    3.device与driver间的绑定
    开搞!

  • 1.bus, device, driver的数据结构

  • 1.1感性的认识什么是bus,device, driver
    bus,即总线, 根据<<设备模型一>>文章内容叙述,总线可以是真实总线的抽象,如i2c,也可以是非真实 的总线,为了一致统一, 非真实统一也要跟真实总线一样被抽象,即虚拟总线
    即 i2c(真实总线)---->抽象—>i2c_bus_type
    虚拟总线—>抽象—>platform
    所谓抽象,意思就是说把真实的总线(或虚拟总线)描述成一个数据结构
    对于加入系统中的硬件或者驱动(写代码时会把硬件或者驱动所依赖的总线提前写好), 都会挂靠到总线上
    那什么是挂靠到总线上呢?说白了就是建立三者之间的联系,比如device的数据结构中有一个指针指向bus跟driver,同理driver与bus也是一样的

  • 1.2 sysfs上的体现

  • 1.2.1在谈sysfs
    sysfs文件系统, 根据前面文章所讲的,体现给用户bus,device,driver的关系层(一目了然)
    大家都知道kset, kobject这些组成上层容器(bus, device, driver)的这些基类,他们的存在就是为了使这三者之间建立联系,并且通过sysfs向用户反映他们之间的联系,打个比方比如我插入Upan,内核检测到U盘插入后会调用device_add来添加设备,这里device_add会调用kobj_add会建立关系,所以你看。。

  • 1.2.2 bus的由来
    内核初始化的时候:
    start_kernel–>kernel_init(启动的一个内核线程)–>do_basic_setup–>driver_init
    在driver_init中:

	devices_init();
	buses_init();
	classes_init();

好的,上面就是这三座大山的初始化, 注意,这里还有个class_init,关于class的讲解,在下一篇文章会讨论
这里简单看一下devices_init()

	devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
	if (!devices_kset)
		return -ENOMEM;
	dev_kobj = kobject_create_and_add("dev", NULL);
	if (!dev_kobj)
		goto dev_kobj_err;
	sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
	if (!sysfs_dev_block_kobj)
		goto block_kobj_err;
	sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
	if (!sysfs_dev_char_kobj)
		goto char_kobj_err;

这里不做过多解释,如果前面的文章都看了的话,相信这里的代码还是比较简单的
对于kset_create_and_add函数的作用前面有讲解:
三个init函数执行完之后,sysfs上的关系如下图所示:
这里写图片描述
摁,设备模型的骨架子基本上是出来了,之后我们看一下bus的数据结构

  • 1.2.3 bus_type
    上面讲到,在linux中真实总线与虚拟总线会被抽象出数据结构,即bus_type,下图会讲述一下bus_type中比较重要的参数
    这里写图片描述
    这里说一下bus_type中的match函数,对于每一个总线对应的match函数不同,比较简单的是platform总线
    platform_bus_init–>bus_register(&platform_bus_type);
    其中platform_bus_type
struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match, //总线的match函数
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};

platform_match函数如下

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);

	/* Attempt an OF style match first */
	if (of_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); //这里比较dev与drv的名字是否相等
}

上图中,重要的数据结构以及其作用已经很好的表达出来,接下来讨论下注册一个总线时都发生了什么
其实注册一个总线就是形成创建一个上图的关系网

  • 1.2.4注册一个总线(虽然现在没有注册总线的机会,但是有必要小研究一下)→bus_register
    我们采用这个方式,从代码中表示生成的步骤,之后用图来描述代码的步骤
    bus_register(&platform_bus_type):
int bus_register(struct bus_type *bus)
{
	int retval;
	struct subsys_private *priv;

	priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); //1.malloc ksubsys_private结构 
	if (!priv)
		return -ENOMEM;

	priv->bus = bus; //2.确立关系
	bus->p = priv;  //2.与bus确立暧昧关系

	BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);//3.设置obj名字之后会在sysfs显示,因为sysfs显示的是obj的名字,所以用的bus->name,如上面显示, bus->name为platform
	if (retval)
		goto out;

	priv->subsys.kobj.kset = bus_kset;//4.确立与/sys/bus的关系
	priv->subsys.kobj.ktype = &bus_ktype;
	priv->drivers_autoprobe = 1;//5.为了加设备后自动添加驱动用

	retval = kset_register(&priv->subsys); //6.前面的准备做好后,注册近kobj机制中
	if (retval)
		goto out;

	retval = bus_create_file(bus, &bus_attr_uevent);//7.在/sys/bus/platform生成文件 uevent
	if (retval)
		goto bus_uevent_fail;

	priv->devices_kset = kset_create_and_add("devices", NULL,
						 &priv->subsys.kobj); //8.创建devices
	if (!priv->devices_kset) {
		retval = -ENOMEM;
		goto bus_devices_fail;
	}

	priv->drivers_kset = kset_create_and_add("drivers", NULL,
						 &priv->subsys.kobj);//9.创建 drivers
	if (!priv->drivers_kset) {
		retval = -ENOMEM;
		goto bus_drivers_fail;
	}

	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
	klist_init(&priv->klist_drivers, NULL, NULL);

	retval = add_probe_files(bus);//10.创建drivers_probe, drivers_autoprobe文件
	if (retval)
		goto bus_probe_files_fail;

	retval = bus_add_attrs(bus);//11.如果bus中还定义了其他的属性文件,则创建
	if (retval)
		goto bus_attrs_fail;

	pr_debug("bus: '%s': registered\n", bus->name);
	return 0;

bus_attrs_fail:
	remove_probe_files(bus);
bus_probe_files_fail:
	kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
	kset_unregister(bus->p->devices_kset);
bus_devices_fail:
	bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
	kset_unregister(&bus->p->subsys);
out:
	kfree(bus->p);
	bus->p = NULL;
	return retval;
}

10来个步骤,还是比较清晰,下面跟一个图。。。(本人喜欢用图来说明问题,理清思路)
这里写图片描述
好了,对于bus的注册就讲完了, 下面开讲device_add这个函数,即,往总线添加一个设备都干嘛了,对于加入一个驱动的话同加入设备大同小异了,就不用再细说,老规矩,先分析代码,后根据上面的图进行延伸

  • 1.2.5注册设备(device_add)
    当然不同的平台对应不同的device结构,但是里面都会嵌入一个struct device结构
    这里我们以platform_device为例子,给出数据结构,呜哈哈
    这里写图片描述
    上图中 dev结构体里面的platform_data是特殊针对,下面我们看一下如何注册一个platform device
static struct platform_device leds_gpio = {
	.name	= "xxx_platDev_ma", //定义设备名字
	.id	= -1, //id初始化
	.dev	= {
		.platform_data	= NULL,//啥都不说了,dev的一个变量
	},
};

调用函数platform_device_register(&leds_gpio);
接下来,厉害了!重点介绍一下platform注册都发生了什么,想想都让人兴奋,GO!
当然,不能涉及到所有的细节,我们还是强调脉络的分析
这里写图片描述
大致的脉络已经体现出来了,至于其他的什么链接文件什么的,个人认为不是什么重点,所以在下面会从现象分析,东西实在太多,接下来我们还是跟据图还说明问题,形象的从图的角度看看都发生了什么,首先,来一个简单的例子

static struct platform_device leds_gpio = {
	.name	= "xx_platDev_ma",
	.id	= -1,
	.dev	= {
		.platform_data	= NULL
	},
};
struct class *ma_class = NULL;
struct device *dev1;
struct device *dev2;
struct device *dev3;
struct device *dev4;

static int ma_class_init(void)
{
	int err;
 	printk(KERN_ALERT "class_init\n");

	err = platform_device_register(&leds_gpio); //非常简单,就是注册一个名字为xx_platDev_ma的platform设备
	return 0;
}

static void ma_class_exit(void)
{
	printk(KERN_ALERT "Goodbye, class_init\n");
	platform_device_unregister(&leds_gpio);
}

module_init(ma_class_init);
module_exit(ma_class_exit);

我们结合上面的代码逻辑,给出小例子的图解分析
这里写图片描述
我们将例子insmod后,出现
这里写图片描述
我们看到/sys下的结构,跟上面分析的一致,摁。。基本上加入一个设备的分析到位了
还差个驱动的绑定,还的继续努力!
1.2.6驱动的绑定,关于这个方面,留个悬念,这里不想说的太多,老生长谈,大家有兴趣可以看下代码,对应的接口函数是bus_probe_device
在上面的1.2.3 中的图已经很清晰的介绍了绑定的流程顺序

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值