platform驱动架构初探
platform总线是Linux2.6引入的虚拟总线,这类总线没有对应的硬件结构。与之相反,USB总线和PCI总线在内核中是有对应的bus(USB-bus和PCI-bus)的。为了统一管理CPU这些既不属于USB又不属于PCI总线的外设资源,采用了platform虚拟总线。和字符设备不同,在platform架构中,整个驱动分为了device和driver两部分,提高了系统的可移植性。
在学习platform架构时,我们可以借助一点面向对象的思想,注意关注一些重要的结构体,将属性和行为分开学习,再联系起来。
本文所有代码基于linux3.9.5
platform总线驱动架构概览
可以分为如下三层:
- 设备struct platform_device : 资源分配
- 驱动struct platform_driver :初始化
- 总线struct platform_bus :device和driver的匹配,管理
linux内核启动流程和platform总线的注册
kernel在进入C语言阶段,会进入start_kernel函数(init/main.c),进行一些内存管理,调度。该函数的最后会执行rest_init();
下面是rest_init(init/main.c)源码
static noinline void __init_refok rest_init(void)
{
	int pid;
	rcu_scheduler_starting();
	/*
	 * We need to spawn init first so that it obtains pid 1, however
	 * the init task will end up wanting to create kthreads, which, if
	 * we schedule it before we create kthreadd, will OOPS.
	 */
	kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
	numa_default_policy();
	pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
	rcu_read_lock();
	kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
	rcu_read_unlock();
	complete(&kthreadd_done);
	/*
	 * The boot idle thread must execute schedule()
	 * at least once to get things moving:
	 */
	init_idle_bootup_task(current);
	schedule_preempt_disabled();
	/* Call into cpu_idle with preempt disabled */
	cpu_idle();
}
我们可以看到,该函数做了三件事:
- 首先创建了一个线程执行kernel_init函数,该函数读取根文件系统下的init程序。这个操作完成了从内核态到用户态的转变。init进程作为所以用户态进程的父进程,将永远存在,PID是1
- kthreadd是一个守护进程,PID是2
- idle是空闲进程,cpu空闲时启动
我们进入kernel_init函数,在进入其中的kernel_init_freeable函数,继续进入do_basic_setup函数,这里我们就可以看到对驱动的初始化函数driver_init();
driver_init函数中,倒数第三个执行的函数platform_bus_init就是我们想找的platform总线的注册函数,位于drivers/base/platform.c
int __init platform_bus_init(void)
{
	int error;
	early_platform_cleanup();
	error = device_register(&platform_bus);
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);
	if (error)
		device_unregister(&platform_bus);
	return error;
}
除了最后的platform_bus_init位于drivers/base,其余函数都位于init/main.c中。下面我们将注意力从内核启动转移到platform设备上。
platform架构总线
platform_bus是一种设备
struct device platform_bus = {
	.init_name	= "platform",
};
从上面的结构体我们可以看到,platform_bus是一个名字为“platform”的device。device结构体是内核中设备的基本结构体。其他是设备,例如USB,都和device有关。这些设备的结构体或包含device成员,或实现device的部分成员。C中没有面向对象的继承特性,所以通过这种方式,我们变相的实现了“继承”
platform_bus_type实现总线的管理
struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_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 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的device和driver名字相比较,相同则返回True表示匹配。
总线注册
上文linux启动过程中已经分析,不赘述
int __init platform_bus_init(void)
{
	int error;
	early_platform_cleanup();
	error = device_register(&platform_bus);
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);
	if (error)
		device_unregister(&platform_bus);
	return error;
}
platform设备
platform_device结构体
这里我们要关注下platform_device结构体,位于include/linux/platform_device.h
struct platform_device {
	const char	* name;
	int		id;
	bool		id_auto;
	struct device	dev;
	u32		num_resources;
	struct resource	* resource;
	const struct platform_device_id	*id_entry;
	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;
	/* arch specific additions */
	struct pdev_archdata	archdata;
};
platform_device 封装了device。
- name:设备的名称
- dev:真正有用的设备,通过contain_of,能找到整个platform_device
- num_resources, resource: 系统使用的资源。Linux系统资源包括IO,寄存器,DMA,Bus,Memory等。
设备的注册和卸载
int platform_device_register(struct platform_device *pdev)
{
	device_initialize(&pdev->dev);
	arch_setup_pdev_archdata(pdev);
	return platform_device_add(pdev);
}
void platform_device_unregister(struct platform_device *pdev)
{
	platform_device_del(pdev);
	platform_device_put(pdev);
}
platform driver
platform_driver结构体
struct platform_driver {
	int (*probe)(struct platform_device *);
	int (*remove)(struct platform_device *);
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*resume)(struct platform_device *);
	struct device_driver driver;
	const struct platform_device_id *id_table;
};
由device_driver结构体封装而来
struct device_driver {
	const char		*name;
	struct bus_type		*bus;
	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */
	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */
	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_table;
	int (*probe) (struct device *dev);
	int (*remove) (struct device *dev);
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
	const struct attribute_group **groups;
	const struct dev_pm_ops *pm;
	struct driver_private *p;
};
- probe:将driver绑定到device上调用该函数
- remove:系统卸载设备的时候,将driver和device解绑
- shutdown:关机时使设备静默
- suspend:使设备进入睡眠模式
- resume: 唤醒设备
platform驱动架构初探的更多相关文章
- 云原生时代, Kubernetes 多集群架构初探
		为什么我们需要多集群? 近年来,多集群架构已经成为“老生常谈”.我们喜欢高可用,喜欢异地多可用区,而多集群架构天生就具备了这样的能力.另一方面我们也希望通过多集群混合云来降低成本,利用到不同集群各自的 ... 
