platform平台总线——讯为笔记

概念

平台(platform)总线是一种虚拟的总线。

Linux内核要求每出现一个device就要向总线汇报(注册),出现一个driver,也要向总线汇报(注册)。

注册device/driver时,总线会寻找与之匹配的driver/device。如果对应的name相同则发生匹配。

匹配发生时,会调用platform_driver中的probe函数。所以驱动的关键的处理、初始化等都要从probe函数开始。

以杂项设备使用平台总线为例:将之前的一个驱动文件分成两个:device.c和driver.c
把稳定不变的放在driver.c里(类似文件操作集里的一些函数),把需要经常或根据平台不同改变的放在device.c里

平台设备 device

主要使用的两个函数

int platform_device_register(struct platform_device *);  //注册平台设备
void platform_device_unregister(struct platform_device *);  //注销平台设备

使用的结构体

//include/linux/platform_device.h
struct platform_device {
	const char	*name;  //在/sys/bus/platform/
	int		id;     //多个同name的设备使用,只有一个时写-1
	bool		id_auto;
	struct device	dev;   //设备结构体
	u32		num_resources;   //资源数量
	struct resource	*resource;  //硬件资源

	const struct platform_device_id	*id_entry;
	char *driver_override; /* Driver name to force a match */

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};



//include/linux/ioport.h //硬件资源resource结构体
struct resource {
	resource_size_t start;  //资源起始地址
	resource_size_t end;  //资源的结束地址
	const char *name;    //资源名字
	unsigned long flags;  //资源的类型
	unsigned long desc;
	struct resource *parent, *sibling, *child;
};

//资源类型的宏定义也在ioport.h里,就在resource结构体下方

注册device之后,在/sys/bus/platform/devices目录下,会有以platform_device里name成员命名的文件夹

平台驱动 driver

注册和注销函数

#define platform_driver_register(drv) __platform_driver_register(drv, THIS_MODULE) 
extern int __platform_driver_register(struct platform_driver *,
					struct module *);
extern void platform_driver_unregister(struct platform_driver *);

platform_driver结构体

struct platform_driver {
	int (*probe)(struct platform_device *);  //device和driver匹配时执行
	int (*remove)(struct platform_device *);  //当device和driver任意一个remove时执行
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*resume)(struct platform_device *);
	struct device_driver driver;   //driver结构体
	const struct platform_device_id *id_table; //优先匹配id_table里的名字,如果id_table为空,才会再匹配driver里的name	
	bool prevent_deferred_probe;
};

device_driver结构体

//  include/linux/device.h
struct device_driver {
	const char		*name;  //用于和device匹配的名字
	struct bus_type		*bus;
	struct module		*owner;
	const struct of_device_id	*of_match_table;  //也可以用来匹配,一般和设备树做匹配时使用
    /*---省略了其他成员---*/
};

probe函数注意事项

思路:

  • 从device里获取硬件资源
  • 完善file_operations,注册杂项/字符设备,生成设备节点

从device里获取硬件资源

直接获得:probe函数的形参platform_device结构体,从中直接获得device里定义的参数
不安全

推荐使用:

使用函数
struct resource *platform_get_resource(struct platform_device *,
					      unsigned int, unsigned int);
//参数:device结构体,资源类型,同类型资源在device结构体的第几位

如果获取的资源有地址之类需要使用的参数,则注册设备之前先向内核登记资源

//include/linux/ioport.h
requset_mem_region(start,n)  //登记,确保硬件资源没有被占用
//起始地址,长度

release_mem_region(start,n)  //释放,登记失败也要释放

完善file_operations,注册杂项/字符设备,生成设备节点

注册设备,生成节点(和字符或杂项设备一样的操作)

总结

如果使用平台总线设计驱动,要将代码分成两部分:device和driver。

device代码放一些跟随平台改变(比如芯片不同)的参数,driver里放稳定不变的代码。

以控制gpio为例,将寄存器等信息放在device里,文件操作集、注册设备等放在driver里。

platform总线会对比device里platform_device结构体的name成员 和 platform_driver.driver.name或platform_driver.id_table的值 ,如果相同,平台总线会将device和driver匹配。

device和driver模块不关注谁先加载进内核。

如果发生匹配,会执行platform_driver.probe函数。

一般驱动设计中,会在probe函数中进行:获取device中定义的硬件资源、申请设备号、注册设备、创建设备节点等操作。

代码

device.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>


static struct resource pResource[] = {
    [0] = {
        .name = "ABCD",
        .flags = IORESOURCE_MEM
    }
};

static struct platform_device pdevice = {
    .name = "platform_test",  //用于匹配的名字
    .id = -1,
    .resource = pResource,  //资源,一般用于提供给driver
    .num_resources = ARRAY_SIZE(pResource)  //资源大小
};


static int device_init_led(void){
    int ret=0;
    ret = platform_device_register(&pdevice);  //注册平台设备
    if(ret < 0){
        printk("platform device regist failed\n");
        return -1;
    }
    return 0;
}

static void device_exit_led(void){
    platform_device_unregister(&pdevice);
    printk("platform device exit!\n");
}

module_init(device_init_led);
module_exit(device_exit_led);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TAXUE");

driver.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>

int misc_open(struct inode *a,struct file *b){
    return 0;
}
int misc_release (struct inode * a, struct file * b){
    return 0;
}
ssize_t misc_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t){
    return 0;
}
ssize_t misc_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t){
    return 0;
}

static struct file_operations misc_fops = {
    .owner = THIS_MODULE,
    .open  = misc_open,
    .release = misc_release,
    .read = misc_read,
    .write = misc_write
};

static struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,    //自动分配从设备号
    .name  = "hello_led",          //设备节点名
    .fops  = &misc_fops             //文件操作集
};

int drProbe(struct platform_device *dev){
    struct resource *res;
    int ret;
    printk("probe here\n");
    res = platform_get_resource( dev, IORESOURCE_MEM, 0);
    printk("device resource name is %s\n",res->name);

    ret = misc_register(&misc_dev);   //注册杂项设备
    if(ret < 0){
        printk("misc regist failed\n");
        return -1;
    }

    printk("misc regist succeed\n");
    return 0;
}
int drRemove(struct platform_device *dev){
    misc_deregister(&misc_dev);
    printk("driver remove\n");
    return 0;
}

static struct platform_driver pdrv = {
    .probe = drProbe,   //匹配成功时执行
    .remove = drRemove,  //device或driver任一个remove时执行
    .driver = {
        .name = "platform_test",  //用于匹配的名字
        .owner = THIS_MODULE
    }
};

static int driver_init_led(void){
    int ret=0;
    ret = platform_driver_register(&pdrv);  //注册driver
    if(ret < 0){
        printk("platform driver regist failed\n");
        return -1;
    }
    return 0;
}

static void driver_exit_led(void){
   platform_driver_unregister(&pdrv);  //注销driver
   printk("platform driver exit!\n");
}

module_init(driver_init_led);
module_exit(driver_exit_led); 

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TAXUE");

单独加载进kernel后,在/sys/bus/platform/devices或/sys/bus/platform/drivers目录下会有对应的文件夹。

两个模块都加载之后,会看到执行了probe函数创建了一个杂项设备。

平台总线工作的框架图

image

posted @ 2021-09-10 00:11  WuYunTaXue  阅读(386)  评论(0编辑  收藏  举报