• platform驱动架构初探


    platform总线是Linux2.6引入的虚拟总线,这类总线没有对应的硬件结构。与之相反,USB总线和PCI总线在内核中是有对应的bus(USB-bus和PCI-bus)的。为了统一管理CPU这些既不属于USB又不属于PCI总线的外设资源,采用了platform虚拟总线。和字符设备不同,在platform架构中,整个驱动分为了device和driver两部分,提高了系统的可移植性。
    在学习platform架构时,我们可以借助一点面向对象的思想,注意关注一些重要的结构体,将属性和行为分开学习,再联系起来。
    本文所有代码基于linux3.9.5

    platform总线驱动架构概览

    可以分为如下三层:

    1. 设备struct platform_device : 资源分配
    2. 驱动struct platform_driver :初始化
    3. 总线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: 唤醒设备
  • 相关阅读:
    微服务概述
    Airflow 配置celery+rabbitmq和celery+redis
    CentOS7安装Airflow
    Python如何import其它.py文件及其函数
    ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
    CentOS7安装MySQL
    Hadoop完全分布式环境下,DataNode进程正常启动,但是网页上不显示DataNode节点
    <一> windbg简介
    几个资料下载网站
    使用VS2012 C++ 进行单元测试
  • 原文地址:https://www.cnblogs.com/pusidun/p/9053998.html
Copyright © 2020-2023  润新知