- C#进阶系列——DDD领域驱动设计初探(一):聚合
		前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的 ... 
- C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)
		前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ... 
- C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下)
		前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势.本章还是继续来完善下仓储的设计.上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑.那么涉及到具体的实 ... 
- C#进阶系列——DDD领域驱动设计初探(四):WCF搭建
		前言:前面三篇分享了下DDD里面的两个主要特性:聚合和仓储.领域层的搭建基本完成,当然还涉及到领域事件和领域服务的部分,后面再项目搭建的过程中慢慢引入,博主的思路是先将整个架构走通,然后一步一步来添加 ... 
- C#进阶系列——DDD领域驱动设计初探(六):领域服务
		前言:之前一直在搭建项目架构的代码,有点偏离我们的主题(DDD)了,这篇我们继续来聊聊DDD里面另一个比较重要的知识点:领域服务.关于领域服务的使用,书中也介绍得比较晦涩,在此就根据博主自己的理解谈谈 ... 
- C#进阶系列——DDD领域驱动设计初探(七):Web层的搭建
		前言:好久没更新博客了,每天被该死的业务缠身,今天正好一个模块完成了,继续来完善我们的代码.之前的六篇完成了领域层.应用层.以及基础结构层的部分代码,这篇打算搭建下UI层的代码. DDD领域驱动设计初 ... 
- DDD领域驱动设计初探
		DDD领域驱动设计初探1 前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下D ... 
- Linux Platform驱动模型(二) _驱动方法
		在Linux设备树语法详解和Linux Platform驱动模型(一) _设备信息中我们讨论了设备信息的写法,本文主要讨论平台总线中另外一部分-驱动方法,将试图回答下面几个问题: 如何填充platfo ... 
随机推荐
- SQL SERVER修改为sa登陆权限报错,233,18456接连出现【抓狂ing】
			[记录生活] 今天做作业需要修改sa权限,本人电脑没错误. 同样教程发给朋友,错误百出.... 话不多说,百度很多解决方法,但是都没有解决,贴出解决方法. 0.用Windows身份验证登录,执行SQL ... 
- 接单,开发,学习神器--基于SpringSecurity的后台权限管理系统
			基于SpringSecurity--码仔后台管理系统 1.技术选项 >- 核心框架 SpringBoot >- 权限框架 SpringSecurity >- 模板引擎 Thymele ... 
- nginx: [error] invalid PID number "" in ...
			1.查看进程 ps -ef|grep nginx 2.进入nginx安装目录sbin下,执行命令: ./nginx -t 如下显示: syntax is ok test is successful 3 ... 
- PIC单片机的i2c的程序
			#include<pic.h>#define uchar unsigned char#define uint unsigned int#define add 0xaa__CONFIG(0x ... 
- 基于 kubeadm 搭建高可用的kubernetes 1.18.2 (k8s)集群 部署 dashboard 2.x
			1. 部署dashboard 2.x版本 Dashboard 分为 1.x版本 和 2.x版本, k8s 使用的是1.18.2 故部署2.x版本的 # dashboard 2.x版本的部署 # 上传d ... 
- lunix如何查看防火墙是否关闭和关闭开启防火墙命令
			查看防火墙是否关闭的命令如下: 1.通过 /etc/init.d/iptables status 或者 service iptables status命令 2.通过 iptables -L命令 查看 ... 
- SpringBoot获取配置文件,就这么简单。
			在讲SpringBoot 获取配置文件之前我们需要对SpringBoot 的项目有一个整体的了解,如何创建SpringBoot 项目,项目结构等等知识点,我在这里就不一一讲述了,没有学过的小伙伴可以自 ... 
- 01 . Memcached简介及部署
			Memcached简介 memcached是一个自由开源,高性能,分布式内存对象存储系统 基于内存的key-valued存储,用来存储小块的任意数据(字符串,对象) 他是一个简洁的key-value存 ... 
- zabbix通过IPMI模式监控服务器风扇转速和温度反映机房室温变化实例
			说明:2019年4月7日321机房OA服务器主板监控风扇转速和温度有明显升高,其后3天呈逐日升高趋势.检查机房感觉空调制冷量不足.4月11日联系空调维修进行处理,空调制冷恢复正常,风扇转速和温度监 ... 
- ERROR: ...hbase.PleaseHoldException: Master is initializing
			同学升级HBase后遇到这个问题,hbase shell,status就可以看到 ERROR: -hbase.PleaseHoldException: Master is initializing 解 ... 
